我试图使用PyCrypto构建两个函数,它们接受两个参数:消息和密钥,然后加密/解密消息。

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

codekoala的这个使用os。这是PyCrypto不鼓励的。

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

还有,有几种模式,推荐哪种模式?我不知道用什么:/

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


当前回答

下面是我的实现,它通过一些修复为我工作。它将密钥和秘密短语的对齐增强为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:])]

其他回答

下面是我的实现,它通过一些修复为我工作。它将密钥和秘密短语的对齐增强为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:])]

对于想要使用urlsafe_b64encode和urlsafe_b64decode的人,这里是为我工作的版本(在花了一些时间与unicode问题之后)

BS = 16
key = hashlib.md5(settings.SECRET_KEY).hexdigest()[:BS]
pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)
unpad = lambda s : s[:-ord(s[len(s)-1:])]

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.urlsafe_b64encode(iv + cipher.encrypt(raw)) 

    def decrypt(self, enc):
        enc = base64.urlsafe_b64decode(enc.encode('utf-8'))
        iv = enc[:BS]
        cipher = AES.new(self.key, AES.MODE_CBC, iv)
        return unpad(cipher.decrypt(enc[BS:]))

我用过Crypto和PyCryptodomex库,它非常快…

import base64
import hashlib
from Cryptodome.Cipher import AES as domeAES
from Cryptodome.Random import get_random_bytes
from Crypto import Random
from Crypto.Cipher import AES as cryptoAES

BLOCK_SIZE = AES.block_size

key = "my_secret_key".encode()
__key__ = hashlib.sha256(key).digest()
print(__key__)

def encrypt(raw):
    BS = cryptoAES.block_size
    pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)
    raw = base64.b64encode(pad(raw).encode('utf8'))
    iv = get_random_bytes(cryptoAES.block_size)
    cipher = cryptoAES.new(key= __key__, mode= cryptoAES.MODE_CFB,iv= iv)
    a= base64.b64encode(iv + cipher.encrypt(raw))
    IV = Random.new().read(BLOCK_SIZE)
    aes = domeAES.new(__key__, domeAES.MODE_CFB, IV)
    b = base64.b64encode(IV + aes.encrypt(a))
    return b

def decrypt(enc):
    passphrase = __key__
    encrypted = base64.b64decode(enc)
    IV = encrypted[:BLOCK_SIZE]
    aes = domeAES.new(passphrase, domeAES.MODE_CFB, IV)
    enc = aes.decrypt(encrypted[BLOCK_SIZE:])
    unpad = lambda s: s[:-ord(s[-1:])]
    enc = base64.b64decode(enc)
    iv = enc[:cryptoAES.block_size]
    cipher = cryptoAES.new(__key__, cryptoAES.MODE_CFB, iv)
    b=  unpad(base64.b64decode(cipher.decrypt(enc[cryptoAES.block_size:])).decode('utf8'))
    return b

encrypted_data =encrypt("Hi Steven!!!!!")
print(encrypted_data)
print("=======")
decrypted_data = decrypt(encrypted_data)
print(decrypted_data)

使用AES-256和utf8mb4加密和解密拉丁字符和特殊字符(中文):

对于那些需要加密和解密拉丁文和特殊值(如中文)的人,这里有一个@MIkee代码的修改来完成这项任务。

记住,UTF-8本身不能处理这种类型的编码。

import base64, re
from Crypto.Cipher import AES
from Crypto import Random
from django.conf import settings

import codecs

# Make utf8mb4 recognizable.
codecs.register(lambda name: codecs.lookup('utf8') if name == 'utf8mb4' else None)


class AESCipher:

    def __init__(self, key, blk_sz):
        self.key = key
        self.blk_sz = blk_sz

    def encrypt( self, raw ):
        # raw is the main value
        if raw is None or len(raw) == 0:
            raise NameError("No value given to encrypt")
        raw = raw + '\0' * (self.blk_sz - len(raw) % self.blk_sz)
        raw = raw.encode('utf8mb4')
        # Initialization vector to avoid same encrypt for same strings.
        iv = Random.new().read( AES.block_size )
        cipher = AES.new( self.key.encode('utf8mb4'), AES.MODE_CFB, iv )
        return base64.b64encode( iv + cipher.encrypt( raw ) ).decode('utf8mb4')

    def decrypt( self, enc ):
        # enc is the encrypted value
        if enc is None or len(enc) == 0:
            raise NameError("No value given to decrypt")
        enc = base64.b64decode(enc)
        iv = enc[:16]
        # AES.MODE_CFB that allows bigger length or Latin values
        cipher = AES.new(self.key.encode('utf8mb4'), AES.MODE_CFB, iv )
        return re.sub(b'\x00*$', b'', cipher.decrypt( enc[16:])).decode('utf8mb4')

用法:

>>> from django.conf import settings
>>> from aesencryption import AESCipher
>>>
>>> aes = AESCipher(settings.SECRET_KEY[:16], 32)
>>>
>>> value = aes.encrypt('漢字')
>>>
>>> value
'hnuRwBjwAHDp5X0DmMF3lWzbjR0r81WlW9MRrWukgQwTL0ZI88oQaWvMfBM+W87w9JtSTw=='
>>> dec_value = aes.decrypt(value)
>>> dec_value
'漢字'
>>>

拉丁字母也是如此,例如ã, á, à, â, ã, ç等。

注意点

请记住,如果要在数据库中存储拉丁值,则需要将其设置为允许这种类型的数据。因此,如果您的数据库设置为utf-8,它将不接受这种类型的数据。你也需要在那里换衣服。

你可以通过使用像SHA-1或SHA-256这样的加密哈希函数(不是Python的内置哈希)从任意密码中获得密码短语。Python在其标准库中包含了对这两者的支持:

import hashlib

hashlib.sha1("this is my awesome password").digest() # => a 20 byte string
hashlib.sha256("another awesome password").digest() # => a 32 byte string

您可以使用[:16]或[:24]截断加密哈希值,它将保留其安全性,直到您指定的长度。