我正在使用Pyramid 1.3(Python 2.7)进行一个项目,并将数据存储在MySQL中。 我有一个电子邮件地址表,我想加密它们以进行存储。 我正在尝试在应用程序中加密它们,然后解密它们以供查看。 我不打算完全安全,但主要目的是在数据库本身受到损害的情况下对数据进行足够的混淆。
我正在将 PyCrypto 与 AES 一起使用,并且一直在尝试关注这里的一些帖子和我找到的一些网络教程。 到目前为止,我找到的最接近的是这篇文章,它似乎有效,至少加密了它。 我遵循它并得到类似存储在数据库中的"7hBAQrWhJRnL9YdBGJfRErGFwGi3aC6noGzYTrGwAoQ="
的东西。 但是解密函数不断出错:
UnicodeDecodeError: 'ascii' codec can't decode byte 0xa1 in position 1: ordinal not in range(128)
我遇到了一些关于 Python 的 unicode 演示文稿,这帮助我更理解它,但我仍然不断收到同样的错误。
是否有关于如何编码、存储在数据库中、拉出数据库和解码源数据字符串的简单教程?
是否需要对数据库列进行特定的排序规则? 字段是否需要是某种类型? 到目前为止,我一直在使用默认排序规则并将其设置为 VARCHAR,假设我正在存储一个字符串。 听起来我在某个地方遇到了一些不兼容类型的编码问题或其他东西,但我的头在我需要改变的地方旋转。
有什么更好的指针或我可以提供的其他东西吗? 我可以显示我的代码,但它基本上是上面链接的副本......我只是想在修改太多之前获得概念验证。
编辑:一些示例源...在 MySQL 中,该表是ID (整数)client_id(国际)电子邮件地址 varchar(100) utf8mb4_general_ci (我一直在玩排序规则,我不知道它应该是什么!
蟒:
from base64 import b64encode, b64decode, urlsafe_b64decode, urlsafe_b64encode
BLOCK_SIZE = 32
INTERRUPT = u'u0001'
PAD = u'u0000'
def AddPadding(data, interrupt, pad, block_size):
new_data = ''.join([data, interrupt])
new_data_len = len(new_data)
remaining_len = block_size - new_data_len
to_pad_len = remaining_len % block_size
pad_string = pad * to_pad_len
return ''.join([new_data, pad_string])
def StripPadding(data, interrupt, pad):
return data.rstrip(pad).rstrip(interrupt)#data.rsplit(interrupt,1)[0]#rstrip(pad).rstrip(interrupt)
SECRET_KEY = u'a1b2c3d4e5f6g7h8a1b2c3d4e5f6g7h8'
IV = u'12345678abcdefgh'
cipher_for_encryption = AES.new(SECRET_KEY, AES.MODE_CBC, IV)
cipher_for_decryption = AES.new(SECRET_KEY, AES.MODE_CBC, IV)
def EncryptWithAES(encrypt_cipher, plaintext_data):
plaintext_padded = AddPadding(plaintext_data, INTERRUPT, PAD, BLOCK_SIZE)
encrypted = encrypt_cipher.encrypt(plaintext_padded)
return urlsafe_b64encode(encrypted)
def DecryptWithAES(decrypt_cipher, encrypted_data):
decoded_encrypted_data = urlsafe_b64decode(encrypted_data)
decrypted_data = decrypt_cipher.decrypt(decoded_encrypted_data)
return StripPadding(decrypted_data, INTERRUPT, PAD)
#encrypts it
posted_singleaddress = EncryptWithAES(cipher_for_encryption, posted_singleaddress)
#"me@mail.com" inserts "Ktpr49Uzn99HZXbmqEzGKlWo9wk-XBMXGZl_iyna-8c=" into the database
客户电子邮件是上表中的电子邮件列表。 取消注释时出现错误:
#if clientemails:
# decrypted = DecryptWithAES(cipher_for_decryption, clientemails[0].emailaddress)
我只是想解码第一个项目只是为了尝试让它工作,但这就是现在似乎适合它的部分......
PyCrypto 的一般规则是加密密钥、IV、明文、填充和密文应始终定义为二进制字符串,而不是文本。你对它们使用 Unicode 的事实本身就是问题的根源。
另一个问题是你以十六进制编码的形式传递给AES.new
密钥和 IV,因此前者是 256 位,后者是 128 位。这似乎仍然有效,但我想您的意图是使用 AES128 - 它具有 128 位密钥。因此,您需要将其转换为二进制,例如通过unhexlify
:两个字符串b'34'将映射到单个字节'\x34'。IV需要两倍的时间。
因此,在您的代码中,最好具有:
from binascii import unhexlify
INTERRUPT = b'x01'
PAD = b'x00'
SECRET_KEY = unhexlify('a1b2c3d4e5f6g7h8a1b2c3d4e5f6g7h8')
IV = unhexlify('12345678abcdefgh'*2)
如果需要加密文本,请先对其进行编码(例如,编码为 UTF-8),然后将其传递给函数EncryptWithAES()
。另请参阅取自 PyCrypto API 的此示例:
from Crypto.Cipher import AES
from Crypto import Random
key = b'Sixteen byte key'
iv = Random.new().read(AES.block_size)
cipher = AES.new(key, AES.MODE_CFB, iv)
msg = iv + cipher.encrypt(b'Attack at dawn')
加密步骤(即密文)的结果再次是二进制字符串。为了将其直接存储在MySQL数据库中,您必须使用BINARY
或VARBINARY
类型列。