Web Crypto API 在 AES 解密时抛出“DOMException”
Web Crypto API throws `DOMException` on AES decryption
我想执行基本的 AES-CBC 解密。我有使用 128 位密钥 rawKey
加密的字符串 encData
,初始化向量 defaultIV
为零。我只想使用 Web Crypto API,没有第 3 方库。可以吗?
Web Crypto API 的 window.crypto.subtle.decrypt
在我使用它时抛出异常:Chromium 中的 DOMException
(没有更多信息)和 Firefox 中的 OperationError: The operation failed for an operation-specific reason
。
这是什么问题?
密钥和加密数据没问题,我在网上解密查过(使用控制台输出的十六进制字符串)。
代码:
!async function script() {
// ArrayBuffer to Hex String.
function buf2hex(buffer) {
return Array.prototype.map.call(new Uint8Array(buffer), x => ('00' + x.toString(16)).slice(-2)).join('');
}
const defaultIV = new Uint8Array(16);
const rawKey = new Uint8Array([42, 40, 254, 9, 99, 201, 174, 52, 226, 21, 90, 155, 81, 50, 2, 9]);
const encData = new Uint8Array([102, 80, 220, 73, 185, 233, 85, 7, 195, 196, 137, 107, 65, 150, 162, 161, 80, 82, 26, 18, 110, 247, 189, 176, 35, 197, 140, 4, 138, 75, 159, 197, 75, 88, 131, 23, 235, 125, 96, 81, 41, 170, 220, 45, 64, 55, 30, 68, 39, 6, 112, 194, 243, 209, 177, 173, 54, 71, 21, 172, 62, 147, 112, 76]);
console.log("defaultIV\n", defaultIV, buf2hex(defaultIV));
console.log("rawKey\n", rawKey, buf2hex(rawKey));
console.log("encData\n", encData, buf2hex(encData));
const key = await crypto.subtle.importKey(
"raw",
rawKey,
"AES-CBC",
true,
["decrypt"]
);
console.log("key", key);
// It throws "Uncaught (in promise) DOMException"
const decrypted = await crypto.subtle.decrypt(
{
name: "AES-CBC",
iv: defaultIV
},
key,
encData
);
console.log("decrypted", decrypted);
}();
看到这个 post,如果可能最好使用 AES-GCM:Webcrypto AES-CBC Decrypt: Operation Error - The operation failed for an operation-specific reason
您可以使用此测试套件测试您的浏览器版本支持的内容 - https://peculiarventures.github.io/pv-webcrypto-tests/
当然可以使用 WebCrypto 进行 AES-CBC:https://github.com/diafygi/webcrypto-examples#aes-cbc
填充 (PKCS #7)。这是问题所在。我从第 3 方服务获得的加密文本没有填充(在加密之前添加)。
Web Crypto API 不适用于没有填充的加密数据。
AES-JS 易于使用 API,但它仅适用于没有填充的文本(在我的情况下没问题),但是如果你用它来解密带有填充的密文,你需要手动将其从结果字符串中删除。
CryptoJS API 看起来很古老,但无论文本是否有填充,它在这两种情况下都能正常工作。
您需要将 ArrayBuffer 转换为其他内容:
let key = ab_to_hex(keyArrayBuffer);
let iv = ab_to_bin_str(ivArrayBuffer);
let encrypted = ab_to_bin_str(encryptedArrayBuffer);
function decrypt(key, iv, encrypted) {
const plaintextArray = CryptoJS.AES.decrypt(
{ ciphertext: CryptoJS.enc.Latin1.parse(encrypted) },
CryptoJS.enc.Hex.parse(key),
{ iv: CryptoJS.enc.Latin1.parse(iv) }
);
return CryptoJS.enc.Utf8.stringify(plaintextArray);
}
function ab_to_hex(buffer) {
return Array.from(new Uint8Array(buffer)).map(n => ("0" + n.toString(16)).slice(-2)).join("");
}
function ab_to_bin_str(buffer) {
return String.fromCharCode(...new Uint8Array(buffer));
}
我想执行基本的 AES-CBC 解密。我有使用 128 位密钥 rawKey
加密的字符串 encData
,初始化向量 defaultIV
为零。我只想使用 Web Crypto API,没有第 3 方库。可以吗?
window.crypto.subtle.decrypt
在我使用它时抛出异常:Chromium 中的 DOMException
(没有更多信息)和 Firefox 中的 OperationError: The operation failed for an operation-specific reason
。
这是什么问题?
密钥和加密数据没问题,我在网上解密查过(使用控制台输出的十六进制字符串)。
代码:
!async function script() {
// ArrayBuffer to Hex String.
function buf2hex(buffer) {
return Array.prototype.map.call(new Uint8Array(buffer), x => ('00' + x.toString(16)).slice(-2)).join('');
}
const defaultIV = new Uint8Array(16);
const rawKey = new Uint8Array([42, 40, 254, 9, 99, 201, 174, 52, 226, 21, 90, 155, 81, 50, 2, 9]);
const encData = new Uint8Array([102, 80, 220, 73, 185, 233, 85, 7, 195, 196, 137, 107, 65, 150, 162, 161, 80, 82, 26, 18, 110, 247, 189, 176, 35, 197, 140, 4, 138, 75, 159, 197, 75, 88, 131, 23, 235, 125, 96, 81, 41, 170, 220, 45, 64, 55, 30, 68, 39, 6, 112, 194, 243, 209, 177, 173, 54, 71, 21, 172, 62, 147, 112, 76]);
console.log("defaultIV\n", defaultIV, buf2hex(defaultIV));
console.log("rawKey\n", rawKey, buf2hex(rawKey));
console.log("encData\n", encData, buf2hex(encData));
const key = await crypto.subtle.importKey(
"raw",
rawKey,
"AES-CBC",
true,
["decrypt"]
);
console.log("key", key);
// It throws "Uncaught (in promise) DOMException"
const decrypted = await crypto.subtle.decrypt(
{
name: "AES-CBC",
iv: defaultIV
},
key,
encData
);
console.log("decrypted", decrypted);
}();
看到这个 post,如果可能最好使用 AES-GCM:Webcrypto AES-CBC Decrypt: Operation Error - The operation failed for an operation-specific reason
您可以使用此测试套件测试您的浏览器版本支持的内容 - https://peculiarventures.github.io/pv-webcrypto-tests/
当然可以使用 WebCrypto 进行 AES-CBC:https://github.com/diafygi/webcrypto-examples#aes-cbc
填充 (PKCS #7)。这是问题所在。我从第 3 方服务获得的加密文本没有填充(在加密之前添加)。
Web Crypto API 不适用于没有填充的加密数据。
AES-JS 易于使用 API,但它仅适用于没有填充的文本(在我的情况下没问题),但是如果你用它来解密带有填充的密文,你需要手动将其从结果字符串中删除。
CryptoJS API 看起来很古老,但无论文本是否有填充,它在这两种情况下都能正常工作。
您需要将 ArrayBuffer 转换为其他内容:
let key = ab_to_hex(keyArrayBuffer);
let iv = ab_to_bin_str(ivArrayBuffer);
let encrypted = ab_to_bin_str(encryptedArrayBuffer);
function decrypt(key, iv, encrypted) {
const plaintextArray = CryptoJS.AES.decrypt(
{ ciphertext: CryptoJS.enc.Latin1.parse(encrypted) },
CryptoJS.enc.Hex.parse(key),
{ iv: CryptoJS.enc.Latin1.parse(iv) }
);
return CryptoJS.enc.Utf8.stringify(plaintextArray);
}
function ab_to_hex(buffer) {
return Array.from(new Uint8Array(buffer)).map(n => ("0" + n.toString(16)).slice(-2)).join("");
}
function ab_to_bin_str(buffer) {
return String.fromCharCode(...new Uint8Array(buffer));
}