使用PyCrypto AES 256加密和解密

2024-04-18 17:58:19 发布

您现在位置:Python中文网/ 问答频道 /正文

我尝试使用PyCrypto构建两个函数,接受两个参数:消息和密钥,然后对消息进行加密/解密。

我在网上找到了几个帮助我的链接,但每个链接都有缺陷:

This one at codekoala使用os.urandom,PyCrypto不鼓励这样做。

此外,我给函数的键不能保证有预期的确切长度。我该怎么做才能做到?

另外,有几种模式,推荐哪一种?我不知道该用什么:

最后,静脉注射到底是什么?我可以为加密和解密提供一个不同的IV吗,或者这将返回一个不同的结果?

我到目前为止所做的是:

from Crypto import Random
from Crypto.Cipher import AES
import base64

BLOCK_SIZE=32

def encrypt(message, passphrase):
    # passphrase MUST be 16, 24 or 32 bytes long, how can I do that ?
    IV = Random.new().read(BLOCK_SIZE)
    aes = AES.new(passphrase, AES.MODE_CFB, IV)
    return base64.b64encode(aes.encrypt(message))

def decrypt(encrypted, passphrase):
    IV = Random.new().read(BLOCK_SIZE)
    aes = AES.new(passphrase, AES.MODE_CFB, IV)
    return aes.decrypt(base64.b64decode(encrypted))

Tags: 函数fromimport消息newsize链接random
3条回答

下面是我的实现,它对我起到了一些修复作用,并增强了密钥和机密短语与32字节和iv到16字节的对齐:

import base64
import hashlib
from Crypto import Random
from Crypto.Cipher import AES

class AESCipher(object):

    def __init__(self, key): 
        self.bs = AES.block_size
        self.key = hashlib.sha256(key.encode()).digest()

    def encrypt(self, raw):
        raw = self._pad(raw)
        iv = Random.new().read(AES.block_size)
        cipher = AES.new(self.key, AES.MODE_CBC, iv)
        return base64.b64encode(iv + cipher.encrypt(raw.encode()))

    def decrypt(self, enc):
        enc = base64.b64decode(enc)
        iv = enc[:AES.block_size]
        cipher = AES.new(self.key, AES.MODE_CBC, iv)
        return self._unpad(cipher.decrypt(enc[AES.block_size:])).decode('utf-8')

    def _pad(self, s):
        return s + (self.bs - len(s) % self.bs) * chr(self.bs - len(s) % self.bs)

    @staticmethod
    def _unpad(s):
        return s[:-ord(s[len(s)-1:])]

让我来回答你关于“模式”的问题。AES256是一种分组密码。它接受一个32字节的键和一个16字节的字符串,称为块并输出一个块。我们在操作模式下使用AES来加密。上面的解决方案建议使用CBC,这是一个例子。另一种称为CTR,使用起来比较容易:

from Crypto.Cipher import AES
from Crypto.Util import Counter
from Crypto import Random

# AES supports multiple key sizes: 16 (AES128), 24 (AES192), or 32 (AES256).
key_bytes = 32

# Takes as input a 32-byte key and an arbitrary-length plaintext and returns a
# pair (iv, ciphtertext). "iv" stands for initialization vector.
def encrypt(key, plaintext):
    assert len(key) == key_bytes

    # Choose a random, 16-byte IV.
    iv = Random.new().read(AES.block_size)

    # Convert the IV to a Python integer.
    iv_int = int(binascii.hexlify(iv), 16) 

    # Create a new Counter object with IV = iv_int.
    ctr = Counter.new(AES.block_size * 8, initial_value=iv_int)

    # Create AES-CTR cipher.
    aes = AES.new(key, AES.MODE_CTR, counter=ctr)

    # Encrypt and return IV and ciphertext.
    ciphertext = aes.encrypt(plaintext)
    return (iv, ciphertext)

# Takes as input a 32-byte key, a 16-byte IV, and a ciphertext, and outputs the
# corresponding plaintext.
def decrypt(key, iv, ciphertext):
    assert len(key) == key_bytes

    # Initialize counter for decryption. iv should be the same as the output of
    # encrypt().
    iv_int = int(iv.encode('hex'), 16) 
    ctr = Counter.new(AES.block_size * 8, initial_value=iv_int)

    # Create AES-CTR cipher.
    aes = AES.new(key, AES.MODE_CTR, counter=ctr)

    # Decrypt and return the plaintext.
    plaintext = aes.decrypt(ciphertext)
    return plaintext

(iv, ciphertext) = encrypt(key, 'hella')
print decrypt(key, iv, ciphertext)

这通常被称为AES-CTR。我建议谨慎使用AES-CBC和PyCrypto。原因是它要求您指定填充方案,如给出的其他解决方案所示。一般来说,如果你对填充不太小心,就会有完全破坏加密的attacks

现在,需要注意的是,密钥必须是一个随机的32字节字符串;密码不够。通常,密钥的生成方式如下:

# Nominal way to generate a fresh key. This calls the system's random number
# generator (RNG).
key1 = Random.new().read(key_bytes)

密钥也可以从密码中导出:

# It's also possible to derive a key from a password, but it's important that
# the password have high entropy, meaning difficult to predict.
password = "This is a rather weak password."

# For added # security, we add a "salt", which increases the entropy.
#
# In this example, we use the same RNG to produce the salt that we used to
# produce key1.
salt_bytes = 8 
salt = Random.new().read(salt_bytes)

# Stands for "Password-based key derivation function 2"
key2 = PBKDF2(password, salt, key_bytes)

上面的一些解决方案建议使用SHA256来派生密钥,但这通常被认为是bad cryptographic practice。 查看wikipedia了解有关操作模式的更多信息。

当输入长度不是块大小的倍数时,您可能需要以下两个函数来填充(加密时)和取消填充(解密时)。

BS = 16
pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS) 
unpad = lambda s : s[:-ord(s[len(s)-1:])]

所以你问钥匙的长度?您可以使用密钥的md5sum,而不是直接使用它。

此外,根据我使用PyCrypto的一点经验,当输入相同时,IV用于混合加密的输出,因此选择IV作为随机字符串,并将其用作加密输出的一部分,然后使用它来解密消息。

下面是我的实现,希望对你有用:

import base64
from Crypto.Cipher import AES
from Crypto import Random

class AESCipher:
    def __init__( self, key ):
        self.key = key

    def encrypt( self, raw ):
        raw = pad(raw)
        iv = Random.new().read( AES.block_size )
        cipher = AES.new( self.key, AES.MODE_CBC, iv )
        return base64.b64encode( iv + cipher.encrypt( raw ) ) 

    def decrypt( self, enc ):
        enc = base64.b64decode(enc)
        iv = enc[:16]
        cipher = AES.new(self.key, AES.MODE_CBC, iv )
        return unpad(cipher.decrypt( enc[16:] ))

相关问题 更多 >