encryption - .Net Core 和 WebCrypto 握手
问题描述
我想通过以下步骤初始化服务器(.NET Core 3.1)和客户端浏览器应用程序(使用 WebCryptoAPI 的 JS)之间的安全通道:
- 服务器将其公共 RSA 密钥发送
K
给客户端 - 客户端使用其公共 RSA 密钥加密
L
并将K
其发送到服务器 - 服务器解密获取的消息
L
,使用 AES 密钥加密L
并将其发送回客户端 - 客户端解密包含 AES 密钥的消息,然后他们就可以安全地交谈
到目前为止,我设法达到了第二步:
TypeScript 客户端请求服务器的公钥
const hubConnection = new SignalR.HubConnectionBuilder().withUrl("/hub/myhub").build();
Crypto.generateKey({name: "RSA-OAEP",
modulusLength: 2048,
publicExponent: new Uint8Array([1, 0, 1]),
hash: "SHA-512"},
true,
["encrypt", "decrypt"])
.then(key => {
clientRSA = key;
console.debug("Client RSA:", clientRSA);
return hubConnection.start();
})
.then(() => hubConnection.invoke("requestServerKey"));
C# 后端创建一个新的密钥和答案
public class MyHub : Hub<IMyHub>
{
public override async Task OnConnectedAsync()
{
using var rsa = RSA.Create(KeySize);
//store it to in-memory DB
}
}
public void RequestServerKey()
{
RSA rsa = DB.Get(Context.ConnectionId);
Clients.Caller.SetServerKey(Convert.ToBase64String(rsa.ExportSubjectPublicKeyInfo()));
}
Typescript 客户端接收公钥
hubConnection.on('setServerKey', (data: string) => {
const buffer = Uint8Array.from(atob(data), c => c.charCodeAt(0));
Crypto.importKey("spki", buffer, { name: "RSA-OAEP", hash: "SHA-512"}, false, ['encrypt'])
.then(rsaPublicKey => { //by some reason I can't pull this promise out to the zero level
serverKey = rsaPublicKey;
return Crypto.exportKey("spki", clientRSA.publicKey);
})
//THE FOLLOWING CALL FAILS
.then(clientPublicKey => Crypto.encrypt({name: "RSA-OAEP"}, serverKey, clientPublicKey))
.then(message => {
const str = btoa(String.fromCharCode(...new Uint8Array(message)));
hubConnection.invoke("requestSymmetricKey", str);
})
});
似乎服务器的公钥已成功导入,WebCrypto API
但是当我尝试使用它在将客户端的公钥发送到服务器之前对其进行加密时,它失败了。我也试过这个wrapKey
方法,但错误是一样的。我得到以下信息:
index.js:1 Uncaught Error: The error you provided does not contain a stack trace.
at L (index.js:1)
at Y (index.js:1)
at index.js:1
at index.js:1
at l (index.js:1)
home:1 Uncaught (in promise) DOMException
Promise.then (async)
...
所以我正在寻找任何暗示这里可能是什么问题。我尝试了初始化参数的各种组合,查看了所有中间值,但没有任何迹象表明可能出了什么问题。除了我犯了一些愚蠢的复制错误外,我的代码应该遵循以下示例:
- https://gist.github.com/QingpingMeng/f51902e2629fc061c6b9fc9bb0f3f57b
- https://github.com/diafygi/webcrypto-examples#rsa-oaep---encrypt
任何帮助表示赞赏!
解决方案
所以我在以下帖子的帮助下找到了解决方案:https ://crypto.stackexchange.com/questions/42097/what-is-the-maximum-size-of-the-plaintext-message-for-rsa-oaep
由于服务器的公钥是 2048 位并使用 SHA-512,因此最大消息长度仅为 126 字节。但是,我尝试传输的消息是 294 字节。遗憾的是 API 没有专门的错误消息,因为它可以很容易地自动检查。
在对我的问题的评论中进行了讨论之后,我将程序简化为:
- 服务器在其 API 中公开其公共 RSA 密钥并将其(或其哈希)发布到其他独立位置以进行验证
- 客户端生成对称 AES 密钥,通过服务器的公共 RSA 密钥对其进行加密并发送
- 服务器使用其私有 RSA 密钥解密对称 AES 密钥,然后他们就可以安全地交谈
现在我希望在 JS 和 C# 之间完成所有工作。
推荐阅读
- excel - 如何将代码应用于满足条件的多行?
- matlab - 如何评估多变量函数
- java - 如何将 sqlite json 列转换为 Spark 字符串或结构?
- php - 重写 url 变量包含 %2F 用于斜线转义时的 404 状态标头
- java - Google 表格 Java API 读取公式未计算值
- mysql - 使用 sql_mode=only_full_group_by 在 MySQL 中按错误分组
- asp.net-core - 从 C# 中的复杂模型中获取属性值
- excel - 创建循环以从三个工作表中复制整个列并将它们排序到新工作表中
- php - Symfony 4 Doctrine 无法连接到 Docker MySQL
- angular - 如何在嵌套的 FormControlGroup 中引用 FormControlName?