草庐IT

javascript - 在 PyCrypto AES MODE_CTR 中包含 nonce 和 block 计数

coder 2025-03-28 原文

一些背景信息,你可以跳过这部分的实际问题

这是我在stackoverflow上关于这个主题的第三个问题。为了完整起见,这些是其他问题 AES with crypt-js and PyCryptoMatch AES de/encryption in python and javascript .不幸的是,我最后一次尝试对原始问题投了两次反对票。问题是,即使我不知道我真正的问题是什么。我只是四处寻找我正在寻找的真正问题。有了评论中的反馈,并阅读了一些额外的信息,我更新了我的问题。我认为我挖掘了正确的问题。但是我的问题在我更新后没有得到更多的意见。所以我真的希望这个问题现在更加清晰易懂 - 即使我知道我现在的问题是什么:D
感谢大家为这个很酷的社区制作 stackoverflow - 我经常在这里找到解决我的问题的方法。请继续对不好的问题提供反馈,以便改进和更新它们,从而增加这个庞大的知识和解决方案数据库。
并随时纠正我的英语语法和拼写。

问题

Javascript 中的 AES

我有一个加密的字符串,我可以用这个 this Javascript Implementation of AES 256 CTR Mode 解密它

password = "myPassphrase"
ciphertext = "bQJdJ1F2Y0+uILADqEv+/SCDV1jAb7jwUBWk"
origtext = Aes.Ctr.decrypt(ciphertext, password, 256);
alert(origtext)

这将使用 This is a test Text 解密我的字符串和警报框弹出。

AES 与 PyCrypto

现在我想用 python and PyCrypto 解密这个字符串

password = 'myPassphrase'
ciphertext = "bQJdJ1F2Y0+uILADqEv+/SCDV1jAb7jwUBWk"
ctr = Counter.new(nbits=128)
encryptor = AES.new(key, AES.MODE_CTR, counter=ctr)
origtext = encryptor.decrypt(base64.b64decode(ciphertext))
print origtext

此代码不运行。我得到一个 ValueError: AES key must be either 16, 24, or 32 bytes long .当我认识到我必须在 PyCrypto 中做更多事情然后调用一个解密方法时,我开始调查并试图弄清楚我必须做什么。

调查

