gpt4 book ai didi

javascript - .NET Core 5.0 到 Javascript DFH key 交换不起作用

转载 作者:行者123 更新时间:2023-12-05 03:45:34 28 4
gpt4 key购买 nike

我们正在尝试使用 JS 获取基于浏览器的应用程序,以使用椭圆曲线 Diffie Hellman 与 .Net Core 5.0 服务器交换 key 。我们的应用程序需要在两端共享 secret 以进行我们进行的某些特定处理(不是加密,而是我们自己的过程),并且我们希望导出该 secret 而不是出于安全目的传输。我们四处寻找合适的解决方案并将各种解决方案拼凑在一起,我们想出了这个,但它失败了,C# 端出现异常。基本上,我们的伪代码如下(到目前为止我们只进行了第 3 步):

  1. 客户端 (Bob) 使用 window.crypto.subtle 库在客户端中生成 ECDH/P-256 key 对。
  2. Bob 从这对导出公钥并向服务器 (Alice) 发出 GET 以获取她的公钥(将 bobPublicKeyB64 作为查询参数传递)。
  3. Alice 接收传入请求并调用 C# 方法以使用 Bob 的公钥创建共享 key 。
  4. Alice 将此共享 secret 存储在内存缓存中,并将她的公钥返回给 Bob。
  5. 然后 Bob 使用 Alice 的公钥获取他自己的共享 secret 并将其存储在浏览器的“ session 存储”中。

将 Bob 的公钥发送给 Alice 的代码在这里没有显示,它是一个标准的 XMLHttpRequest。然而,它确实有效,并且服务器被调用,正如 C# 代码中的失败所证明的那样。我们已经验证 bobPublicKeyB64 在 Bob 发送时和 Alice 收到时完全相同。

一旦成功,我们将加强两端的存储方法,但首先我们需要让交换正常工作。Alice (C#) 代码块中的注释显示了它失败的地方。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);
}
}

最佳答案

如果 Web Crypto 端的公钥以 X.509/SPKI 格式而不是原始 key 导出,则解决方案会更简单,因为 .NET 5 有专用的导入方法,ImportSubjectPublicKeyInfo() , 对于这种格式。此外,这与 C# 代码一致,其中公钥也以 X.509/SPKI 格式导出。在以下示例中,Web Crypto 代码以 X.509/SPKI 格式导出公钥。

第 1 步 - Web 加密端 (Bob):生成 EC key 对

以下 Web Crypto 代码创建一个 ECDH key 对并以 X.509/SPKI 格式导出公钥(以及以 PKCS8 格式导出私钥)。请注意,由于错误,以 PKCS8 格式导出在 Firefox 下不起作用,另请参阅 here :

setUpDFHKeys().then(() => {});

async function setUpDFHKeys() {
var bobKey = await window.crypto.subtle.generateKey(
{ name: 'ECDH', namedCurve: 'P-256' },
true,
["deriveKey"]
);
var publicKeyData = await window.crypto.subtle.exportKey("spki", bobKey.publicKey);
var publicKeyBytes = new Uint8Array(publicKeyData);
var publicKeyB64 = btoa(String.fromCharCode.apply(null, publicKeyBytes));
console.log("Bob's public: \n" + publicKeyB64.replace(/(.{56})/g,'$1\n'));
var privateKeyData = await window.crypto.subtle.exportKey("pkcs8", bobKey.privateKey);
var privateKeyBytes = new Uint8Array(privateKeyData);
var privateKeyB64 = btoa(String.fromCharCode.apply(null, privateKeyBytes));
console.log("Bob's private:\n" + privateKeyB64.replace(/(.{56})/g,'$1\n'));
};

一个可能的输出是下面的 key 对,在后续类(class)中作为Web Crypto端的 key 对使用:

Bob's public:  MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2X9cW7P2g4Db3BEgfjs8lwpgukPY4Qg3mcLwVJW+WwA7lbiz+N1MIL3y+JumBF1qIdyx24r5+Sr4c4iYsTWh2w== 
Bob's private: MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg72fE/+7WX5aKAMiy8kTkCTVeGR8oOlKuoQ8iXTQWmxGhRANCAATZf1xbs/aDgNvcESB+OzyXCmC6Q9jhCDeZwvBUlb5bADuVuLP43UwgvfL4m6YEXWoh3LHbivn5KvhziJixNaHb

第 2 步 - C# 端 (Alice):从 Web Crypto 端导入公钥并生成共享 key

以下 C# 代码生成 ECDH key 对,导出 X.509/SPKI 格式的公钥(和 PKCS8 格式的私钥)并确定共享 key 。为获取共享 key ,使用 ImportSubjectPublicKeyInfo() 导入 X.509/SPKI 格式的 Web Crypto 端公钥。

