duanrou5680 2019-03-08 12:04
浏览 241
已采纳

在Python和PHP上解密AES256

I'm have problems with AES encryption, using PHP to encrypt and Python to decrypt.

For encrypting, I am using this PHP function:

function cryptpass($arg1) {

    $k = '61b4c705859f4158d38090c1e38e8fdc4f3d29db007f012766276aa498835cf6';
    $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc'));
    $cipher = base64_encode(openssl_encrypt($arg1,'aes-256-cbc',$k,OPENSSL_RAW_DATA,$iv));
    return urlencode(base64_encode('{"cipher":"'.$cipher.'","i":"'.base64_encode($iv).'"}'));
}

And I using this Python code to decrypt:

def decryptpass(info):
   key = '61b4c705859f4158d38090c1e38e8fdc4f3d29db007f012766276aa498835cf6'
   data = json.loads(base64.b64decode(info))
   iv = base64.b64decode(data.get('i'))
   cipher = AES.new(key,AES.MODE_CBC,iv)
   return cipher.decrypt(data.get('cipher'))

But when running this code, the following error occurs:

ValueError: AES key must be either 16, 24, or 32 bytes long

I understand that my key has 64 bytes, but how is PHP encrypt used it? I tried remove last 32 characters from the key, but that is not working.

  • 写回答

1条回答 默认 最新

  • douhui4699 2019-03-08 12:14
    关注

    You are defining a 64-character key; that those 64 characters are hex digits is neither here nor there, the openssl_encrypt() will not decode the hex characters in any way, it uses those characters verbatim.

    But, AES-256 only takes keys with 32 bytes (== 256 bits), not 64, and openssl_encrypt() silently truncates the key. The PyCrypto AES.new() method on the other hand, explicitly tells you that the key is too long, alerting you to your error here, which is that you should probably decode your hex key to bytes first.

    You can successfully decrypt messages if you cut the key down to 32 characters in Python, or if you convert your key from hex to bytes in both cases:

    $k = hex2bin('61b4c705859f4158d38090c1e38e8fdc4f3d29db007f012766276aa498835cf6');
    
    key = bytes.fromhex('61b4c705859f4158d38090c1e38e8fdc4f3d29db007f012766276aa498835cf6')
    

    I strongly recommend decoding over truncating; 32 hex characters have a lot less entropy (the bytes cover as many possible values as the number of values encodable in 32 hex characters squared, 2 to the power 256 vs. 2 to the power 128).

    Because openssl_encrypt() also base64 encodes the return value you need to base64-decode the cipher value on the Python side:

    >>> data = json.loads(base64.b64decode(info))
    >>> data
    {'cipher': 'Iu9VgH8DdxHdQgnq8o23ew==', 'i': 'Vz+wy5VS6toNHx7MEYl+/A=='}
    # base64:   ^^^^^^^^^^^^^^^^^^^^^^^^         ^^^^^^^^^^^^^^^^^^^^^^^^
    

    Finally, openssl_encrypt() adds PKCS#7 padding to the encrypted message to make it fit the AES block size (16 bytes), you need to remove that padding again on the Python side, the PyCrypto AES.decrypt() method doesn't do this for you:

    # Decode from hex to create a key 256 bits (32 bytes) long:
    key = bytes.fromhex('61b4c705859f4158d38090c1e38e8fdc4f3d29db007f012766276aa498835cf6')
    # or, if you don't use hex2bin in PHP, truncate to 32 characters
    # key = b'61b4c705859f4158d38090c1e38e8fdc4f3d29db007f012766276aa498835cf6'[:32]
    
    
    def decryptpass(info):
        data = json.loads(base64.b64decode(info))
        iv = base64.b64decode(data['i'])
        cipher = AES.new(key, AES.MODE_CBC, iv)
        padded = cipher.decrypt(base64.b64decode(data['cipher']))
        # manual PKCS#7 unpadding
        return padded[:-padded[-1:]].decode()
    

    Note, however, that the PyCrypto project has not seen in a new release in 6 years now, and should not be trusted to be secure anymore. You really want to use the cryptography project here instead:

    from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
    from cryptography.hazmat.primitives import padding
    from cryptography.hazmat.backends import default_backend
    
    key = bytes.fromhex('61b4c705859f4158d38090c1e38e8fdc4f3d29db007f012766276aa498835cf6')
    
    def decrypt_aes_256(key, iv, encrypted):
        decryptor = Cipher(
            algorithms.AES(key), modes.CBC(iv), default_backend()
        ).decryptor()
        unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder()
        decrypted = decryptor.update(encrypted) + decryptor.finalize()
        return unpadder.update(decrypted) + unpadder.finalize()
    
    def decryptpass(info):
        data = json.loads(base64.b64decode(info))
        iv = base64.b64decode(data['i'])
        encrypted = base64.b64decode(data['cipher'])
        return decrypt_aes_256(key, iv, encrypted).decode()
    

    Demo, first in PHP:

    $ php -a
    Interactive shell
    
    php > function cryptpass($arg1) {
    php {     $k = hex2bin('61b4c705859f4158d38090c1e38e8fdc4f3d29db007f012766276aa498835cf6');
    php {     $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc'));
    php {     $cipher = base64_encode(openssl_encrypt($arg1,'aes-256-cbc',$k,OPENSSL_RAW_DATA,$iv));
    php {     return urlencode(base64_encode('{"cipher":"'.$cipher.'","i":"'.base64_encode($iv).'"}'));
    php { }
    php > echo cryptpass("Hello, world!");
    eyJjaXBoZXIiOiJJdTlWZ0g4RGR4SGRRZ25xOG8yM2V3PT0iLCJpIjoiVnord3k1VlM2dG9OSHg3TUVZbCsvQT09In0%3D
    

    then in Python; with the cryptography functions defined as above:

    >>> from urllib.parse import unquote
    >>> info = unquote("eyJjaXBoZXIiOiJJdTlWZ0g4RGR4SGRRZ25xOG8yM2V3PT0iLCJpIjoiVnord3k1VlM2dG9OSHg3TUVZbCsvQT09In0%3D")
    >>> decryptpass(info)
    'Hello, world!'
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 使用C#,asp.net读取Excel文件并保存到Oracle数据库
  • ¥15 C# datagridview 单元格显示进度及值
  • ¥15 thinkphp6配合social login单点登录问题
  • ¥15 HFSS 中的 H 场图与 MATLAB 中绘制的 B1 场 部分对应不上
  • ¥15 如何在scanpy上做差异基因和通路富集?
  • ¥20 关于#硬件工程#的问题,请各位专家解答!
  • ¥15 关于#matlab#的问题:期望的系统闭环传递函数为G(s)=wn^2/s^2+2¢wn+wn^2阻尼系数¢=0.707,使系统具有较小的超调量
  • ¥15 FLUENT如何实现在堆积颗粒的上表面加载高斯热源
  • ¥30 截图中的mathematics程序转换成matlab
  • ¥15 动力学代码报错,维度不匹配