Python 3.2 - Symphony AES加密在CBC模式 - 请求建议 [英] Python 3.2 - Symétric AES Crypto in CBC mode - advices requested

查看:221
本文介绍了Python 3.2 - Symphony AES加密在CBC模式 - 请求建议的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的目标是使用python 3.x设置简单的加密,所以我在本周末搜索网络以获得关于RSA / AES等的信息...实际上,可能看起来像一个可能性加密文本数据传递在一个合理的安全的方式..没有偏执狂,我不是一个专家只是想确保这些东西是很难阅读没有钥匙!



老实说,我不太了解加密。经过几个小时的搜索和收集信息和源代码,我的尝试失败,因为无效的长度问题或其他转换错误,由于Python 2.7中提供的示例。我发现很少的例子在python 3和加密方法使用似乎不是真正合适或严重的我。



我终于能够运行以下代码接受ISO 8859-1编码字符。我实际上封装了所有的UTF-8编码,以避免语言问题..我希望如此....



我想知道如果我是在正确的方式的设计,特别是如果数据安全是可以接受的,我再也不是寻找大安全解决方案,只是想保护我自己的个人数据,而不是保护军事防御秘密Lol!



随时向我转发您的意见或建议,尤其是我可能错过的事情。



非常感谢每一个预先。



Emmanuel(法国)



注意:下一步我将尝试向收件人发送RSA加密的AES密码以及文本流。由于AES密码对于每个消息是不同的,因此客户端需要将其自动翻译成能够解密密码消息。 AES密码将使用RSA非对称加密以最强的密钥传输,而不会导致性能下降。目的是在合理的时间范围内传输简单的消息(无需base64编码)或大量数据。



@ +看到你。



要执行下面的代码,你应该安装PyCrypto(python 3.2)

  import os ,base64,hashlib 
从Crypto.Cipher导入AES

类Aes(对象):

#Crypte /décrypteun textedonnéen AES模式CBC。 Accepte l'encodage base64。
#加密输入文本字符串&解密带有或不带base64编码的字节编码字符串
#作者: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éesource en AES模式CBC avec sortie encodage base64 / fichier facultative

@param str源:要编码的文本或文本文件路径
@ param bytes aes_key:password
@parm str outfile:要编写编码文本的磁盘文件。默认为None
@param bool base64_encode:返回base64编码字符串如果为True(对于电子邮件)或字节如果为False

@return bytes ciphertext:字节编码的字符串。
'''


'''
------------------------ ----
输入管理
----------------------------
'''
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

'''
--------------------------- ------------------------------
计算派生密钥(derived_key)。
---------------------------------------------- -----------
Elle permet d'utiliser lacléinitiale(aes_key)plusieurs
fois,une pour chaque blocàencrypter。
---------------------------------------------- -----------
'''

salt = os.urandom(self.SALT_LENGTH)

derived_key = bytes(aes_key, 'UTF-8')

在未使用的范围内(0,self.DERIVATION_ROUNDS):

derived_key = hashlib.sha256(derived_key + salt).digest
$ b derived_key = derived_key [:self.KEY_SIZE]

'''
----------------
加密
----------------
'''
#初始化向量应该是随机的
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

'''
-------------------------
输出管理
- -----------------------
'''
如果outfile是None:
'''
返回base64编码中的密码。有用的电子邮件管理例如
'''
如果base64_encode:
return(base64.b64encode(cipher_text))
else:
return(cipher_text)

else:
'''
将结果写入磁盘
'''

fp = open(outfile,'w')

if base64_encode:
fp.write(base64.b64encode(cipher_text))
else:
fp.write(cipher_text)

fp.close (self,source,aes_key,outfile = None,base64_encode = False)

print('Cipher text saved in',outfile)



'''
解密编码的字符串或数据文件

@param bytes或str source:解码或文件路径的加密字节字符串
@param bytes aes_key: password
@parm str outfile:将编码文本写入的磁盘文件。默认为None
@param bool base64_encode:密码文本被给予base64编码(对于邮件内容的示例)

