我试图使用PyCrypto构建两个函数,它们接受两个参数:消息和密钥,然后加密/解密消息。
我在网上找到了几个链接来帮助我,但每一个都有缺陷:
codekoala的这个使用os。这是PyCrypto不鼓励的。
此外,我给函数的键不能保证具有预期的确切长度。我要怎么做才能做到呢?
还有,有几种模式,推荐哪种模式?我不知道用什么:/
最后,静脉注射到底是什么?我是否可以提供不同的IV来加密和解密,或者这将返回不同的结果?
我试图使用PyCrypto构建两个函数,它们接受两个参数:消息和密钥,然后加密/解密消息。
我在网上找到了几个链接来帮助我,但每一个都有缺陷:
codekoala的这个使用os。这是PyCrypto不鼓励的。
此外,我给函数的键不能保证具有预期的确切长度。我要怎么做才能做到呢?
还有,有几种模式,推荐哪种模式?我不知道用什么:/
最后,静脉注射到底是什么?我是否可以提供不同的IV来加密和解密,或者这将返回不同的结果?
当前回答
你可以通过使用像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]截断加密哈希值,它将保留其安全性,直到您指定的长度。
其他回答
你可以通过使用像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]截断加密哈希值,它将保留其安全性,直到您指定的长度。
您可能需要以下两个函数:当输入长度不是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:] ))
PyCrypto已经过时了。
如今,密码学有了更好的支持。
这是另一个实现。注意,这将返回字节,您需要使用base64将它们转换为用于传输的字符串。
import os
import hashlib
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
_BLOCK_SIZE = 16
class AesStringCipher:
def __init__(self, key):
self._key = hashlib.sha256(key.encode()).digest()
def encrypt_str(self, raw:str) -> bytes:
iv = os.urandom(_BLOCK_SIZE)
cipher = Cipher(algorithms.AES(self._key), modes.CBC(iv), default_backend())
encryptor = cipher.encryptor()
raw = _pad(raw)
return iv + encryptor.update(raw.encode('utf-8')) + encryptor.finalize()
def decrypt_str(self, enc:bytes) -> str:
iv = enc[:_BLOCK_SIZE]
enc = enc[_BLOCK_SIZE:]
cipher = Cipher(algorithms.AES(self._key), modes.CBC(iv), default_backend())
decryptor = cipher.decryptor()
raw = decryptor.update(enc) + decryptor.finalize()
raw = raw.decode('utf-8')
return _unpad(raw)
def _pad(s:str) -> str:
padding = (_BLOCK_SIZE - (len(s) % _BLOCK_SIZE))
return s + padding * chr(padding)
def _unpad(s:str) -> str:
return s[:-ord(s[len(s)-1:])]
if __name__ == '__main__':
cipher = AesStringCipher('my secret password')
secret_msg = 'this is a super secret msg ...'
enc_msg = cipher.encrypt_str(secret_msg)
dec_msg = cipher.decrypt_str(enc_msg)
assert secret_msg == dec_msg
你可以使用新的django-mirage-field包。
对于想要使用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:]))