在Python中使用盐加密/解密数据
我想知道基本上怎么用生成的盐值密钥来加密数据,然后再用Python解密它?
我浏览了很多网站和模块,它们在加密方面看起来都不错,但似乎没有一个能解密。
我最关心的是要有一个强大的盐值密钥,这个密钥可能会生成几百次,然后用这个密钥来加密数据。特别是我想加密JSON格式的数据,使用盐值密钥,把加密后的数据发送到另一端(监听客户端),然后在那边根据生成盐值密钥的算法来解密数据。
我发现mcrypt模块在这方面效果最好,但关于python-mcrypt模块的文档不多(而且这个模块目前已经过时,不再维护)。
2 个回答
你只需要用到 RNCryptor 就可以了:
import rncryptor data = '...' password = '...' # rncryptor.RNCryptor's methods cryptor = rncryptor.RNCryptor() encrypted_data = cryptor.encrypt(data, password) decrypted_data = cryptor.decrypt(encrypted_data, password) assert data == decrypted_data # rncryptor's functions encrypted_data = rncryptor.encrypt(data, password) decrypted_data = rncryptor.decrypt(encrypted_data, password) assert data == decrypted_data
它提供了一种语义上安全的加密方式(每次加密都会使用随机的盐值和初始化向量),并且通过 HMAC 进行安全的完整性检查(加密后的数据如果被篡改会被发现)。
RNCryptor 还有一个特定的数据格式,这样你就不用担心这个问题了,并且在 很多编程语言中都有实现。
简单来说,你的问题的答案是:把密码和盐(salt)结合在一起,然后反复进行哈希处理,生成你的密钥。接着,把盐附加到密文上,这样你就可以用它来生成解密的密钥。为了确保我给出的答案是正确的,我写了一些函数来完成这些工作。下面是这些函数。
在我的回答中,我使用了pycrypto库,所以我们需要导入一些相关的库。
import Crypto.Random
from Crypto.Cipher import AES
import hashlib
为了让代码更易读,我定义了一些常量,后面会用到。
# salt size in bytes
SALT_SIZE = 16
# number of iterations in the key generation
NUMBER_OF_ITERATIONS = 20
# the size multiple required for AES
AES_MULTIPLE = 16
为了使用盐,我采用了一种基于密码的加密方案。我使用了RSA PKCS #5标准来生成基于密码的加密密钥和填充,这个标准是为了适配AES加密算法的。
生成密钥时,密码和盐会被连接在一起。这个组合会根据需要进行多次哈希处理。
def generate_key(password, salt, iterations):
assert iterations > 0
key = password + salt
for i in range(iterations):
key = hashlib.sha256(key).digest()
return key
为了填充文本,你需要计算出超出16的倍数的额外字节数。如果是0,就添加16个字节的填充;如果是1,就添加15个,以此类推。这样你总是会添加填充。填充的字符是与填充字节数相同值的字符(chr(padding_size)
),这样在最后去除填充时就很方便了(ord(padded_text[-1])
)。
def pad_text(text, multiple):
extra_bytes = len(text) % multiple
padding_size = multiple - extra_bytes
padding = chr(padding_size) * padding_size
padded_text = text + padding
return padded_text
def unpad_text(padded_text):
padding_size = ord(padded_text[-1])
text = padded_text[:-padding_size]
return text
加密时需要生成一个随机的盐,并将其与密码一起使用来生成加密密钥。文本会使用上面提到的pad_text
函数进行填充,然后用一个加密对象进行加密。最后,密文和盐会被连接在一起并返回。如果你想以明文形式发送这些内容,你需要用base64进行编码。
def encrypt(plaintext, password):
salt = Crypto.Random.get_random_bytes(SALT_SIZE)
key = generate_key(password, salt, NUMBER_OF_ITERATIONS)
cipher = AES.new(key, AES.MODE_ECB)
padded_plaintext = pad_text(plaintext, AES_MULTIPLE)
ciphertext = cipher.encrypt(padded_plaintext)
ciphertext_with_salt = salt + ciphertext
return ciphertext_with_salt
解密的过程是反向进行的,先从密文中提取盐,然后用这个盐来解密剩余的密文。最后,使用unpad_text
去除填充。
def decrypt(ciphertext, password):
salt = ciphertext[0:SALT_SIZE]
ciphertext_sans_salt = ciphertext[SALT_SIZE:]
key = generate_key(password, salt, NUMBER_OF_ITERATIONS)
cipher = AES.new(key, AES.MODE_ECB)
padded_plaintext = cipher.decrypt(ciphertext_sans_salt)
plaintext = unpad_text(padded_plaintext)
return plaintext
如果你还有其他问题或需要进一步的解释,请告诉我。