【发布时间】:2021-01-17 00:04:07
【问题描述】:
我们正在尝试使用 Eliptical Curve Diffie Hellman 让基于浏览器的应用程序使用 JS 与 .Net Core 5.0 服务器交换密钥。 我们的应用程序需要在两端共享一个秘密,以进行我们所做的某些特定处理(不是加密,而是我们自己的过程),我们希望派生该秘密 而不是出于安全目的而传输。 我们已经四处寻找合适的解决方案并将各种解决方案修补在一起,我们想出了这个,但它失败了,在 C# 端有一个例外。 基本上,我们的伪代码如下(目前只到了第3步):
- 客户端 (Bob) 使用 window.crypto.subtle 库在客户端中生成 ECDH / P-256 密钥对。
- Bob 从此对导出公钥并向服务器 (Alice) 发出 GET 以获取她的公钥(将 bobPublicKeyB64 作为查询 arg 传递)。
- Alice 接受传入请求并调用 C# 方法以使用 Bob 的公钥创建共享密钥。
- Alice 将此共享密钥存储在内存缓存中,并将她的公钥返回给 Bob。
- Bob 然后使用 Alice 的公钥获取他自己的共享密钥并将其存储在浏览器的“会话存储”中。
这里没有显示将 Bob 的公钥发送给 Alice 的代码,它是一个标准的 XMLHttpRequest。但是,它确实有效,并且服务器被调用,正如 C# 代码中的故障所见证的那样。我们已经验证了 bobPublicKeyB64 在 Bob 发送它时与 Alice 得到它时是完全相同的。
一旦这个方法奏效,我们将加强两端的存储方法,但首先我们需要让交换正常工作。 Alice (C#) 代码块中的 cmets 显示了它失败的地方。 getDerivedKey 方法(现在已注释掉)来自这篇文章 - ECDH nodejs and C# key exchange 并且它失败了一个不同的异常(我确信这两个失败都是由于 JS 库和 .Net 实现之间的一些不匹配,但我们无法处理它)。 非常感谢任何和所有帮助 - 基本问题是“当 Bob 是 JS 而 Alice 是 C# 时,我们如何才能让 Bob 和 Alice 说话?”
以下是 Bob 上的 JS 代码:
async function setUpDFHKeys() {
let bobPublicKeyB64;
let bobPrivateKeyB64;
try {
const bobKey = await window.crypto.subtle.generateKey(
{ name: 'ECDH', namedCurve: 'P-256' },
true,
["deriveKey"]
);
const publicKeyData = await window.crypto.subtle.exportKey("raw", bobKey.publicKey);
const publicKeyBytes = new Uint8Array(publicKeyData);
const publicKeyB64 = btoa(publicKeyBytes);
bobPublicKeyB64 = publicKeyB64;
const privateKeyData = await window.crypto.subtle.exportKey("pkcs8", bobKey.privateKey);
const privateKeyBytes = new Uint8Array(privateKeyData);
const privateKeyB64 = btoa(String.fromCharCode.apply(null, privateKeyBytes));
bobPrivateKeyB64 = privateKeyB64;
}
catch (error) {
console.log("Could not setup DFH Keys " + error);
}};
以下是 Alice 上的 C# 代码:
private string WorkWithJSPublicKey(string bobPublicKeyB64, out string alicePublicKey)
{
try
{
//
// Alice is this server.
// Bob is a browser that uses the window.crypto.subtle.generateKey with 'ECDH' and 'P-256' as parameters
// and window.crypto.subtle.exportKey of the key.publicKey in "raw" format (this is then converted to a B64 string using btoa)
//
using (ECDiffieHellman alice = ECDiffieHellman.Create(ECCurve.NamedCurves.nistP256))
{
//
// Get the public key info from Bob and convert to B64 string to return to Alice
//
Span<byte> exported = new byte[alice.KeySize];
int len = 0;
alice.TryExportSubjectPublicKeyInfo(exported, out len);
alicePublicKey = Convert.ToBase64String(exported.Slice(0, len));
//
// Get Alice's private key to use to generate a shared secret
//
byte[] alicePrivateKey = alice.ExportECPrivateKey();
//
// Import Bob's public key after converting it to bytes
//
var bobPubKeyBytes = Convert.FromBase64String(bobPublicKeyB64);
//
// TRY THIS... (Bombs with "The specified curve 'nistP256' or its parameters are not valid for this platform").
//
// getDerivedKey(bobPubKeyBytes, alice);
//
// This throws exception ("The provided data is tagged with 'Universal' class value '20', but it should have been 'Universal' class value '16'.")
//
alice.ImportSubjectPublicKeyInfo(bobPubKeyBytes, out len);
//
// Once Alice knows about Bob, create a shared secret and return it.
//
byte[] sharedSecret = alice.DeriveKeyMaterial(alice.PublicKey);
return Convert.ToBase64String(sharedSecret);
}
}
catch (Exception ex)
{
_logger.LogError(ex, ex.Message);
alicePublicKey = string.Empty;
return string.Empty;
}
}
getDerivedKey(借用自ECDH nodejs and C# key exchange)的代码如下所示:
static byte[] getDerivedKey(byte[] key1, ECDiffieHellman alice)
{
byte[] keyX = new byte[key1.Length / 2];
byte[] keyY = new byte[keyX.Length];
Buffer.BlockCopy(key1, 1, keyX, 0, keyX.Length);
Buffer.BlockCopy(key1, 1 + keyX.Length, keyY, 0, keyY.Length);
ECParameters parameters = new ECParameters
{
Curve = ECCurve.NamedCurves.nistP256,
Q = {
X = keyX,
Y = keyY,
},
};
byte[] derivedKey;
using (ECDiffieHellman bob = ECDiffieHellman.Create(parameters))
using (ECDiffieHellmanPublicKey bobPublic = bob.PublicKey)
{
return derivedKey = alice.DeriveKeyFromHash(bobPublic, HashAlgorithmName.SHA256);
}
}
【问题讨论】:
-
只是“.NET 5.0”,而不是“.NET Core 5.0”
标签: javascript c# diffie-hellman