我首先想到的基本事情是:
  • AES 256 位 (?)。但是 AES 标准是 128 位的。将密码增加到 32 字节就足够了吗?
  • 计数器模式。使用 AES.MODE_CTR 在 PyCrypto 中轻松设置。但我必须指定一个 counter() 方法。所以我使用了basic binary Counter provided by PyCrypto .这与 Javascript 实现兼容吗?我无法弄清楚他们在做什么。
  • 该字符串是 base64 编码的。不是什么大问题。
  • 填充一般。密码和加密字符串。

  • 对于密码,他们这样做:

    for (var i=0; i<nBytes; i++) {
        pwBytes[i] = isNaN(password.charCodeAt(i)) ? 0 : password.charCodeAt(i);
    }
    

    然后我在python中做了这个

    l = 32
    key = key + (chr(0)*(l-len(key)%l))
    

    但这并没有帮助。我仍然得到一个奇怪的字符串 ? A???B??d9= ,?h????'使用以下代码

    l = 32
    key = 'myPassphrase'
    key = key + (chr(0)*(l-len(key)%l))
    ciphertext = "bQJdJ1F2Y0+uILADqEv+/SCDV1jAb7jwUBWk"
    ctr = Counter.new(nbits=128)
    encryptor = AES.new(key, AES.MODE_CTR, counter=ctr)
    origtext = encryptor.decrypt(base64.b64decode(ciphertext))
    print origtext
    

    然后我阅读了有关 Javascript 实现的更多信息,它说

    [...] In this implementation, the initial block holds the nonce in the first 8 bytes, and the block count in the second 8 bytes. [...]



    我认为这可能是解决问题的关键。所以我测试了当我在 Javascript 中加密一个空字符串时会发生什么:

    origtext = ""
    var ciphertext =Aes.Ctr.encrypt(origtext, password, 256);
    alert(ciphertext)
    

    警告框显示 /gEKb+N3Y08= (12 个字符)。但是为什么是12?不应该是 8+8 = 16Bytes 吗?好吧,无论如何,我通过使用 for i in xrange(0,20): 测试解密来尝试对 python 解密的蛮力方法。和 ciphertext[i:]base64.b64decode(ciphertext)[i:] .我知道这是一次非常尴尬的尝试,但我越来越绝望。它也没有工作。

    future 的前景也是以同样的方式实现加密。

    附加信息

    加密的字符串最初不是用 this Javascript implementation 加密的。 , 它来自另一个来源。我刚刚认识到,Javascript 代码做了正确的事情。所以我肯定这种实现有点像“标准”。

    问题

    我能做什么,使用 PyCrypto 对字符串的加密和解密与 Javascript 实现中的相同,以便我可以在 Javascript 和 Python 之间交换数据?
    如果您可以建议另一个,我也会切换到 python 中的另一个加密库。
    此外,我对任何类型的提示和反馈都很满意。

    我认为,一切都归结为如何将随机数和 block 计数包含到加密字符串中? 如何提取此信息进行解密?

    最佳答案

    我们仍然在这里处理一堆问题。

    如何提取随机数和计数器进行解密?

    这很简单。在 Javascript 实现中(不遵循特定的
    在这方面的标准)8 字节随机数被添加到加密结果之前。
    在 Python 中,您可以使用以下命令提取它:

    import base64
    from_js_bin = base64.decode(from_js)
    nonce = from_js_bin[:8]
    ciphertext = from_js_bin[8:]
    

    在哪里 from_js是您收到的二进制字符串。

    无法提取计数器,因为 JS 实现不传输它。
    但是,初始值是(通常情况下)0。

    如何使用随机数和计数器解密 Python 中的字符串?

    首先,必须确定如何组合 nonce 和 counter 以获得 counter block 。
    似乎 JS 实现遵循 NIST 800-38A standard , 其中
    左半边是随机数,右半边是计数器。更准确地说,计数器
    采用大端格式(LSB 是最右边的字节)。这也是维基百科显示的:
    .

    不幸的是,CTR 模式在 PyCrypto (a well-known problem) 中的记录很差。
    基本上,counter参数必须是返回的可调用对象
    对于每个后续调用,正确的 16 字节(用于 AES)计数器 block 。Crypto.Util.Counter这样做,但以一种模糊的方式。

    它只是出于性能目的。您可以像这样轻松地自己实现它:
    from Crypto.Cipher import AES
    import struct
    
    class MyCounter:
    
      def __init__(self, nonce):
        """Initialize the counter object.
    
        @nonce      An 8 byte binary string.
        """
        assert(len(nonce)==8)
        self.nonce = nonce
        self.cnt = 0
    
      def __call__(self):
        """Return the next 16 byte counter, as binary string."""
        righthalf = struct.pack('>Q',self.cnt)
        self.cnt += 1
        return self.nonce + righthalf
    
    cipher_ctr = AES.new(key, mode=AES.MODE_CTR, counter=MyCounter(nonce))
    plaintext = cipher_ctr.decrypt(ciphertext)
    

    AES 的 key 有多长?

    AES-128 的 key 长度为 16 个字节。
    AES-192 的 key 长度为 24 字节。
    AES-256 的 key 长度为 32 字节。
    每种算法都不同,但大部分实现是共享的。
    在所有情况下,该算法都对 16 字节的数据 block 进行操作。
    为简单起见,请坚持使用 AES-128 (nBits=128)。

    你的代码会工作吗?

    我感觉不会,因为您计算 AES key 的方式似乎不正确。
    JS 代码将密码编码为 UTF-8 并使用自身加密。
    结果是实际的 key Material 。它是 16 字节长,因此对于 AES-192 和 -256,实现会在后面复制它的一部分。此外,明文在加密之前也是 UTF-8 编码的。

    一般来说,我建议你遵循这种方法:
  • 使您的 JS 实现可重现(现在加密取决于当前时间,它经常变化;-))。
  • 在每一步打印键和数据的值(或使用调试器)。
  • 尝试在 Python 中重现相同的算法,并打印这些值。
  • 调查他们开始不同的地方。

  • 在 Python 中复制加密算法后,解密应该很容易。

    关于javascript - 在 PyCrypto AES MODE_CTR 中包含 nonce 和 block 计数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9742113/

    有关javascript - 在 PyCrypto AES MODE_CTR 中包含 nonce 和 block 计数的更多相关文章

    1. ruby - RSpec - 使用测试替身作为 block 参数 - 2

      我有一些Ruby代码,如下所示:Something.createdo|x|x.foo=barend我想编写一个测试,它使用double代替block参数x,这样我就可以调用:x_double.should_receive(:foo).with("whatever").这可能吗? 最佳答案 specify'something'dox=doublex.should_receive(:foo=).with("whatever")Something.should_receive(:create).and_yield(x)#callthere

    2. ruby-on-rails - Enumerator.new 如何处理已通过的 block ? - 2

      我在理解Enumerator.new方法的工作原理时遇到了一些困难。假设文档中的示例:fib=Enumerator.newdo|y|a=b=1loopdoy[1,1,2,3,5,8,13,21,34,55]循环中断条件在哪里,它如何知道循环应该迭代多少次(因为它没有任何明确的中断条件并且看起来像无限循环)? 最佳答案 Enumerator使用Fibers在内部。您的示例等效于:require'fiber'fiber=Fiber.newdoa=b=1loopdoFiber.yieldaa,b=b,a+bendend10.times.m

    3. ruby-on-rails - Ruby on Rails 计数器缓存错误 - 2

      尝试在我的RoR应用程序中实现计数器缓存列时出现错误Unknownkey(s):counter_cache。我在这个问题中实现了模型关联:Modelassociationquestion这是我的迁移:classAddVideoVotesCountToVideos0Video.reset_column_informationVideo.find(:all).eachdo|p|p.update_attributes:videos_votes_count,p.video_votes.lengthendenddefself.downremove_column:videos,:video_vot

    4. ruby - 使用多个数组创建计数 - 2

      我正在尝试按0-9和a-z的顺序创建数字和字母列表。我有一组值value_array=['0','1','2','3','4','5','6','7','8','9','a','b','光盘','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','','u','v','w','x','y','z']和一个组合列表的数组,按顺序,这些数字可以产生x个字符,比方说三个list_array=[]和一个当前字母和数字组合的数组(在将它插入列表数组之前我会把它变成一个字符串,]current_combo['0','0','0']

    5. ruby - 在匿名 block 中产生 - 2

      我没有理解以下行为(另请参阅inthisSOthread):defdef_testputs'def_test.in'yieldifblock_given?puts'def_test.out'enddef_testdoputs'def_testok'endblock_test=procdo|&block|puts'block_test.in'block.callifblockputs'block_test.out'endblock_test.calldoputs'block_test'endproc_test=procdoputs'proc_test.in'yieldifblock_gi

    6. ruby - Ruby 中的单 block AES 解密 - 2

      我需要尝试一些AES片段。我有一些密文c和一个keyk。密文已使用AES-CBC加密,并在前面加上IV。不存在填充,纯文本的长度是16的倍数。所以我这样做:aes=OpenSSL::Cipher::Cipher.new("AES-128-CCB")aes.decryptaes.key=kaes.iv=c[0..15]aes.update(c[16..63])+aes.final它工作得很好。现在我需要手动执行CBC模式,所以我需要单个block的“普通”AES解密。我正在尝试这个:aes=OpenSSL::Cipher::Cipher.new("AES-128-ECB")aes.dec

    7. ruby-on-rails - 在 heroku 的 .fonts 文件夹中包含自定义字体,似乎无法识别它们 - 2

      Heroku支持人员告诉我,为了在我的Web应用程序中使用自定义字体(未安装在系统中,您可以在bash控制台中使用fc-list查看已安装的字体)我必须部署一个包含所有字体的.fonts文件夹里面的字体。问题是我不知道该怎么做。我的意思是,我不知道文件名是否必须遵循heroku的任何特殊模式,或者我必须在我的代码中做一些事情来考虑这种字体,或者如果我将它包含在文件夹中它是自动的......事实是,我尝试以不同的方式更改字体的文件名,但根本没有使用该字体。为了提供更多详细信息,我们使用字体的过程是将PDF转换为图像,更具体地说,使用rghostgem。并且最终图像根本不使用自定义字体。在

    8. ruby-on-rails - 无法在 Rails 助手中捕获 block 的输出 - 2

      我在使用自定义RailsFormBuilder时遇到了问题,从昨天晚上开始我就发疯了。基本上我想对我的构建器方法之一有一个可选block,以便我可以在我的主要content_tag中显示其他内容。:defform_field(method,&block)content_tag(:div,class:'field')doconcatlabel(method,"Label#{method}")concattext_field(method)capture(&block)ifblock_given?endend当我在我的一个Slim模板中调用该方法时,如下所示:=f.form_field:e

    9. ruby - 具有两个参数的 block - 2

      我从用户Hirolau那里找到了这段代码:defsum_to_n?(a,n)a.combination(2).find{|x,y|x+y==n}enda=[1,2,3,4,5]sum_to_n?(a,9)#=>[4,5]sum_to_n?(a,11)#=>nil我如何知道何时可以将两个参数发送到预定义方法(如find)?我不清楚,因为有时它不起作用。这是重新定义的东西吗? 最佳答案 如果您查看Enumerable#find的文档,您会发现它只接受一个block参数。您可以将它发送两次的原因是因为Ruby可以方便地让您根据它的“并行赋

    10. ruby - 在参数为 `yield self` 的方法中使用 `&block` 和在没有参数 `yield self` 的方法中使用 `&block` 有什么区别吗? - 2

      我明白了defa(&block)block.call(self)end和defa()yieldselfend导致相同的结果,如果我假设有这样一个blocka{}。我的问题是-因为我偶然发现了一些这样的代码,它是否有任何区别或者是否有任何优势(如果我不使用变量/引用block):defa(&block)yieldselfend这是一个我不理解&block用法的具体案例:defrule(code,name,&block)@rules=[]if@rules.nil?@rules 最佳答案 我能想到的唯一优点就是自省(introspecti

    随机推荐