【问题标题】:PHP aes-128-gcm openssl_decrypt only works after using openssl_encryptPHP aes-128-gcm openssl_decrypt 仅在使用 openssl_encrypt 后有效
【发布时间】:2020-12-21 08:04:51
【问题描述】:

我必须解密从外部方获得的 aes-128-gcm 加密数据。由于 openssl_decrypt 从未返回任何数据,因此我尝试自己加密其他地方解密的数据,看看这是否有效,实际上我收到了我尝试解密的相同加密数据。因此我知道,我所有的参数都是正确的。所以我玩弄了我的PHP代码并得出了一个奇怪的结论,在我加密明文之后解密数据只对我有用?!? 有人知道这里发生了什么吗?

谢谢, 哈利

<?php

$method='aes-128-gcm';

$key = hex2bin('0748BEF58E04D5917ED0B9B558628265');

//echo "iv_length: ". openssl_cipher_iv_length($method)."<br>";
$iv = hex2bin('534D5367700114E600102D29');
$tag = NULL;

$enc = hex2bin('09E89C959CD513057787832142E6796E1F6DE55CBA8E5CEC6E16AA635B3B102DDB22D85841923DDC2EE3052027945DFD00D025A0A5D0EB385E0033DD28037D80B47522B3DB310B01871474686B609D2DA15864785895DF2BE887');
$plain = hex2bin('0F00102D280C07E4081F01103B1000FF8880020C09060006190900FF090D323232313230323031323735360904103B1000090507E4081F0106004C48DF06000000CB06000089CF06000E61E7060000020A060000000009000900');

$decrypted = openssl_decrypt($enc, $method, $key, OPENSSL_RAW_DATA, $iv, $tag);
$encrypted = openssl_encrypt($plain, $method, $key, OPENSSL_RAW_DATA, $iv, $tag);
$decrypted2 = openssl_decrypt($enc, $method, $key, OPENSSL_RAW_DATA, $iv, $tag);

echo "plain: ".bin2hex($plain)."<br>";
echo "enc: ".bin2hex($encrypted)."<br>";
echo "dec: ".bin2hex($decrypted)."<br>";
echo "dec2: ".bin2hex($decrypted2)."\n";

while ($msg = openssl_error_string())
    echo $msg . "<br>\n";
?>

输出:

简单: 0f00102d280c07e4081f01103b1000ff8880020c09060006190900ff090d323232313230323031323735360904103b1000090507e4081f0106004c48df06000000cb06000089cf06000e61e7060000020a060000000009000900 P>

编码: 09e89c959cd513057787832142e6796e1f6de55cba8e5cec6e16aa635b3b102ddb22d85841923ddc2ee3052027945dfd00d025a0a5d0eb385e0033dd28037d80b47522b3db310b01871474686b609d2da15864785895df2be887 P>

十二月:

dec2: 0f00102d280c07e4081f01103b1000ff8880020c09060006190900ff090d323232313230323031323735360904103b1000090507e4081f0106004c48df06000000cb06000089cf06000e61e7060000020a060000000009000900 P>

