【问题标题】:Python AES-256-CBC Encrypt to PHP Decrypt with openssl_decrypt return falsePython AES-256-CBC Encrypt to PHP Decrypt with openssl_decrypt return false
【发布时间】:2022-01-25 07:36:45
【问题描述】:

我在 python 中成功加密数据并将 url 安全数据发送到 PHP。当我尝试使用openssl_decrypt PHP 方法解密数据时,它返回false。我已经阅读了 2 天,知道正在寻找解决方案,但没有一个能解决我的问题 [这个论坛上有 2 个类似的问题,但他们没有帮助,Q1Q2]。

加密使用python3.8和包PyCryptodome

from Crypto.Cipher import AES
import base64
import hashlib
import os
import secrets

BLOCK = 16
PADDING = '~'
iv = secrets.token_urlsafe(16)
secretKey = secrets.token_urlsafe(32) # 32 bytes is used as AES 256 expects 256 bits
encoding = 'latin-1'
s = 'very secret data' # secret to be encrypted

k = hashlib.sha256(secretKey.encode(encoding)).hexdigest()[:32].encode(encoding) # key hashed and encoded
pad = s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * PADDING
cipher = AES.new(key=k, mode=AES.MODE_CBC, IV=hashlib.sha256(iv.encode(encoding)).hexdigest()[:16].encode(encoding)) # cipher object

encrypted_secret = base64.urlsafe_b64encode(cipher.encrypt(pad.encode(encoding)))
print("Key: ", secretKey, "\n", "IV:",iv, "\n", "Encrypted:", encrypted_secret.decode(encoding))

Outputs[0]:

Key:  cdLTCHW95FjHF6ESu2Rkm-90AUbzDBv71HrdshsEx3k 
 IV: b16ezD5O05EDNovsqLExUg 
 Encrypted: RJqA3-n6d7hmKgj7biiwUKD0SjRjm__4f42P06R-qO8=

*我对加密后的秘密结果进行解码,因为它是作为 json 编码对象发送的,而 json 的编码器在其中抱怨字节对象。

在 PHP 中解密的代码更少(对于测试,我将密钥和 iv 复制过来,但 iv 每次都是新创建的,并且密钥存储在不同的服务器上):

$key = 'cdLTCHW95FjHF6ESu2Rkm-90AUbzDBv71HrdshsEx3k';
$iv = 'b16ezD5O05EDNovsqLExUg';
$method = "AES-256-CBC";
$blocksize = 16;
$padwith = '~';
$decoded_secret = 'RJqA3-n6d7hmKgj7biiwUKD0SjRjm__4f42P06R-qO8='; // secret is sent as string

$hashKey = substr(hash('sha256', $key), 0, 32);
$iv_len = openssl_cipher_iv_length($method);
$iv_hash = substr(hash('sha256', $iv), 0, $iv_len); // cipher expects an IV of precisely 16 bytes, padding with \\0

$decrypted_secret = openssl_decrypt($decoded_secret, $method, $hashKey, OPENSSL_RAW_DATA, $iv_hash);

$what = rtrim($decrypted_secret, $padwith);
var_dump($decrypted_secret, $what);

Outputs[1]: bool(false) string(0) ""

我尝试在 python 中将utf-8 设置为编码,在 php 中使用base64 解码加密字符串,将块大小更改为 32,在 python 端不使用hashlib,但没有任何更改给出预期的解密结果。

谁能给我一个线索来解决这个问题?

【问题讨论】:

    标签: python-3.x encryption cryptography aes php-8


    【解决方案1】:

    你实际上需要两条线索,所以我们称之为节日奖金:-)

    • 您的数据在 urlsafe-base64 中,但您的 php 没有对其进行解码,因此它会尝试解密完全错误的数据。 php不(AFAICS)直接支持urlsafe,所以需要转换一下;然后,您可以对其进行解码并传递给openssl_decrypt,或者直接调用openssl_decrypt 没有 OPENSSL_RAW_DATA,因为它默认为(传统的又名MIME 或PEM 或PGP 或XML)base64。

    • 您的加密不使用 PKCS5/7 填充,但 OpenSSL 默认使用该填充,因此您的解密失败。您需要使用OPENSSL_ZERO_PADDING,在这种情况下它变成了无操作,然后(就像您已经做过的那样)自己进行 unpad。

    使用此代码中未注释或已注释的行,它可以工作:

    <?php
    $key = 'cdLTCHW95FjHF6ESu2Rkm-90AUbzDBv71HrdshsEx3k';
    $iv = 'b16ezD5O05EDNovsqLExUg';
    $method = "AES-256-CBC";
    $blocksize = 16; // not used
    $padwith = '~';
    $secret = 'RJqA3-n6d7hmKgj7biiwUKD0SjRjm__4f42P06R-qO8='; // secret is sent as string
    $fixed_secret = str_replace(array('-','_'),array('+','/'),$secret);
    $decoded_secret = base64_decode($fixed_secret); // maybe used
    
    $hashKey = substr(hash('sha256', $key), 0, 32);
    $iv_len = openssl_cipher_iv_length($method);
    $iv_hash = substr(hash('sha256', $iv), 0, $iv_len);
    
    $decrypted_secret = openssl_decrypt($fixed_secret, $method, $hashKey, OPENSSL_ZERO_PADDING, $iv_hash);
    //$decrypted_secret = openssl_decrypt($decoded_secret, $method, $hashKey, OPENSSL_RAW_DATA+OPENSSL_ZERO_PADDING, $iv_hash);
    
    $what = rtrim($decrypted_secret, $padwith);
    var_dump($decrypted_secret, $what);
    ?>
    ->
    string(32) "very secret data~~~~~~~~~~~~~~~~"
    string(16) "very secret data"
    

    【讨论】:

      猜你喜欢
      • 2022-12-02
      • 2022-12-02
      • 2017-09-28
      • 2022-08-17
      • 2021-06-07
      • 2022-11-16
      • 1970-01-01
      • 2012-08-05
      相关资源
      最近更新 更多