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

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

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

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

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

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


当前回答

对于想要使用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:]))

其他回答

您可能需要以下两个函数:当输入长度不是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:] ))
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:])

为了其他人的利益,这里是我的解密实现,我通过组合@Cyril和@Marcus的答案得到的。这假设它是通过HTTP请求传入的,加密文本加引号,base64编码。

import base64
import urllib2
from Crypto.Cipher import AES


def decrypt(quotedEncodedEncrypted):
    key = 'SecretKey'

    encodedEncrypted = urllib2.unquote(quotedEncodedEncrypted)

    cipher = AES.new(key)
    decrypted = cipher.decrypt(base64.b64decode(encodedEncrypted))[:16]

    for i in range(1, len(base64.b64decode(encodedEncrypted))/16):
        cipher = AES.new(key, AES.MODE_CBC, base64.b64decode(encodedEncrypted)[(i-1)*16:i*16])
        decrypted += cipher.decrypt(base64.b64decode(encodedEncrypted)[i*16:])[:16]

    return decrypted.strip()

你可以通过使用像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]截断加密哈希值,它将保留其安全性,直到您指定的长度。

我很感激其他启发我的答案,但它们对我不起作用。

在花了几个小时试图弄清楚它是如何工作的之后,我用最新的PyCryptodomex库提出了下面的实现(这是另一个故事,我如何设法在代理后面设置它,在Windows上,在virtualenv中…唷)

它正在处理您的实现。记得写下填充、编码和加密步骤(反之亦然)。你必须打包和拆包,记住顺序。

import base64
import hashlib
from Cryptodome.Cipher import AES
from Cryptodome.Random import get_random_bytes

__key__ = hashlib.sha256(b'16-character key').digest()

def encrypt(raw):
    BS = AES.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(AES.block_size)
    cipher = AES.new(key= __key__, mode= AES.MODE_CFB,iv= iv)
    return base64.b64encode(iv + cipher.encrypt(raw))

def decrypt(enc):
    unpad = lambda s: s[:-ord(s[-1:])]

    enc = base64.b64decode(enc)
    iv = enc[:AES.block_size]
    cipher = AES.new(__key__, AES.MODE_CFB, iv)
    return unpad(base64.b64decode(cipher.decrypt(enc[AES.block_size:])).decode('utf8'))