使用 AES+CTR 的 PyCrypto 问题
我正在编写一段代码来使用对称加密来加密文本。但它没有返回正确的结果......
from Crypto.Cipher import AES
import os
crypto = AES.new(os.urandom(32), AES.MODE_CTR, counter = lambda : os.urandom(16))
encrypted = crypto.encrypt("aaaaaaaaaaaaaaaa")
print crypto.decrypt(encrypted)
在这里,解密的文本与原始文本不同。
我对密码学不太了解,所以请耐心等待。我知道 CTR 模式需要一个“计数器”函数来每次提供一个随机计数器,但是当我的密钥是 32 字节并且它坚持我的消息也是 16 字节的倍数时,为什么它需要它是 16 字节?这是正常的吗?
我猜测它不会返回原始消息,因为计数器在加密和解密之间发生了变化。但是,理论上它应该如何运作呢?我做错了什么?不管怎样,我不得不求助于欧洲央行,直到我弄清楚这个问题:(
I'm writing a piece of code to encrypt a text using symmetric encryption. But it's not coming back with the right result...
from Crypto.Cipher import AES
import os
crypto = AES.new(os.urandom(32), AES.MODE_CTR, counter = lambda : os.urandom(16))
encrypted = crypto.encrypt("aaaaaaaaaaaaaaaa")
print crypto.decrypt(encrypted)
Here, the decrypted text is different from the original.
I don't really understand much about cryptography so please bear with me. I understand the CTR mode requires a "counter" function to supply a random counter each time, but why does it need it to be 16 bytes when my key is 32 bytes and it insists that my message is in multiples of 16 bytes too? Is this normal?
I'm guessing that it doesn't get back to the original message because the counter changed between encrypt and decrypt. But then, how is it supposed to work theoretically anyway? What am I doing wrong? Anyway, I'm forced to resort back to ECB until I figure this out :(
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
正如您所直觉的那样,
计数器
在解密时返回的结果必须与在加密时返回的结果相同,因此,一种(完全不安全)方法是:CTR 是分组密码,因此“一次 16 个”约束似乎让您感到惊讶,这是一个非常自然的约束。
当然,所谓的“计数器”在每次调用时返回相同值非常不安全。不需要太多就能做得更好,例如......:
The
counter
must return the same on decryption as it did on encryption, as you intuit, so, one (NOT SECURE AT ALL) way to do it is:CTR is a block cipher, so the "16-at-a-time" constraint that seems to surprise you is a pretty natural one.
Of course, a so-called "counter" returning the same value at each call is grossly insecure. Doesn't take much to do better, e.g....:
AES 是一种分组密码:它是一种算法(更准确地说,是一对算法),需要密钥和消息块,并对块进行加密或解密。无论密钥大小如何,块的大小始终为 16 字节。
CTR 是一种操作模式。它是一对基于分组密码生成流密码的算法,可以加密和解密任意长度的消息。
CTR 的工作原理是将连续的消息块与计数器的连续值的加密相结合。计数器的大小必须是一个块,以便它是分组密码的有效输入。
'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' 开始
。但是,如果密钥被多次使用,则第二条消息不允许重用第一条消息使用的任何计数器值,确保这一点的最简单方法是随机生成初始计数器值(使用 2^128空间,碰撞的可能性可以忽略不计)。通过让调用者选择一个计数器函数,PyCrypto 库为您提供了足够的绳索来吊死自己。您应该使用
Crypto.Util.Counter
,不仅仅是像文档所说的“为了更好的性能”,而是因为构建安全的东西比您自己可能想出的东西更容易。即便如此,请注意使用随机初始值,这不是默认值。AES is a block cipher: it's an algorithm (more precisely, a pair of algorithms) that takes a key and a message block and either encrypts or decrypts the block. The size of a block is always 16 bytes, regardless of the key size.
CTR is a mode of operation. It's a pair of algorithms that builds on a block cipher to produce a stream cipher, which can encrypt and decrypt messages of arbitrary lengths.
CTR works by combining successive message blocks with the encryption of successive values of a counter. The size of the counter needs to be one block so that it's valid input for the block cipher.
'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
. But if the key is used multiple times then the second message is not allowed to reuse any of the counter values used by the first message, and the easiest way to ensure that is to generate the initial counter value at random (with a 2^128 space, the chances of a collision are acceptably negligible).By letting the caller pick a counter function, the PyCrypto library gives you plenty of rope to hang yourself. You should use
Crypto.Util.Counter
, not just “for better performance” as the documentation puts it, but because it's easier to build something secure than what you're likely to come up with on your own. And even so, take care to use a random initial value, which is not the default.我可能肯定迟到了,而且我可能忽略了前面的答案,但我没有找到明确的说明,说明应该如何(至少恕我直言)根据 PyCrypto 包来完成此操作。
Crypto.Util.Counter 包提供了可调用的有状态计数器,这非常有用,但至少对我来说很容易不正确地使用它们。
您必须创建一个计数器,例如
ctr = Counter.new('parameters here')
。每次计数器模式密码对象调用计数器来加密消息时,计数器都会递增。这是良好的密码学实践所必需的,否则有关相同块的信息可能会从密文中泄漏。现在您不能在同一个密码对象上调用解密函数,因为它会再次调用同一个计数器,同时该计数器已经增加,可能会增加几次。您需要做的是创建一个新的密码对象,其中具有使用相同参数初始化的不同计数器。通过这种方式,解密可以正常工作,从加密完成的同一点开始计数器。
下面的工作示例:
I may be definitely late and I may have overlooked the previous answers, but I didn't find a clear statement of how this should (at least IMHO) be done according with the PyCrypto packages.
The Crypto.Util.Counter package provides callable stateful counters, which are very useful, but it was easy at least for me to use them improperly.
You have to create a counter, with e.g.
ctr = Counter.new('parameters here')
. Every time your counter is called by your counter mode cipher object to encrypt the message, it is incremented. This is needed for good cryptography practices, otherwise information about equal blocks may leak from the ciphertext.Now you cannot call the decryption function on the same cipher object, because it would call again the same counter which in the meanwhile has been incremented, possibly several times. What you need to do is to create a new cipher object with a different counter initialized with the same parameters. In this way the decryption works properly, starting the counter from the same point as the encryption was done.
Working example below:
它的长度必须与密码块大小相同。 CTR 模式只是对计数器进行加密,并将明文与加密的计数器块进行异或。
注意:
标准免责声明:加密货币很难。如果您不明白自己在做什么,您就会出错。
使用 scrypt。 scrypt 包括使用 AES-CTR 和密码派生密钥的
加密
和解密
。It has to be the same length as the cipher's block size. CTR mode just encrypts the counter and XORs the plaintext with the encrypted counter block.
Notes:
Standard disclaimer: Crypto is hard. If you don't understand what you are doing, you will get it wrong.
Use scrypt. scrypt includes
encrypt
anddecrypt
which use AES-CTR with a password-derived key.初始化向量(“计数器”)需要在加密和解密之间保持相同,就像密钥一样。使用它可以对相同的文本进行一百万次编码,并每次得到不同的密文(防止一些已知的明文攻击和模式匹配/攻击)。解密时仍然需要使用与加密时相同的 IV。通常,当您开始解密流时,会将 IV 初始化为开始加密该流时的相同值。
有关初始化向量的信息,请参阅 http://en.wikipedia.org/wiki/Initialization_vector。
请注意,os.urandom(16) 不是“确定性”的,这是计数器函数的要求。我建议你使用增量函数,因为这就是 CTR 模式的设计原理。初始计数器值应该是随机的,但连续值应该可以从初始值完全预测(确定性)。甚至可能会为您处理初始值(我不知道详细信息)
关于密钥、IV 和输入大小,听起来您选择的密码的块大小为 16 字节。你所描述的一切都符合这一点,对我来说似乎很正常。
The initialization vector ("counter") needs to stay the same, just as the key does, between encryption and decryption. It is used so that you can encode the same text a million times, and get different ciphertext each time (preventing some known plaintext attacks and pattern matching / attacks). You still need to use the same IV when decrypting as when encrypting. Usually when you start decrypting a stream, you initialize the IV to the same value that you started with when you started encrypting that stream.
See http://en.wikipedia.org/wiki/Initialization_vector for info on initialization vectors.
Note that os.urandom(16) is not 'deterministic', which is a requirement for counter functions. I suggest you use the increment function, as that is how CTR mode is designed. The initial counter value should be random, but the successive values should be fully predictable from the initial value (deterministic). The initial value may even be taken care of for you (I don't know the details)
About the key, IV, and input sizes, it sounds like the cipher you chose has a block size of 16 bytes. Everything you describe fits that and seems normal to me.