string alicePublicKey;
string bobPublicKeyB64 = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2X9cW7P2g4Db3BEgfjs8lwpgukPY4Qg3mcLwVJW+WwA7lbiz+N1MIL3y+JumBF1qIdyx24r5+Sr4c4iYsTWh2w==";
string sharedSecret = WorkWithJSPublicKey(bobPublicKeyB64, out alicePublicKey);
Console.WriteLine("Alice's shared secret: " + sharedSecret);

private static string WorkWithJSPublicKey(string bobPublicKeyB64, out string alicePublicKey)
{
alicePublicKey = null;
using (ECDiffieHellman alice = ECDiffieHellman.Create(ECCurve.NamedCurves.nistP256))
{
alicePublicKey = Convert.ToBase64String(alice.ExportSubjectPublicKeyInfo());
Console.WriteLine("Alice's public: " + alicePublicKey);
Console.WriteLine("Alice's private: " + Convert.ToBase64String(alice.ExportPkcs8PrivateKey()));
ECDiffieHellman bob = ECDiffieHellman.Create();
bob.ImportSubjectPublicKeyInfo(Convert.FromBase64String(bobPublicKeyB64), out _);
byte[] sharedSecret = alice.DeriveKeyMaterial(bob.PublicKey);
return Convert.ToBase64String(sharedSecret);
}
}

可能的输出是以下 key 对和共享 secret 。该 key 对作为后续类(class)中C#端的 key 对使用:

Alice's public:        MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEJVUW57L2QeswZhnIp5gjMSiHhqyOVTsPUq2QwHv+R4jQetMQ8JDT+3VQyP/dPpskUhzDd3lKxdRBaiZrWby+VQ==
Alice's private: MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgbho81UNFdNwULs7IoWk1wSy2PP9soSlt4/bveAtoPBOhRANCAAQlVRbnsvZB6zBmGcinmCMxKIeGrI5VOw9SrZDAe/5HiNB60xDwkNP7dVDI/90+myRSHMN3eUrF1EFqJmtZvL5V
Alice's shared secret: hayYCAA23oC98d1SxhFpfiYgY5DVElmEno4851HtgKM=


第 3 步 - Web 加密端 (Bob):从 C# 端导入公钥并生成共享 key

以下 Web Crypto 代码创建共享 key 。为此,必须导入 C# 端的公钥。请注意(类似于导出)PKCS8 格式的导入在 Firefox 下不起作用,这是由于一个错误:

getSharedSecret().then(() => {});

async function getSharedSecret() {
var bobPrivateKeyB64 = 'MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg72fE/+7WX5aKAMiy8kTkCTVeGR8oOlKuoQ8iXTQWmxGhRANCAATZf1xbs/aDgNvcESB+OzyXCmC6Q9jhCDeZwvBUlb5bADuVuLP43UwgvfL4m6YEXWoh3LHbivn5KvhziJixNaHb';
var alicePublicKeyB64 = 'MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEJVUW57L2QeswZhnIp5gjMSiHhqyOVTsPUq2QwHv+R4jQetMQ8JDT+3VQyP/dPpskUhzDd3lKxdRBaiZrWby+VQ==';
var privateKey = await window.crypto.subtle.importKey(
"pkcs8",
new Uint8Array(_base64ToArrayBuffer(bobPrivateKeyB64)),
{ name: "ECDH", namedCurve: "P-256" },
true,
["deriveKey", "deriveBits"]
);
var publicKey = await window.crypto.subtle.importKey(
"spki",
new Uint8Array(_base64ToArrayBuffer(alicePublicKeyB64)),
{ name: "ECDH", namedCurve: "P-256"},
true,
[]
);
var sharedSecret = await window.crypto.subtle.deriveBits(
{ name: "ECDH", namedCurve: "P-256", public: publicKey },
privateKey,
256
);
var sharedSecretHash = await crypto.subtle.digest('SHA-256', sharedSecret);
var sharedSecretHashB64 = btoa(String.fromCharCode.apply(null, new Uint8Array(sharedSecretHash)));
console.log("Bob's shared secret: " + sharedSecretHashB64.replace(/(.{64})/g,'$1\n'));
};

// from https://stackoverflow.com/a/21797381/9014097
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;
}

在 Web Crypto 方面,这导致共享 secret :

Bob's shared secret: hayYCAA23oC98d1SxhFpfiYgY5DVElmEno4851HtgKM=

按照C#端。


请注意,C# 端的DeriveKeyMaterial() 返回的不是实际的共享 key S,而是共享 key 的SHA-256 哈希值H(S)。由于哈希不可逆,因此无法确定实际的共享 secret 。因此,唯一的选择是通过使用 SHA-256 的显式哈希在 Web Crypto 端创建 H(S),另请参见 here .

关于javascript - .NET Core 5.0 到 Javascript DFH key 交换不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65726058/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com