我的目标是使用python 3.x设置简单加密,所以我在本周末搜索了网页以获取有关RSA / AES等的信息......实际上,可能看起来像是加密文本数据的可能性以合理的安全方式传播..也没有偏执狂,我不是专家只是想确保没有钥匙的东西很难读!
老实说,我对密码学知之甚少。经过几个小时的搜索和收集信息和源代码后,由于Python 2.7中提供的示例导致无效长度问题或其他转换错误,我的尝试失败了。我在python 3中发现了很少的例子,并且使用的加密方法似乎并不合适或严重。
我终于能够运行以下接受ISO 8859-1编码字符的代码。我实际上用UTF-8编码封装了所有内容以避免语言问题。我希望如此......
我想知道我是否采用正确的设计方式,特别是如果数据安全性可以接受,我再也不是在寻找伟大的安全解决方案,只是想保护自己的个人数据而不是保护一个军事防御秘密哈哈!
随时转发您的意见或建议,特别是我可能错过的内容!
非常感谢每次前进。
Emmanuel(法国)注意:下一步我将尝试将RSA加密的AES密码与文本流一起发送给收件人。由于每个消息的AES密码不同,客户端需要自动将其转换为能够解码密码消息。 AES密码将使用最强密钥以RSA非对称加密方式传输,而不会出现性能故障。目的是在合理的时间范围内传输简单的消息(没有base64编码)或大量数据。
@ +见。
要执行下面的代码,您应该安装PyCrypto(python 3.2)
import os, base64, hashlib
from Crypto.Cipher import AES
class Aes(object):
# Crypte / décrypte un texte donné en AES mode CBC. Accepte l'encodage base64.
# Encrypts input text string & decrypts bytes encoded string with or without base64 encoding
# Author: emmanuel.brunet@live.fr - 12/2013
SALT_LENGTH = 64
DERIVATION_ROUNDS=10000
BLOCK_SIZE = 16
KEY_SIZE = 256
MODE = AES.MODE_CBC
def encrypt(self, source, aes_key, outfile=None, base64_encode=False):
'''
Crypte l'entrée source en AES mode CBC avec sortie encodage base64 / fichier facultative
@param str source: text to encode or text file path
@param bytes aes_key: password
@parm str outfile: disk file to write encoded text to. defaults to None
@param bool base64_encode: returns base64 encoded string if True (for emails) or bytes if False
@return bytes ciphertext: the bytes encoded string.
'''
'''
----------------------------
Inputs management
----------------------------
'''
if os.path.exists(source):
fp = open(source, 'rb')
input_text = fp.read()
fp.close()
else:
input_text = bytes(source, 'UTF-8')
if input_text == b'':
print('No data to encrypt')
return
padding_len = 16 - (len(input_text) % 16)
padded_text = str(input_text, 'UTF-8') + chr(padding_len) * padding_len
'''
---------------------------------------------------------
Computes the derived key (derived_key).
---------------------------------------------------------
Elle permet d'utiliser la clé initiale (aes_key) plusieurs
fois, une pour chaque bloc à encrypter.
---------------------------------------------------------
'''
salt = os.urandom(self.SALT_LENGTH)
derived_key = bytes(aes_key, 'UTF-8')
for unused in range(0,self.DERIVATION_ROUNDS):
derived_key = hashlib.sha256(derived_key + salt).digest()
derived_key = derived_key[:self.KEY_SIZE]
'''
----------------
Encrypt
----------------
'''
# The initialization vector should be random
iv = os.urandom(self.BLOCK_SIZE)
cipherSpec = AES.new(derived_key, self.MODE, iv)
cipher_text = cipherSpec.encrypt(padded_text)
cipher_text = cipher_text + iv + salt
'''
-------------------------
Output management
-------------------------
'''
if outfile is None:
'''
Returns cipher in base64 encoding. Useful for email management for instance
'''
if base64_encode:
return(base64.b64encode(cipher_text))
else:
return(cipher_text)
else:
'''
Writes result to disk
'''
fp = open(outfile, 'w')
if base64_encode:
fp.write(base64.b64encode(cipher_text))
else:
fp.write(cipher_text)
fp.close()
print('Cipher text saved in', outfile)
def decrypt(self, source, aes_key, outfile=None, base64_encode=False):
'''
Decrypts encoded string or data file
@param bytes or str source: encrypted bytes string to decode or file path
@param bytes aes_key: password
@parm str outfile: disk file to write encoded text to. defaults to None
@param bool base64_encode: cipher text is given base64 encoded (for mails content for examples)
@returns str secret_text: the decoding text string or None if invalid key given
'''
'''
---------------------------
Input management
---------------------------
'''
if type(source) == str and os.path.exists(source):
fp = open(source, 'rb')
ciphertext = fp.read()
fp.close()
elif type(source) == bytes:
ciphertext = source
else:
print('Invalid data source')
return
if base64_encode:
encoded_text = base64.b64decode(ciphertext)
else:
# decodedCiphertext = ciphertext.decode("hex")
encoded_text = ciphertext
'''
-------------------------
Computes derived key
-------------------------
'''
iv_start = len(encoded_text) - self.BLOCK_SIZE - self.SALT_LENGTH
salt_start = len(encoded_text) - self.SALT_LENGTH
data, iv, salt = encoded_text[:iv_start], encoded_text[iv_start:salt_start], encoded_text[salt_start:]
derived_key = bytes(aes_key, 'utf-8')
for unused in range(0, self.DERIVATION_ROUNDS):
derived_key = hashlib.sha256(derived_key + salt).digest()
derived_key = derived_key[:self.KEY_SIZE]
'''
-------------------------
Decrypt
-------------------------
'''
Cipher = AES.new(derived_key, self.MODE, iv)
padded_text = Cipher.decrypt(data)
padding_length = padded_text[-1]
secret_text = padded_text[:-padding_length]
'''
Si le flux n'est pas décodé (mot de passe invalide), la conversion UTF-8 plante ou au mieux on obtient un texte illisible
'''
try:
secret_text = str(secret_text, 'utf-8')
except:
return
if outfile is None:
return(secret_text)
else:
'''
Writes result to disk
'''
fp = open(outfile, 'w')
fp.write(secret_text)
fp.close()
最终内容
我做了以下更改:
我已经尝试了很多时间来插入新的代码块...但它不起作用。文本编辑器的行为非常奇怪。
答案 0 :(得分:1)
你的表现比我预期的要好:P。 只需要几点建议来改进您的代码:
另外,为了您的下一步,我建议您查看DH key exchange。这将为您提供完美的前瞻性保密。
答案 1 :(得分:1)
padding_length = padded_text[-1]
(参见有关HMAC的部分); AES.block_size
代替16; BaseRNG
中的任何内容,但默认情况下使用OSRNG
请注意,我发现Python加密的RNG类非常难以理解,如果找不到利用库中的方法的好方法,请保留os.urandom
。
答案 2 :(得分:0)
最后一个源版本0.3。希望它会帮助别人。
# -*- coding: utf-8 -*-
from Crypto.Cipher import AES
from Crypto.Hash import HMAC, SHA512
from pbkdf2 import PBKDF2
import os, base64, bz2, binascii
class Aes(object):
'''
Crypte / décrypte un texte donné en AES mode CBC. Accepte l'encodage base64.
Encrypts input text string & decrypts bytes encoded string with or without base64 encoding
PyCrypto and pbkdf2-1.3 packages are mandatory
Author: emmanuel.brunet@live.fr - 12/2013
'''
SALT_LENGTH = 32 # 32 bytes = 256 bits salt
DERIVATION_ROUNDS=7000
KEY_SIZE = 32 # 256 bits key
MODE = AES.MODE_CBC
def encrypt(self, source, aes_key, outfile=None, base64_encode=False):
'''
Crypte l'entrée source en AES mode CBC avec sortie encodage base64 / fichier facultative
@param str source: text to encode or text file path
@param bytes aes_key: password in byte
@parm str outfile: disk file to write encoded text to. defaults to None
@param bool base64_encode: returns base64 encoded string if True (for emails) or bytes if False
@return bytes ciphertext: the bytes encoded string.
'''
'''
----------------------------
Inputs management
----------------------------
'''
if os.path.exists(source):
fp = open(source, 'rb')
input_text = fp.read()
fp.close()
else:
input_text = bytes(source, 'UTF-8')
if input_text == b'':
print('No data to encrypt')
return
'''
# padding_len = AES.block_size - (len(input_text) % AES.block_size)
# padded_text = str(input_text, 'UTF-8') + chr(padding_len) * padding_len
'''
'''
-------------------
Compress
------------------
'''
cmp_text = bz2.compress(input_text)
b64_bin = base64.b64encode(cmp_text)
b64_str = str(b64_bin, 'UTF-8')
padding_len = AES.block_size - (len(b64_str) % AES.block_size)
padded_text = b64_str + chr(padding_len) * padding_len
'''
---------------------------------------------------------
Derived key computing PBKDF2 / specs RSA PKCS#5 V2.0
---------------------------------------------------------
'''
salt = os.urandom(self.SALT_LENGTH)
derived_key = PBKDF2(bytes(aes_key, 'UTF-8'), salt, iterations=self.DERIVATION_ROUNDS, digestmodule=SHA512, macmodule=HMAC).read(self.KEY_SIZE)
'''
----------------
Encrypt
----------------
'''
# le vecteur d'initialisation doit être aléatoire
iv = os.urandom(AES.block_size)
Cipher = AES.new(derived_key, self.MODE, iv)
cipher_text = Cipher.encrypt(padded_text)
cipher_text = cipher_text + iv + salt
# cipher_text = salt + cipher_text
'''
-------------------------
Output management
-------------------------
'''
if outfile is None:
'''
Returns cipher in base64 encoding. Useful for email management for instance
'''
if base64_encode:
return(base64.b64encode(cipher_text))
else:
return(cipher_text)
else:
'''
Writes result to disk
'''
fp = open(outfile, 'w')
if base64_encode:
fp.write(base64.b64encode(cipher_text))
else:
fp.write(cipher_text)
fp.close()
print('Cipher text saved in', outfile)
def decrypt(self, source, aes_key, outfile=None, base64_encode=False):
'''
@param bytes or str source: encrypted bytes string to decode or file path
@param bytes aes_key: password
@parm str outfile: disk file to write encoded text to. defaults to None
@param bool base64_encode: cipher text is given base64 encoded (for mails content for examples)
@returns str secret_text: the decoding text string or None if invalid key given
'''
'''
---------------------------
Input management
---------------------------
'''
if type(source) == str and os.path.exists(source):
fp = open(source, 'rb')
ciphertext = fp.read()
fp.close()
elif type(source) == bytes:
ciphertext = source
else:
print('Invalid data source')
return
if base64_encode:
encoded_text = base64.b64decode(ciphertext)
else:
encoded_text = ciphertext
salt_start = len(encoded_text) - self.SALT_LENGTH
iv_start = len(encoded_text) - AES.block_size - self.SALT_LENGTH
data, iv, salt = encoded_text[:iv_start], encoded_text[iv_start:salt_start], encoded_text[salt_start:]
'''
-------------------------
Derived key computing
-------------------------
'''
# derived_key = PBKDF2(bytes(aes_key, 'UTF-8'), salt).read(self.KEY_SIZE)
derived_key = PBKDF2(bytes(aes_key, 'UTF-8'), salt, iterations=self.DERIVATION_ROUNDS, digestmodule=SHA512, macmodule=HMAC).read(self.KEY_SIZE)
'''
-------------------------
Decrypt
-------------------------
'''
Cipher = AES.new(derived_key, self.MODE, iv)
padded_text = Cipher.decrypt(data)
padding_length = padded_text[-1]
secret_text = padded_text[:-padding_length]
'''
--------------------------
Decompress
--------------------------
'''
cmp_text = base64.b64decode(secret_text)
secret_text = bz2.decompress(cmp_text)
'''
Si le flux n'est pas décodé (mot de passe invalide), la conversion UTF-8 plante ou au mieux on obtient un texte illisible
'''
try:
secret_text = str(secret_text, 'utf-8')
except:
return
if outfile is None:
return(secret_text)
else:
'''
Writes result to disk
'''
fp = open(outfile, 'w')
fp.write(secret_text)
fp.close()