我试图建立使用PyCrypto接受两个参数有两个功能:在消息和密钥,然后加密/解密的消息。
我在网上找到了几个环节,以帮助我,但他们每个人都有缺点:
这一次在codekoala使用os.urandom,这是由PyCrypto气馁。
此外,我给功能键不能保证有预期的精确长度。 我能做些什么来实现这一目标?
此外,有几种模式,推荐哪一个? 我不知道该用什么:/
最后,究竟是什么IV? 我可以用于加密和解密提供不同的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))
Answer 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 = 32
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))
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:])]
Answer 2:
您可能需要以下两个函数到焊盘(做加密时)和unpad(做解密时)当输入的长度不是BLOCK_SIZE的倍数。
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:] ))
Answer 3:
让我来解决你的问题“的模式。” 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建议慎用 。 原因是,它需要你指定填充方案 ,给出的其他解决方案的例证。 一般来说,如果你不是很小心的填充, 有攻击完全破解加密!
现在,要注意的关键必须是随机的,32字节的字符串,是很重要的; 密码是不够的。 通常情况下,关键是像这样产生的:
# Nominal way to generate a fresh key. This calls the system's random number
# generator (RNG).
key1 = Random.new().read(key_bytes)
key可以从一个密码 ,也可以得出 :
# 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推导的关键,而这通常被认为是不好的密码的做法 。 看看维基百科的更多的操作模式。
Answer 4:
您可以通过使用密码散列函数( 而不是 Python的内建得到一个密码出任意密码的hash
),如SHA-1或SHA-256。 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]
它会保留其安全性达到您所指定的长度。
Answer 5:
对于有人想用urlsafe_b64encode和urlsafe_b64decode谁,这里的版本that're工作对我来说(花一些时间与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:]))
Answer 6:
对于其他人的利益,这是我的解密实现,我结合@Cyril和@Marcus的答案得。 这是假设,这通过与encryptedText报价,base64编码的HTTP请求来英寸
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()
Answer 7:
这是有点晚,但我认为这将是非常有益的。 没有人提过类似PKCS#7填充使用方案。 你可以用它来代替以前的功能,垫(做加密时)和unpad(做解密时)。我将在下面提供完整的源代码。
import base64
import hashlib
from Crypto import Random
from Crypto.Cipher import AES
import pkcs7
class Encryption:
def __init__(self):
pass
def Encrypt(self, PlainText, SecurePassword):
pw_encode = SecurePassword.encode('utf-8')
text_encode = PlainText.encode('utf-8')
key = hashlib.sha256(pw_encode).digest()
iv = Random.new().read(AES.block_size)
cipher = AES.new(key, AES.MODE_CBC, iv)
pad_text = pkcs7.encode(text_encode)
msg = iv + cipher.encrypt(pad_text)
EncodeMsg = base64.b64encode(msg)
return EncodeMsg
def Decrypt(self, Encrypted, SecurePassword):
decodbase64 = base64.b64decode(Encrypted.decode("utf-8"))
pw_encode = SecurePassword.decode('utf-8')
iv = decodbase64[:AES.block_size]
key = hashlib.sha256(pw_encode).digest()
cipher = AES.new(key, AES.MODE_CBC, iv)
msg = cipher.decrypt(decodbase64[AES.block_size:])
pad_text = pkcs7.decode(msg)
decryptedString = pad_text.decode('utf-8')
return decryptedString
import StringIO
import binascii
def decode(text, k=16):
nl = len(text)
val = int(binascii.hexlify(text[-1]), 16)
if val > k:
raise ValueError('Input is not padded or padding is corrupt')
l = nl - val
return text[:l]
def encode(text, k=16):
l = len(text)
output = StringIO.StringIO()
val = k - (l % k)
for _ in xrange(val):
output.write('%02x' % val)
return text + binascii.unhexlify(output.getvalue())
Answer 8:
另取这个(重从上面解决方案来),但
- 使用空值填充
- 不使用拉姆达(从来就不是一个风扇)
与Python 2.7和3.6.5测试
#!/usr/bin/python2.7 # you'll have to adjust for your setup, eg, #!/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')
Answer 9:
感谢这激发了,但对我没有工作,其他的答案。
花了几个小时试图弄清楚它是如何工作后,我想出了以下最新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'))
Answer 10:
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:])
文章来源: Encrypt & Decrypt using PyCrypto AES 256