无法解密 openSSL AES CTR 加密文本
Trouble decrypting openSSL AES CTR encrypted text
我在使用 openssl_encrypt
方法解密在 php 中加密的消息时遇到问题。我正在使用新的 WebCrypto API(所以我使用 crypto.subtle
)。
正在 php 中加密:
$ALGO = "aes-256-ctr";
$key = "ae6865183f6f50deb68c3e8eafbede0b33f9e02961770ea5064f209f3bf156b4";
function encrypt ($data, $key) {
global $ALGO;
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($ALGO), $strong);
if (!$strong) {
exit("can't generate strong IV");
}
return bin2hex($iv).openssl_encrypt($data, $ALGO, $key, 0, $iv);
}
$enc = encrypt("Lorem ipsum dolor", $key);
exit($enc);
示例输出:
8d8c3a57d2dbb3287aca61be0bce59fbeAQ4ILKouAQ5eizPtlUTeHU=
(我可以在 php 中解密并取回明文)
在JS中我是这样解密的:
function Ui8FromStr (StrStart) {
const Ui8Result = new Uint8Array(StrStart.length);
for (let i = 0; i < StrStart.length; i++) {
Ui8Result[i] = StrStart.charCodeAt(i);
}
return Ui8Result;
}
function StrFromUi8 (Ui8Start) {
let StrResult = "";
Ui8Start.forEach((charcode) => {
StrResult += String.fromCharCode(charcode);
});
return StrResult;
}
function Ui8FromHex (hex) {
for (var bytes = new Uint8Array(Math.ceil(hex.length / 2)), c = 0; c < hex.length; c += 2)
bytes[c/2] = parseInt(hex.substr(c, 2), 16);
return bytes;
}
const ALGO = 'AES-CTR'
function decrypt (CompCipher, HexKey) {
return new Promise (function (resolve, reject) {
// remove IV from cipher
let HexIv = CompCipher.substr(0, 32);
let B64cipher = CompCipher.substr(32);
let Ui8Cipher = Ui8FromStr(atob(B64cipher));
let Ui8Iv = Ui8FromHex (HexIv);
let Ui8Key = Ui8FromHex (HexKey);
crypto.subtle.importKey("raw", Ui8Key, {name: ALGO}, false, ["encrypt", "decrypt"]). then (function (cryptokey){
return crypto.subtle.decrypt({ name: ALGO, counter: Ui8Iv, length: 128}, cryptokey, Ui8Cipher).then(function(result){
let Ui8Result = new Uint8Array(result);
let StrResult = StrFromUi8(Ui8Result);
resolve(StrResult);
}).catch (function (err){
reject(err)
});
})
})
}
当我现在 运行 decrypt("8d8c3a57d2dbb3287aca61be0bce59fbeAQ4ILKouAQ5eizPtlUTeHU=", "ae6865183f6f50deb68c3e8eafbede0b33f9e02961770ea5064f209f3bf156b4").then(console.log)
我得到了乱码: SÌõÅ°blfçSÑ-
我的问题是,我不确定 counter
是什么意思。我尝试了 IV 但失败了。
This Github tutorial 建议*1,它是 IV - 或者至少是它的一部分,因为我看到人们谈论计数器是一部分IV 的(大约 4 个字节,这意味着 IV 由 12 个字节的 IV 和 4 个字节的计数器组成)
如果那确实是真的,那么我的问题就变成了:当计数器只有 4 个字节时,我在哪里给脚本其他 12 个字节的 IV。
任何人都可以在 php
中给我一个有效的加密示例吗
*1 它表示必须使用相同的计数器进行加密和解密。这让我相信,它至少类似于 IV
您在 PHP 中错误地处理了密钥。
在 PHP 代码中,您将十六进制编码的密钥直接传递给 openssl_encrypt
函数,而不对其进行解码。这意味着您尝试使用的密钥是预期长度的两倍(即 64 字节)。然而,OpenSSL 不检查密钥长度——它只是截断它,取前 32 个字节并将它们用作加密密钥。
Javascript 代码正确处理密钥,在将解码数组传递给解密函数之前对其进行十六进制解码。
总体结果是您在每种情况下都使用了不同的密钥,因此解密无效。
您需要在 PHP 代码中的键上添加对 hex2bin
的调用,以将其从十六进制编码转换为实际的 32 个原始字节。
我在使用 openssl_encrypt
方法解密在 php 中加密的消息时遇到问题。我正在使用新的 WebCrypto API(所以我使用 crypto.subtle
)。
正在 php 中加密:
$ALGO = "aes-256-ctr";
$key = "ae6865183f6f50deb68c3e8eafbede0b33f9e02961770ea5064f209f3bf156b4";
function encrypt ($data, $key) {
global $ALGO;
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($ALGO), $strong);
if (!$strong) {
exit("can't generate strong IV");
}
return bin2hex($iv).openssl_encrypt($data, $ALGO, $key, 0, $iv);
}
$enc = encrypt("Lorem ipsum dolor", $key);
exit($enc);
示例输出:
8d8c3a57d2dbb3287aca61be0bce59fbeAQ4ILKouAQ5eizPtlUTeHU=
(我可以在 php 中解密并取回明文)
在JS中我是这样解密的:
function Ui8FromStr (StrStart) {
const Ui8Result = new Uint8Array(StrStart.length);
for (let i = 0; i < StrStart.length; i++) {
Ui8Result[i] = StrStart.charCodeAt(i);
}
return Ui8Result;
}
function StrFromUi8 (Ui8Start) {
let StrResult = "";
Ui8Start.forEach((charcode) => {
StrResult += String.fromCharCode(charcode);
});
return StrResult;
}
function Ui8FromHex (hex) {
for (var bytes = new Uint8Array(Math.ceil(hex.length / 2)), c = 0; c < hex.length; c += 2)
bytes[c/2] = parseInt(hex.substr(c, 2), 16);
return bytes;
}
const ALGO = 'AES-CTR'
function decrypt (CompCipher, HexKey) {
return new Promise (function (resolve, reject) {
// remove IV from cipher
let HexIv = CompCipher.substr(0, 32);
let B64cipher = CompCipher.substr(32);
let Ui8Cipher = Ui8FromStr(atob(B64cipher));
let Ui8Iv = Ui8FromHex (HexIv);
let Ui8Key = Ui8FromHex (HexKey);
crypto.subtle.importKey("raw", Ui8Key, {name: ALGO}, false, ["encrypt", "decrypt"]). then (function (cryptokey){
return crypto.subtle.decrypt({ name: ALGO, counter: Ui8Iv, length: 128}, cryptokey, Ui8Cipher).then(function(result){
let Ui8Result = new Uint8Array(result);
let StrResult = StrFromUi8(Ui8Result);
resolve(StrResult);
}).catch (function (err){
reject(err)
});
})
})
}
当我现在 运行 decrypt("8d8c3a57d2dbb3287aca61be0bce59fbeAQ4ILKouAQ5eizPtlUTeHU=", "ae6865183f6f50deb68c3e8eafbede0b33f9e02961770ea5064f209f3bf156b4").then(console.log)
我得到了乱码: SÌõÅ°blfçSÑ-
我的问题是,我不确定 counter
是什么意思。我尝试了 IV 但失败了。
This Github tutorial 建议*1,它是 IV - 或者至少是它的一部分,因为我看到人们谈论计数器是一部分IV 的(大约 4 个字节,这意味着 IV 由 12 个字节的 IV 和 4 个字节的计数器组成)
如果那确实是真的,那么我的问题就变成了:当计数器只有 4 个字节时,我在哪里给脚本其他 12 个字节的 IV。
任何人都可以在 php
中给我一个有效的加密示例吗*1 它表示必须使用相同的计数器进行加密和解密。这让我相信,它至少类似于 IV
您在 PHP 中错误地处理了密钥。
在 PHP 代码中,您将十六进制编码的密钥直接传递给 openssl_encrypt
函数,而不对其进行解码。这意味着您尝试使用的密钥是预期长度的两倍(即 64 字节)。然而,OpenSSL 不检查密钥长度——它只是截断它,取前 32 个字节并将它们用作加密密钥。
Javascript 代码正确处理密钥,在将解码数组传递给解密函数之前对其进行十六进制解码。
总体结果是您在每种情况下都使用了不同的密钥,因此解密无效。
您需要在 PHP 代码中的键上添加对 hex2bin
的调用,以将其从十六进制编码转换为实际的 32 个原始字节。