如何使用 AES-GCM 对 C#.NET 加密()然后 JS WebCryptoApi 解密()?
Posted
技术标签:
【中文标题】如何使用 AES-GCM 对 C#.NET 加密()然后 JS WebCryptoApi 解密()?【英文标题】:How to C#.NET encrypt() then JS WebCryptoApi decrypt() using AES-GCM? 【发布时间】:2017-04-07 14:05:22 【问题描述】:我想使用 C# 加密数据并使用 JS 解密它。
此表表明 AES-GCM 是使用 WebCryptoApi https://diafygi.github.io/webcrypto-examples/ 的方法。
我已成功使用 BouncyCastle https://codereview.stackexchange.com/questions/14892/simplified-secure-encryption-of-a-string 在 .NET 中加密(和解密)。
var message = "This is the test message";
var key = AESGCM.NewKey();
Console.Out.WriteLine("KEY:" + Convert.ToBase64String(key));
>> KEY:5tgX6AOHot1T9SrImyILIendQXwfdjfOSRAVfMs0ed4=
string encrypted = AESGCM.SimpleEncrypt(message, key);
Console.Out.WriteLine("ENCRYPTED:" + encrypted);
>> ENCRYPTED:Ct0/VbOVsyp/LMxaaFqKKw91+ts+8uzDdHLrTG1XVjPNL7KiBGYB4kfdNGl+xj4fYqdb4JXgdTk=
var decrypted = AESGCM.SimpleDecrypt(encrypted, key);
Console.Out.WriteLine("DECRYPTED:" + decrypted);
>> DECRYPTED:This is the test message
但是,我不知道如何解密这个客户端。在https://github.com/diafygi/webcrypto-examples#aes-cbc---decrypt 上有很多 WebCryptoApi 示例,包括 AES-GCM。
第一步(似乎可行)是导入密钥,我将其作为 base-64 编码字符串:
var keyString = "+6yDdIiJJl8Lqt60VOHuP25p4yNxz0CRMoE/WKA+Mqo=";
function _base64ToArrayBuffer(base64)
var binary_string = window.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;
var key = _base64ToArrayBuffer(keyString )
var cryptoKey; // we'll get this out in the promise below
window.crypto.subtle.importKey(
"raw",
key,
//this is the algorithm options
name: "AES-GCM",
,
true, // whether the key is extractable
["encrypt", "decrypt"] // usages
)
.then(function(key)
//returns the symmetric key
console.log(key);
cryptoKey = key;
)
.catch(function(err)
console.error(err);
);
最后一步应该是解密编码的消息,这也是一个base-64编码的字符串
var encryptedString = "adHb4UhM93uWyRIV6L1SrYFbxEpIbj3sQW8VwJDP7v+XoxGi6fjmucEEItP1kQWxisZp3qhoAhQ=";
var encryptedArrayBuffer = _base64ToArrayBuffer(encryptedString)
window.crypto.subtle.decrypt(
name: "AES-GCM",
iv: new ArrayBuffer(12), //The initialization vector you used to encrypt
//additionalData: ArrayBuffer, //The addtionalData you used to encrypt (if any)
// tagLength: 128, //The tagLength you used to encrypt (if any)
,
cryptoKey, //from above
encryptedArrayBuffer //ArrayBuffer of the data
)
.then(function(decrypted)
//returns an ArrayBuffer containing the decrypted data
console.log(new Uint8Array(decrypted));
)
.catch(function(err)
debugger; console.error(err);
);
不幸的是,这引发了 DomError。
我不知道在解密方法中我应该为“iv”使用什么。我试过空,ArrayBuffer(0),ArrayBuffer(12)。这几乎是我的理解结束的地方。
【问题讨论】:
如果您只使用对称加密,您需要在服务器和客户端使用完全相同的密钥。如果您将加密密钥从服务器发送到客户端或其他方式,您需要加密您的对称加密密钥。最简单的方法是使用 TLS。如果你使用 TLS,那么数据和密钥都是加密的,所以你不需要自己加密。这不提供任何安全性,只是有点混淆。你应该阅读:nccgroup.trust/us/about-us/newsroom-and-events/blog/2011/august/… 是的,我们只是试图混淆:所谓的“DRM”。这很荒谬。 “策略”就是混淆js内部的解密,让用户“难以”查看流量。然后我们在浏览器中一目了然地向他们展示解密的结果。当然,任何能走到这一步的人都可以找到十几种其他方法来获取内容。我们将其与其他策略相结合,构建了一个美丽的多层荒谬蛋糕。我还是想解决这个问题。 【参考方案1】:如果您查看AESGCM
的实现,您应该会看到nonce(称为IV)是密文的一部分。它的大小设置为 16 字节 (NonceBitSize = 128
)。您需要在 javascript 中从密文的开头读取那么多字节,并将剩余的字节用作要解密的实际密文。
GCM 仅针对 96 位随机数定义,因此您可能需要将其更改为 NonceBitSize = 96
并读取前 12 个字节。
基于this answer,您需要将密文的最后 16 个字节 (MacBitSize = 128
) 分割为身份验证标签。
96 位随机数示例:
window.crypto.subtle.decrypt(
name: "AES-GCM",
iv: encryptedArrayBuffer.slice(0, 12), //The initialization vector you used to encrypt
//additionalData: ArrayBuffer, //The addtionalData you used to encrypt (if any)
// tagLength: 128, //The tagLength you used to encrypt (if any)
tag: encryptedArrayBuffer.slice(-16), // authentication tag
,
cryptoKey, //from above
encryptedArrayBuffer.slice(12, -16) //ArrayBuffer of the data
// alternatively: encryptedArrayBuffer.slice(12) // in some cases leave the authentication tag in place
)
【讨论】:
Artjom 的输入看起来很有希望,但我仍然收到 DOMException 客户端,但错误中没有详细信息。我正在使用的 BouncyCastle 代码 codereview.stackexchange.com/questions/14892/…> 使用的是 128 位随机数。我尝试将其更改为 96,结果相同。会继续努力。 结果是 data 参数的输入值应该包括最后 16 位:encryptedArrayBuffer.slice(12)。我会编辑答案以上是关于如何使用 AES-GCM 对 C#.NET 加密()然后 JS WebCryptoApi 解密()?的主要内容,如果未能解决你的问题,请参考以下文章