我正在使用 WebCrypto,但输出令人困惑。
以下测试用例使用新生成的 128 位密钥和 128 位随机 IV 加密随机 16 字节(128 位)纯文本,但输出 32 字节(256 位)输出。
如果我记得 AES-CBC 的细节,它应该输出 128 位块。
function test() {
var data = new Uint8Array(16);
window.crypto.getRandomValues(data);
console.log(data)
window.crypto.subtle.generateKey(
{
name: "AES-CBC",
length: 128,
},
false,
["encrypt", "decrypt"]
)
.then(function(key){
//returns a key object
console.log(key);
window.crypto.subtle.encrypt(
{
name: "AES-CBC",
iv: window.crypto.getRandomValues(new Uint8Array(16)),
},
key,
data
)
.then(function(encrypted){
console.log(new Uint8Array(encrypted));
})
.catch(function(err){
console.error(err);
});
})
.catch(function(err){
console.error(err);
});
}
Run Code Online (Sandbox Code Playgroud)
示例输出:
Uint8Array(16) [146, 207, 22, 56, 56, 151, 125, 174, 137, 69, 133, 36, 218, 114, 143, 174]
CryptoKey {
algorithm: {name: "AES-CBC", length: 128}
extractable: false
type: "secret"
usages: (2) ["encrypt", "decrypt"]
__proto__: CryptoKey
Uint8Array(32) [81, 218, 52, 158, 115, 105, 57, 230, 45, 253, 153, 54, 183, 19, 137, 240, 183, 229, 241, 75, 182, 19, 237, 8, 238, 5, 108, 107, 123, 84, 230, 209]
Run Code Online (Sandbox Code Playgroud)
知道我做错了什么。
(如果更合适,可以转到crypto.stackexchange.com)
我目前正在 MacOS 上的 Chrome 71 上进行测试。
是的。额外的 16 个字节是填充。即使消息文本是块大小的倍数,也会添加填充,否则解密逻辑不知道何时寻找填充。
该网络加密API规范说:
在 CBC 模式下运行时,可以在各种填充方案下填充不是 AES 块大小(16 字节)的精确倍数的消息。在 Web Crypto API 中,唯一支持的填充模式是 PKCS#7,如 [RFC2315] 的第 10.3 节第 2 步所述。
这意味着与其他语言实现(如 Java)不同,您可以指定NoPadding何时知道输入消息文本总是块大小的倍数(AES 为 128 位),Web Cryptography API 强制您使用 PKCS#7 填充.
如果我们查看RFC2315:
一些内容加密算法假设输入长度是 k 个八位字节的倍数,其中 k > 1,并让应用程序定义一种处理长度不是 k 个八位字节倍数的输入的方法。对于此类算法,该方法应在尾端用 k - (l mod k) 个八位字节填充输入,所有八位字节都具有值 k - (l mod k),其中 l 是输入的长度。换句话说,输入在尾端用以下字符串之一填充:
Run Code Online (Sandbox Code Playgroud)01 -- if l mod k = k-1 02 02 -- if l mod k = k-2 . . . k k ... k k -- if l mod k = 0填充可以明确删除,因为所有输入都被填充并且没有填充字符串是另一个的后缀。当且仅当 k < 256 时,这种填充方法是明确定义的;更大 k 的方法是一个有待进一步研究的开放问题。
笔记: k k ... k k -- if l mod k = 0
如果参考subtle.encrypt签名,则无法指定填充模式。这意味着,解密逻辑总是需要填充。
但是,就您而言,如果您仅将 Web Cryptography API 用于加密,而您的 Python 应用程序(带有NoPadding)仅用于解密,我认为您可以在将密文提供给 Python 应用程序之前简单地从密文中去除最后 16 个字节。这是仅用于演示目的的代码示例:
function test() {
let plaintext = 'GoodWorkGoodWork';
let encoder = new TextEncoder('utf8');
let dataBytes = encoder.encode(plaintext);
window.crypto.subtle.generateKey(
{
name: "AES-CBC",
length: 128,
},
true,
["encrypt", "decrypt"]
)
.then(function(key){
crypto.subtle.exportKey('raw', key)
.then(function(expKey) {
console.log('Key = ' + btoa(String.
fromCharCode(...new Uint8Array(expKey))));
});
let iv = new Uint8Array(16);
window.crypto.getRandomValues(iv);
let ivb64 = btoa(String.fromCharCode(...new Uint8Array(iv)));
console.log('IV = ' + ivb64);
window.crypto.subtle.encrypt(
{
name: "AES-CBC",
iv: iv,
},
key,
dataBytes
)
.then(function(encrypted){
console.log('Cipher text = ' +
btoa(String.fromCharCode(...new Uint8Array(encrypted))));
})
.catch(function(err){
console.error(err);
});
})
.catch(function(err){
console.error(err);
});
}
Run Code Online (Sandbox Code Playgroud)
上面的输出是:
IV = qW2lanfRo2H/3aSLzxIecA==
Key = 0LDBq5iz243HBTUE/lrM+A==
Cipher text = Wa4nIF0tt4PEBUChiH1KCkSOg6L2daoYdboEEf+Oh6U=
Run Code Online (Sandbox Code Playgroud)
现在,我使用将这些作为输入,去除密文的最后 16 个字节,并在使用以下 Java 代码解密后仍然获得相同的消息文本:
package com.sapbasu.javastudy;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class EncryptCBC {
public static void main(String[] arg) throws Exception {
SecretKey key = new SecretKeySpec(Base64.getDecoder().decode(
"0LDBq5iz243HBTUE/lrM+A=="),
"AES");
IvParameterSpec ivSpec = new IvParameterSpec(Base64.getDecoder().decode(
"qW2lanfRo2H/3aSLzxIecA=="));
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
byte[] cipherTextWoPadding = new byte[16];
System.arraycopy(Base64.getDecoder().decode(
"Wa4nIF0tt4PEBUChiH1KCkSOg6L2daoYdboEEf+Oh6U="),
0, cipherTextWoPadding, 0, 16);
byte[] decryptedMessage = cipher.doFinal(cipherTextWoPadding);
System.out.println(new String(decryptedMessage, StandardCharsets.UTF_8));
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
889 次 |
| 最近记录: |