PHP 使用 Phpseclib 加密 AES-256-GCM 并使用 crypto.subtle 使用 Javascript 解密

问题描述 投票:0回答:1

为了实现我所写的主题,我有这个 php 代码:

<?php
require 'vendor/autoload.php';
use phpseclib3\Crypt\AES; // Per l'encryptMessage
use phpseclib3\Crypt\Random; // Per la generateSharedKey

function generateSharedKey() {
    return base64_encode(Random::string(16));
}
function encryptMessage($messaggio_da_criptare, $chiave) {
    $aes = new AES('gcm');
    $aes->setKey($chiave);
    $iv=base64_encode(openssl_random_pseudo_bytes(12));
    $aes->setNonce($iv);
    $testo_cifrato = $aes->encrypt($messaggio_da_criptare);
    $tag = $aes->getTag();
    $dati_criptati = ['messaggio_criptato_con_tag' => base64_encode($testo_cifrato.$tag),'iv' => $iv];
    return $dati_criptati;
}

$messaggio = "Hello, this is a secret message!";
echo "Messaggio di esempio da criptare: ".$messaggio."<br>";
$chiave = generateSharedKey();
echo "Chiave segreta: ".$chiave."<br>";
$dati_criptati = encryptMessage($messaggio, $chiave);
echo "Messaggio criptato con tag incluso: ".$dati_criptati["messaggio_criptato_con_tag"]."<br>";
echo "IV: ".$dati_criptati["iv"]."<br>";
?>

还有这个html代码

<!DOCTYPE html>
<html>
<script>
var phpkey = "TWw4QCkeZEnXoCDkI1GEHQ==";
var phpiv = "CRKTyQoWdWB2n56f";
var phpmessageencrypted = "7K+HAB7Ch9V4jJ1XJPM0sANXA2ocJok=";

(async () => {
    function _base64ToArrayBuffer(base64) {
        var binary_string = atob(base64);
        var len = binary_string.length;
        var bytes = new Uint8Array(len);
        for (var i = 0; i < len; i++) {
            bytes[i] = binary_string.charCodeAt(i);
        }
        return bytes.buffer;
    }
    async function _importKeyAes(key) {
        return await window.crypto.subtle.importKey("raw",key,{ name: "AES-GCM" },false,["encrypt", "decrypt"]);
    }
    async function decryptMessageSymetric(key, data, iv) {
        var keyArrayBuffer = _base64ToArrayBuffer(key);
        var keyt = await _importKeyAes(keyArrayBuffer);
        var ivt = new TextEncoder().encode(iv);
        var datat = _base64ToArrayBuffer(data);
        var result = await window.crypto.subtle.decrypt({ name: "AES-GCM", iv: ivt, tagLength: 128 },keyt,datat);
        return new TextDecoder().decode(result);
    }
    var result = await decryptMessageSymetric(phpkey, phpmessageencrypted, phpiv); 
    console.log(result);
})();
</script>
</html>type here

问题是: 如果您启动 php 脚本并使用如下在线工具测试结果: 文字 它有效并且似乎加密步骤是正确的。

如果您启动 html 页面并与检查员进行检查,则使用提供的示例数据它可以工作。

但是,如果你更换:

var phpkey = "TWw4QCkeZEnXoCDkI1GEHQ=="; var phpiv = "CRKTyQoWdWB2n56f"; var phpmessageencrypted = "7K+HAB7Ch9V4jJ1XJPM0sANXA2ocJok=";

使用 php 给定的数据,它不起作用,浏览器给出以下(且无用的)错误代码:

Uncaught (in promise) DOMException: The operation failed for an operation-specific reason
而在线工具报告了这一点:

我真的不知道我做错了什么。可能与 KEY 和 IV 有关,但为什么它们被在线工具接受,而不被 html/js 脚本接受?

感谢您的帮助。

我期待它以确定性的方式工作。

javascript php google-cloud-messaging aes
1个回答
0
投票

问题在于,在 PHP 代码中,直接使用 Base64 编码密钥,即不进行 Base64 解码。如果这也是在 JavaScript 代码中完成的,则解密有效,如以下示例所示(数据是使用您发布的 PHP 代码生成的):

// Attention: works e.g. in Firefox, but not in Chrome and Chrome-based browsers, for an explanation see below! var phpkey = "YIUDaeHCjlX3gQb2nxFIxg=="; var phpiv = "BwKuWNyG/PBKr4H/"; var phpmessageencrypted = "J1DQz24HB6ka4tZhhz08hV3TrMIrms/+56ah8n2z3N02LeDEYWzIBIMH2BC6RcyW"; (async () => { function _base64ToArrayBuffer(base64) { var binary_string = atob(base64); var len = binary_string.length; var bytes = new Uint8Array(len); for (var i = 0; i < len; i++) { bytes[i] = binary_string.charCodeAt(i); } return bytes.buffer; } async function _importKeyAes(key) { return await window.crypto.subtle.importKey("raw",key,{ name: "AES-GCM" },false,["encrypt", "decrypt"]); } async function decryptMessageSymetric(key, data, iv) { // var keyArrayBuffer = _base64ToArrayBuffer(key); var keyArrayBuffer = new TextEncoder().encode(key); // Fix! var keyt = await _importKeyAes(keyArrayBuffer); var ivt = new TextEncoder().encode(iv); var datat = _base64ToArrayBuffer(data); var result = await window.crypto.subtle.decrypt({ name: "AES-GCM", iv: ivt, tagLength: 128 },keyt,datat); return new TextDecoder().decode(result); } var result = await decryptMessageSymetric(phpkey, phpmessageencrypted, phpiv); console.log(result); })();

请注意,如果没有 Base64 解码 24 字节密钥,则使用 AES-192!因此,

该代码不适用于 Chrome 和基于 Chrome 的浏览器

,因为它们不支持 AES-192。可以运行代码的可能浏览器是 Firefox。

但是,使用 AES-192,尤其是浏览器限制可能不是您想要的,而是使用 AES-128 以及对 Chrome 和基于 Chrome 的浏览器的支持。
必须在 PHP 代码中对此进行修复:由于

generateSharedKey()
提供的密钥是 Base64 编码的,因此在解密之前必须对其进行 Base64 解码:

...
$chiave = generateSharedKey();                                          // Base64 encoded key
echo "Chiave segreta: ".$chiave."<br>";
$dati_criptati = encryptMessage($messaggio, base64_decode($chiave));    // Fix: Base64 decode key before encryption
...

无需修复 JavaScript 代码。

© www.soinside.com 2019 - 2024. All rights reserved.