使用AES + CTR的PyCrypto问题

xst*_*ter 10 python cryptography aes encryption-symmetric 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)
Run Code Online (Sandbox Code Playgroud)

这里,解密的文本与原始文本不同.

我对密码学的了解并不多,所以请耐心等待.我理解CTR模式需要一个"计数器"功能来每次提供一个随机计数器,但为什么当我的密钥是32字节时它需要它是16个字节并且它坚持我的消息也是16字节的倍数?这是正常的吗?

我猜它没有回到原始消息,因为计数器在加密和解密之间发生了变化.但那么,它究竟应该在理论上如何运作呢?我究竟做错了什么?无论如何,我被迫回到欧洲央行,直到我弄明白:(

Ale*_*lli 12

counter必须返回相同的解密,因为它没有加密,你直觉,所以,一个(不安全AT ALL)的方式来做到这一点是:

>>> secret = os.urandom(16)
>>> crypto = AES.new(os.urandom(32), AES.MODE_CTR, counter=lambda: secret)
>>> encrypted = crypto.encrypt("aaaaaaaaaaaaaaaa")
>>> print crypto.decrypt(encrypted)
aaaaaaaaaaaaaaaa
Run Code Online (Sandbox Code Playgroud)

CTR是一个分组密码,所以看起来让你感到惊讶的"16-at-a-time"约束非常自然.

当然,在每次通话中返回相同值的所谓"计数器" 是非常不安全的.做得不好,例如...:

import array

class Secret(object):
  def __init__(self, secret=None):
    if secret is None: secret = os.urandom(16)
    self.secret = secret
    self.reset()
  def counter(self):
    for i, c in enumerate(self.current):
      self.current[i] = c + 1
      if self.current: break
    return self.current.tostring()
  def reset(self):
    self.current = array.array('B', self.secret)

secret = Secret()
crypto = AES.new(os.urandom(32), AES.MODE_CTR, counter=secret.counter)
encrypted = crypto.encrypt(16*'a' + 16*'b' + 16*'c')
secret.reset()
print crypto.decrypt(encrypted)
Run Code Online (Sandbox Code Playgroud)

  • 实际上CTR可以加密任意数量的文本; 它将块密码转换为密钥流生成器.在这种情况下,没有实际的理由限制输入是块大小的倍数. (3认同)
  • 但如果我使用AES加密会话密码,我必须在某处保存计数器,以便下次运行时解密?这不会破坏随机计数器的安全性吗? (3认同)

Gil*_*il' 5

AES是一种分组密码:它是一种算法(更精确地说是一对算法),它采用密钥和消息块,并对该块进行加密或解密。无论密钥大小如何,块的大小始终为16个字节。

点击率是一种运作方式。这是一对基于块密码的算法,用于生成流密码,该流密码可以加密和解密任意长度的消息。

CTR通过将连续的消息块与计数器连续值的加密相结合来工作。计数器的大小必须为一个块,以便它是块密码的有效输入。

  • 从功能上讲,计数器的连续值是多少都没有关系,只要加密和解密端使用相同的序列即可。通常,将计数器视为256位数字,并为每个连续的块递增,并随机选择一个初始值。因此,通常将增量方法烘焙到代码中,但是解密方需要知道初始值是什么,因此加密方在加密消息的开头发送或存储初始计数器值。
  • 为了安全起见,对于给定的密钥永远不要重复相同的计数器值至关重要。因此,对于一次性密钥,可以从开始'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'。但是,如果密钥被多次使用,则第二条消息将不允许重用第一条消息使用的任何计数器值,而最简单的方法是确保随机生成初始计数器值(2 ^ 128)空间,发生碰撞的机会可以忽略不计)。

通过让调用者选择一个计数器功能,PyCrypto库为您提供了很多绳索来吊死自己。您不仅应该使用Crypto.Util.Counter,如文档所述那样“获得更好的性能”,还应该使用,因为构建安全的东西要比自己想出的东西容易。即使这样,也要小心使用随机的初始值,这不是默认值。

import binascii
import os
from Crypto.Cipher import AES
from Crypto.Util import Counter
def int_of_string(s):
    return int(binascii.hexlify(s), 16)
def encrypt_message(key, plaintext):
    iv = os.urandom(16)
    ctr = Counter.new(128, initial_value=int_of_string(iv))
    aes = AES.new(key, AES.MODE_CTR, counter=ctr)
    return iv + aes.encrypt(plaintext)
def decrypt_message(key, ciphertext):
    iv = ciphertext[:16]
    ctr = Counter.new(128, initial_value=int_of_string(iv))
    aes = AES.new(key, AES.MODE_CTR, counter=ctr)
    return aes.decrypt(ciphertext[16:])
Run Code Online (Sandbox Code Playgroud)