【问题标题】:Python AES CTR to PHPPython AES CTR 到 PHP
【发布时间】:2020-08-13 13:45:21
【问题描述】:

我尝试在 php 中进行这种 AES CTR 加密(在 python 中可以正常工作)

工作蟒

from Crypto.Cipher import AES
import Crypto.Util.Counter
from Crypto.Util import Counter
key = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]
encryptkey = bytes(key)
ctr = Counter.new(128)
crypto = AES.new(encryptkey, AES.MODE_CTR, counter=ctr)
text = "E5ZA,K2JV,PA01,J1W3,386S,AGVZ,9O9T,F640,FR20,40LX,D443,1913,031V"
bytetext = bytes(text,'utf-8')
encryptedtext = crypto.encrypt(bytetext)
encryptedtext = encryptedtext.hex()
print(encryptedtext)

我的php尝试

<?php
function strToHex($string){
    $hex='';
    for ($i=0; $i < strlen($string); $i++){
        $hex .= dechex(ord($string[$i]));
    }
    return $hex;
}

$bytes = array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31);
$lkey= implode(array_map("chr", $bytes));
$method = "AES-256-CTR";
$password = $lkey;
$data = array("E5ZA", "K2JV", "PA01", "J1W3", "386S", "AGVZ", "9O9T", "F640", "FR20", "40LX", "D443", "1913", "031V");
$string= implode(array_map("chr", $data));
$valid = openssl_encrypt ($string, $method, $password);
echo strToHex($valid)
?>

我得到的错误和错误结果

Warning: openssl_encrypt(): Using an empty Initialization Vector (iv) is potentially insecure and not recommended in /test.php on line 16
387041417471684a6c74437032356f5477673d3d

我期望的回应:

b5682cef66f2adaff0dacb7078f31a773feb86f28614b5ee24e9ee634200a8d6eb177a151eb55003c6cc81b3e9cb6d1a1673a2881ec194370af242d9f1fd5818

提前感谢您的帮助

【问题讨论】:

    标签: python php encryption aes


    【解决方案1】:

    两个代码给出不同的结果,原因如下:

    1. 在 Python 代码中,计数器的大小为 16 字节。计数器的初始值为 1(默认情况下)。正如在另一个答案[1] 中已经提到的那样,PHP 代码缺少 IV。要在两个代码中获得相同的结果,必须在每种情况下使用 same IV。
    2. 在 Python 代码中,数据用逗号分隔。因此,在 PHP 代码中连接时必须使用逗号作为分隔符。
    3. 在 PHP 代码中,数据(关于后面的十六进制编码)不能返回 Base64 编码,而是作为原始数据,即标志 OPENSSL_RAW_DATA 必须设置。
    4. 转换为十六进制字符串时,每个值必须以两位数字输出,即如有必要,以0 开头。

    以下 PHP 代码包含必要的更改,因此给出的结果与 Python 代码相同:

    function strToHex($string){
        $hex='';
        for ($i=0; $i < strlen($string); $i++){
            $hex .= sprintf("%02s",dechex(ord($string[$i])));                        // 4. formatting                                                   
        }
        return $hex;
    }
    
    $bytes = array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31);
    $lkey= implode(array_map("chr", $bytes));
    $method = "AES-256-CTR";
    $password = $lkey;
    $data = array("E5ZA", "K2JV", "PA01", "J1W3", "386S", "AGVZ", "9O9T", "F640", "FR20", "40LX", "D443", "1913", "031V");
    $string= implode(",", $data);                                                    // 2. delimiter
    $iv = hex2bin("00000000000000000000000000000001");                               // 1. right IV
    $valid = openssl_encrypt ($string, $method, $password, OPENSSL_RAW_DATA, $iv);   // 3. raw data 
    echo "Result:             " . strToHex($valid) . "\n";
    echo "Result from Python: " . "b5682cef66f2adaff0dacb7078f31a773feb86f28614b5ee24e9ee634200a8d6eb177a151eb55003c6cc81b3e9cb6d1a1673a2881ec194370af242d9f1fd5818" . "\n";
    

    顺便说一句,可以使用内置函数bin2hex 代替strToHex

    注意:对于 CTR,在同一密钥下重复使用 IV 会破坏安全性 [2]。因此,在当前的实现中,每次加密都必须使用一个新密钥(因为使用了修复 IV)。另一种方法是将 IV 拆分为一个随机数和一个计数器,每个加密随机生成随机数[3][4]

    【讨论】:

    • 非常感谢您的解释,这对我理解有很大帮助
    【解决方案2】:

    在 openssl_encrypt 中,您需要一个 IV 来确保加密文本是唯一的。

    $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
    $generatediv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
    

    然后

    $valid = openssl_encrypt ($string, $method, $password, 0, $generatediv);
    

    希望成功

    【讨论】:

    • 我认为 OP 在这里关心的是他必须在 PHP 代码中更改什么,以便结果与 Python 代码匹配。但是你是对的,应该应用随机 IV,至少对于修复密钥,因为它可以防止多次使用相同的密钥/IV 对,这会破坏 CTR 的安全性。关于您的 mcyrpt 方法,请注意它们是deprecated
    猜你喜欢
    • 2013-05-05
    • 1970-01-01
    • 1970-01-01
    • 2015-06-09
    • 1970-01-01
    • 2011-03-12
    • 1970-01-01
    • 2017-08-08
    • 1970-01-01
    相关资源
    最近更新 更多