【问题讨论】:

    标签: php encryption php-openssl aes-gcm


    【解决方案1】:

    欢迎来到 Stackoverflow。您在GCM 模式下使用 AES 算法,这意味着密文可以通过“身份验证标签”或短“标签”进行修改。此标签在使用 AES-GCM 加密明文时生成,需要在解密密文时可用。

    在您的代码中,您向解密函数提供了一个空的 $tag 变量,但解密失败。使用 openssl_encrypt 生成“新”明文时,您的 $tag-variable 会被标记填充。现在您再次解密并将此标记提供给 openssl_decrypt-function,解密工作如预期。

    所以需要从第三方获取$tag的值,才能成功将密文解密回明文。

    使用源代码中的这个小改动,程序提供了 $tag:

    $decrypted = openssl_decrypt($enc, $method, $key, OPENSSL_RAW_DATA, $iv, $tag);
    echo "tag: ".bin2hex($tag)."<br>" . PHP_EOL;
    $encrypted = openssl_encrypt($plain, $method, $key, OPENSSL_RAW_DATA, $iv, $tag);
    echo "tag: ".bin2hex($tag)."<br>" . PHP_EOL;
    $decrypted2 = openssl_decrypt($enc, $method, $key, OPENSSL_RAW_DATA, $iv, $tag);
    

    结果:

    tag: <br>
    tag: 9268f3568512fc9f15075096c1b47902<br>
    

    使用解决方案编辑

    根据@Maarten Bodewes (https://stackoverflow.com/a/49244840/8166854) 的回答,有可能破解“aes gcm” 没有身份验证标签的加密数据,因为

    AES GCM = AES CTR + AuthTag
    

    如下更改您的源代码会在第一次运行时按预期解密密码,我手动将十六进制数据“00000002”添加到 iv:

    plain:  0f00102d280c07e4081f01103b1000ff8880020c09060006190900ff090d323232313230323031323735360904103b1000090507e4081f0106004c48df06000000cb06000089cf06000e61e7060000020a060000000009000900<br>
    enc:    09e89c959cd513057787832142e6796e1f6de55cba8e5cec6e16aa635b3b102ddb22d85841923ddc2ee3052027945dfd00d025a0a5d0eb385e0033dd28037d80b47522b3db310b01871474686b609d2da15864785895df2be887<br>
    decCtr: 0f00102d280c07e4081f01103b1000ff8880020c09060006190900ff090d323232313230323031323735360904103b1000090507e4081f0106004c48df06000000cb06000089cf06000e61e7060000020a060000000009000900<br>
    decGcm: 0f00102d280c07e4081f01103b1000ff8880020c09060006190900ff090d323232313230323031323735360904103b1000090507e4081f0106004c48df06000000cb06000089cf06000e61e7060000020a060000000009000900
    

    代码:

    <?php
    $method='aes-128-gcm';
    $key = hex2bin('0748BEF58E04D5917ED0B9B558628265');
    $iv = hex2bin('534D5367700114E600102D29');
    $tag = NULL;
    $enc = hex2bin('09E89C959CD513057787832142E6796E1F6DE55CBA8E5CEC6E16AA635B3B102DDB22D85841923DDC2EE3052027945DFD00D025A0A5D0EB385E0033DD28037D80B47522B3DB310B01871474686B609D2DA15864785895DF2BE887');
    $plain = hex2bin('0F00102D280C07E4081F01103B1000FF8880020C09060006190900FF090D323232313230323031323735360904103B1000090507E4081F0106004C48DF06000000CB06000089CF06000E61E7060000020A060000000009000900');
    
    //$decrypted = openssl_decrypt($enc, $method, $key, OPENSSL_RAW_DATA, $iv, $tag);
    $methodCtr = 'aes-128-ctr';
    $ivCtr = hex2bin('534D5367700114E600102D2900000002');
    $decryptedCtr = openssl_decrypt($enc, $methodCtr, $key, OPENSSL_RAW_DATA, $ivCtr);
    $encrypted = openssl_encrypt($plain, $method, $key, OPENSSL_RAW_DATA, $iv, $tag);
    echo "tag: ".bin2hex($tag)."<br>" . PHP_EOL;
    $decryptedGcm = openssl_decrypt($enc, $method, $key, OPENSSL_RAW_DATA, $iv, $tag);
    
    echo "plain:  ".bin2hex($plain)."<br>" . PHP_EOL;
    echo "enc:    ".bin2hex($encrypted)."<br>" . PHP_EOL;
    echo "decCtr: ".bin2hex($decryptedCtr)."<br>" . PHP_EOL;
    echo "decGcm: ".bin2hex($decryptedGcm)."\n" . PHP_EOL;
    while ($msg = openssl_error_string())
        echo $msg . "<br>\n";
    ?>
    

    【讨论】:

    • 这解释了 PHP 的行为。但是原始加密是根据DLMS/COSEM绿皮书dlms.com/files/Green_Book_Edition_9-Excerpt.pdf根据第9.2.7.2.4段进行的,AES-GCM只支持认证,只支持加密和加密+认证。因此,在仅加密模式下,不需要标签并将其定义为填充 00 的 12 字节 -> 因此我的 $tag 值为空,因为 $tag = hex2bin('000000000000000000000000') 没有区别。
    • 太棒了!我已经怀疑它可能可以通过 CTR 模式解决,但找不到额外 IV 字节的信息。
    猜你喜欢
    • 2019-10-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-21
    • 2016-12-19
    • 2023-03-12
    • 1970-01-01
    相关资源
    最近更新 更多