我试图使用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:])]

其他回答

这是另一种观点(主要源自上述解决方案),但是

uses null for padding does not use lambda (never been a fan) tested with python 2.7 and 3.6.5 #!/usr/bin/python2.7 # you'll have to adjust for your setup, e.g., #!/usr/bin/python3 import base64, re from Crypto.Cipher import AES from Crypto import Random from django.conf import settings class AESCipher: """ Usage: aes = AESCipher( settings.SECRET_KEY[:16], 32) encryp_msg = aes.encrypt( 'ppppppppppppppppppppppppppppppppppppppppppppppppppppppp' ) msg = aes.decrypt( encryp_msg ) print("'{}'".format(msg)) """ def __init__(self, key, blk_sz): self.key = key self.blk_sz = blk_sz def encrypt( self, raw ): 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('utf-8') iv = Random.new().read( AES.block_size ) cipher = AES.new( self.key.encode('utf-8'), AES.MODE_CBC, iv ) return base64.b64encode( iv + cipher.encrypt( raw ) ).decode('utf-8') def decrypt( self, enc ): if enc is None or len(enc) == 0: raise NameError("No value given to decrypt") enc = base64.b64decode(enc) iv = enc[:16] cipher = AES.new(self.key.encode('utf-8'), AES.MODE_CBC, iv ) return re.sub(b'\x00*$', b'', cipher.decrypt( enc[16:])).decode('utf-8')

您可能需要以下两个函数:当输入长度不是BLOCK_SIZE的倍数时,pad- to pad(加密时)和unpad- to unpad(解密时)。

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

你问的是键的长度?您可以使用该密钥的MD5哈希,而不是直接使用它。

而且,根据我使用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:] ))

你可以使用新的django-mirage-field包。

使用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,它将不接受这种类型的数据。你也需要在那里换衣服。

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

BLOCK_SIZE=16
def trans(key):
     return md5.new(key).digest()

def encrypt(message, passphrase):
    passphrase = trans(passphrase)
    IV = Random.new().read(BLOCK_SIZE)
    aes = AES.new(passphrase, AES.MODE_CFB, IV)
    return base64.b64encode(IV + aes.encrypt(message))

def decrypt(encrypted, passphrase):
    passphrase = trans(passphrase)
    encrypted = base64.b64decode(encrypted)
    IV = encrypted[:BLOCK_SIZE]
    aes = AES.new(passphrase, AES.MODE_CFB, IV)
    return aes.decrypt(encrypted[BLOCK_SIZE:])