@returns str secret_text:解码文本字符串或无如果无效键给定
'''

'''
---------------------------
输入管理
---------------------------
'''

if类型(源)== str和os.path.exists(源):

fp = open(source,'rb')
ciphertext = fp.read()
fp.close()

elif类型(源)==字节:
密文=源

else:
print('Invalid data source' )
return

if base64_encode:
encoded_text = base64.b64decode(ciphertext)
else:
#decodedCiphertext = ciphertext.decode(hex)
encoded_text = ciphertext

'''
-------------------------
计算派生密钥
-------------------------
'''

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')

用于未使用的范围(0,self.DERIVATION_ROUNDS):
derived_key = hashlib.sha256(derived_key + salt).digest()

derived_key = derived_key [:self.KEY_SIZE]


'''
- -----------------------
解密
------------------ -------
'''
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 pasdécodé(mot de passe invalide) ,la convert UTF-8 plante ou au mieux on obtient un texte illisible
'''
try:
secret_text = str(secret_text,'utf-8')

return

如果outfile为None:

return(secret_text)

else:
'''
将结果写入磁盘
'''
fp = open(outfile,'w')
fp.write(secret_text)
fp.close()



最终内容



ve进行了以下更改:




  • 使用PBKDF2作为HMAC-sha512的KDF


  • 强制性套件现在是:PyCypto& pbkdf2-1.3



我尝试过很多次插入新的代码块,但它不工作。

解决方案

你做的比我预期的更好:P。
只是几个建议来改善你的代码:




  • 如果你使用一个漂亮,着名,密钥推导函数,如 PBKDF2 和HMAC-sha256。

  • 您可以考虑使用os.random而不是os.urandom的可能性(或至少make

  • 您可以向加密输出中添加一些标头,这样您就可以在不知道键大小和其他变量的情况下对其进行解密,

  • 向用户提供更简单的方法来更改现在硬编码的设置。



此外,为了下一步,我建议您查看 DH密钥交换。这将给你完美的向前保密。


My goal is to setup simple encryption with python 3.x, so I searched the web this weekend to get informations about RSA / AES and so on ... Actually, things that may look like a possibility to encrypt text data for transmission in a reasonable secure way .. without paranoia either, I'm not an expert just want to make sure that the stuff is pretty hard to read without the key !

Honestly, I do not know much about cryptography. After several hours of searching and collecting information and source code, my attempts failed because of invalid length problems or other conversion errors due to the examples provided in Python 2.7 . I found very few examples in python 3 and encryption methods used seemed to be not really appropriate or serious me.

I was finally was able to run the following code that accepts ISO 8859-1 coded characters. I actually encapsulates everything in UTF-8 encoding to avoid language issues .. I hope so ....

I would like to know if I'm on the right way of design and especially if data security is acceptable, again I'm not looking for the Great security solution, just want to protect my own personnal data and not to protect a military defense secret Lol !

Feel free to forward me your comments or suggestions and especially things I could have missed !

Thanks very much per advance.

Emmanuel (France)

Note: next step I'll try to send an RSA encrypted AES password to the recipient along with the text stream. As the AES password is different for each message the client needs to automatically translate it to be able to decode the cipher message. The AES password will be transmitted in RSA asymmetric encryption with the strongest possible key without performance breakdown. The aim is to transmit simple messages ( w/o base64 encoding) or large volumes of data in a reasonable timeframe.

@+ see you.

To execute the code bellow, you should have PyCrypto (python 3.2) installed

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()

final stuff

I've made the following changes:

  • uses PBKDF2 as KDF with HMAC-sha512
  • Fixed the constant issue
  • mandatory packages are now : PyCypto & pbkdf2-1.3

I've tried many time to insert the new code block ... but it doesn't work. Very strange behaviour of the text editor.

解决方案

You are doing better than I was expecting :P. Just a couple of suggestions to improve a bit your code:

  • Would be better if you use a nice, famous and strong key derivation function like PBKDF2 with HMAC-sha256. Your KDF looks strong, but when speaking about cryptography is better to rely on widely reviewed algorithms.
  • You may consider the possibility to use os.random instead of os.urandom (or at least make it simple to switch from one to another) for more entropy.
  • You could add some "headers" to encrypted output which would let you decrypt it without knowing before keys sizes and other variable things, that now are hard-coded.
  • Give the user an easier way to change those settings that now are hard-coded.

Also, for your next step, I suggest you to take a look at DH key exchange. This will give you perfect forward secrecy.

这篇关于Python 3.2 - Symphony AES加密在CBC模式 - 请求建议的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