【发布时间】:2022-12-03 12:51:06
【问题描述】:
我想用window.crypto.subtle加密,用C#解密。
js 中的 crypt / decrypt 正在运行。
在 C# 中,计算的身份验证标记与输入不匹配。
我不知道我是否可以将任何 12 个字节作为盐,也不知道我是否需要导出密码。
export async function deriveKey(password, salt) {
const buffer = utf8Encoder.encode(password);
const key = await crypto.subtle.importKey(
'raw',
buffer,
{ name: 'PBKDF2' },
false,
['deriveKey'],
);
const privateKey = crypto.subtle.deriveKey(
{
name: 'PBKDF2',
hash: { name: 'SHA-256' },
iterations,
salt,
},
key,
{
name: 'AES-GCM',
length: 256,
},
false,
['encrypt', 'decrypt'],
);
return privateKey;
}
const buff_to_base64 = (buff) => btoa(String.fromCharCode.apply(null, buff));
const base64_to_buf = (b64) => Uint8Array.from(atob(b64), (c) => c.charCodeAt(null));
export async function encrypt(key, data) {
const salt = crypto.getRandomValues(new Uint8Array(12));
const iv = crypto.getRandomValues(new Uint8Array(12));
console.log('encrypt');
console.log('iv', iv);
console.log('salt', salt);
const buffer = new TextEncoder().encode(data);
const privatekey = await deriveKey(key, salt);
const encrypted = await crypto.subtle.encrypt(
{
name: 'AES-GCM',
iv,
tagLength: 128,
},
privatekey,
buffer,
);
const bytes = new Uint8Array(encrypted);
console.log('concat');
const buff = new Uint8Array(iv.byteLength + encrypted.byteLength + salt.byteLength);
buff.set(iv, 0);
buff.set(salt, iv.byteLength);
buff.set(bytes, iv.byteLength + salt.byteLength);
console.log('iv', iv);
console.log('salt', salt);
console.log('buff', buff);
const base64Buff = buff_to_base64(buff);
console.log(base64Buff);
return base64Buff;
}
export async function decrypt(key, data) {
console.log('decryption');
console.log('buff', base64_to_buf(data));
const d = base64_to_buf(data);
const iv = d.slice(0, 12);
const salt = d.slice(12, 24);
const ec = d.slice(24);
console.log('iv', iv);
console.log('salt', salt);
console.log(ec);
const decrypted = await window.crypto.subtle.decrypt(
{
name: 'AES-GCM',
iv,
tagLength: 128,
},
await deriveKey(key, salt),
ec,
);
return new TextDecoder().decode(new Uint8Array(decrypted));
}
Span<byte> encryptedData = Convert.FromBase64String(enc).AsSpan();
Span<byte> nonce = encryptedData[..12];
Span<byte> salt = encryptedData.Slice(12, 12);
Span<byte> data = encryptedData.Slice(12 + 12, encryptedData.Length - 16 - 12 - 12);
Span<byte> tag = encryptedData[^16..];
Span<byte> result = new byte[data.Length];
using Rfc2898DeriveBytes pbkdf2 = new(Encoding.UTF8.GetBytes(password), salt.ToArray(), 1000, HashAlgorithmName.SHA256);
using AesGcm aes = new(pbkdf2.GetBytes(16));
aes.Decrypt(nonce, data, tag, result);
【问题讨论】:
-
关于 PBKDF2 的迭代似乎有所不同。此外,在 C# 代码中,数据确定不正确(
Slice()的第一个参数应该是12而不是11,或者使用范围运算符[12..^16]而不是Slice(),至于nonce和 @ 987654331@)。 -
以为这是一个索引..谢谢!
-
此外,密钥大小似乎不同,盐应该与密文连接在一起,如 IV(也应该是随机的)。
-
const salt= crypto.getRandomValues(new Uint8Array(12));不见了。我会尝试传递盐。
-
两种代码都派生出不同长度的密钥,因此应用不同的 AES 变体,因此不兼容。我已经在我的回答中更详细地描述了这一点。
标签: c# cryptography aes-gcm subtlecrypto window.crypto