.NET Core 5.0 到 Javascript DFH 密钥交换不起作用
.NET Core 5.0 to Javascript DFH Key exchange not working
我们正在尝试使用 JS 获取基于浏览器的应用程序,以使用椭圆曲线 Diffie Hellman 与 .Net Core 5.0 服务器交换密钥。
我们的应用程序需要在两端共享秘密以进行我们进行的某些特定处理(不是加密,而是我们自己的过程),并且我们希望导出该秘密
而不是出于安全目的传输。
我们四处寻找合适的解决方案并将各种解决方案拼凑在一起,我们想出了这个,但它失败了,C# 端出现异常。
基本上,我们的伪代码如下(到目前为止我们只进行了第 3 步):
- 客户端 (Bob) 使用 window.crypto.subtle 库在客户端中生成 ECDH / P-256 密钥对。
- Bob 从这对导出 public 密钥并向服务器 (Alice) 发出 GET 以获取她的 public 密钥(将 bobPublicKeyB64 作为查询参数传递)。
- Alice 接收传入请求并调用 C# 方法使用 Bob 的 public 密钥创建共享密钥。
- Alice 将此共享秘密存储在内存缓存中,returns 她的 public 密钥存储在 Bob 中。
- Bob 然后使用 Alice 的 public 密钥获取他自己的共享秘密并将其存储在浏览器的“会话存储”中。
发送 Bob 的 public 密钥给 Alice 的代码这里没有显示,它是一个标准的 XMLHttpRequest。然而,它确实有效,并且服务器被调用,正如 C# 代码中的失败所证明的那样。我们验证了 Bob 发送的 bobPublicKeyB64 和 Alice 收到的 bobPublicKeyB64 是完全一样的
一旦成功,我们将强化两端的存储方法,但首先我们需要让交换正常工作。
Alice (C#) 代码块中的注释显示了它失败的地方。
getDerivedKey 方法(暂时注释掉)来自这个 post -
并且它因不同的异常而失败(我确信这两次失败都是由于 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 的代码(借自 )如下所示:
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 端的 public 密钥以 X.509/SPKI 格式而不是原始密钥导出,则解决方案更简单,因为 .NET 5 有专用的导入方法,ImportSubjectPublicKeyInfo()
,对于这种格式。此外,这与 C# 代码一致,其中 public 密钥也以 X.509/SPKI 格式导出。在以下示例中,Web Crypto 代码以 X.509/SPKI 格式导出 public 密钥。
第 1 步 - Web 加密端 (Bob):生成 EC 密钥对
以下 Web Crypto 代码创建一个 ECDH 密钥对并以 X.509/SPKI 格式导出 public 密钥(以及 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,'\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,'\n'));
};
一个可能输出的是下面的密钥对,在后面的课程中作为Web Crypto端的密钥对:
Bob's public: MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2X9cW7P2g4Db3BEgfjs8lwpgukPY4Qg3mcLwVJW+WwA7lbiz+N1MIL3y+JumBF1qIdyx24r5+Sr4c4iYsTWh2w==
Bob's private: MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg72fE/+7WX5aKAMiy8kTkCTVeGR8oOlKuoQ8iXTQWmxGhRANCAATZf1xbs/aDgNvcESB+OzyXCmC6Q9jhCDeZwvBUlb5bADuVuLP43UwgvfL4m6YEXWoh3LHbivn5KvhziJixNaHb
步骤 2 - C# 端 (Alice):从 Web Crypto 端导入 public 密钥并生成共享密钥
以下 C# 代码生成 ECDH 密钥对,导出 X.509/SPKI 格式的 public 密钥(以及 PKCS8 格式的私钥)并确定共享密钥。为获取共享密钥,Web Crypto 端的 X.509/SPKI 格式的 public 密钥以 ImportSubjectPublicKeyInfo()
.
导入
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);
}
}
一个可能的输出是以下密钥对和共享秘密。该密钥对作为后续课程中C#端的密钥对使用:
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# 端导入 public 密钥并生成共享密钥
以下 Web Crypto 代码创建共享机密。为此,必须导入 C# 端的 public 密钥。请注意(类似于导出)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,'\n'));
};
// from
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 端,这导致共享秘密:
Bob's shared secret: hayYCAA23oC98d1SxhFpfiYgY5DVElmEno4851HtgKM=
按照C#端。
请注意,C# 端的 DeriveKeyMaterial()
并不是 return 实际的共享机密 S,而是共享机密的 SHA-256 哈希 H(S)。由于哈希不可逆,因此无法确定实际的共享秘密。因此,唯一的选择是通过使用 SHA-256 的显式哈希在 Web 加密端创建 H(S),另请参见 here。
我们正在尝试使用 JS 获取基于浏览器的应用程序,以使用椭圆曲线 Diffie Hellman 与 .Net Core 5.0 服务器交换密钥。 我们的应用程序需要在两端共享秘密以进行我们进行的某些特定处理(不是加密,而是我们自己的过程),并且我们希望导出该秘密 而不是出于安全目的传输。 我们四处寻找合适的解决方案并将各种解决方案拼凑在一起,我们想出了这个,但它失败了,C# 端出现异常。 基本上,我们的伪代码如下(到目前为止我们只进行了第 3 步):
- 客户端 (Bob) 使用 window.crypto.subtle 库在客户端中生成 ECDH / P-256 密钥对。
- Bob 从这对导出 public 密钥并向服务器 (Alice) 发出 GET 以获取她的 public 密钥(将 bobPublicKeyB64 作为查询参数传递)。
- Alice 接收传入请求并调用 C# 方法使用 Bob 的 public 密钥创建共享密钥。
- Alice 将此共享秘密存储在内存缓存中,returns 她的 public 密钥存储在 Bob 中。
- Bob 然后使用 Alice 的 public 密钥获取他自己的共享秘密并将其存储在浏览器的“会话存储”中。
发送 Bob 的 public 密钥给 Alice 的代码这里没有显示,它是一个标准的 XMLHttpRequest。然而,它确实有效,并且服务器被调用,正如 C# 代码中的失败所证明的那样。我们验证了 Bob 发送的 bobPublicKeyB64 和 Alice 收到的 bobPublicKeyB64 是完全一样的
一旦成功,我们将强化两端的存储方法,但首先我们需要让交换正常工作。
Alice (C#) 代码块中的注释显示了它失败的地方。
getDerivedKey 方法(暂时注释掉)来自这个 post -
以下是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 的代码(借自
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 端的 public 密钥以 X.509/SPKI 格式而不是原始密钥导出,则解决方案更简单,因为 .NET 5 有专用的导入方法,ImportSubjectPublicKeyInfo()
,对于这种格式。此外,这与 C# 代码一致,其中 public 密钥也以 X.509/SPKI 格式导出。在以下示例中,Web Crypto 代码以 X.509/SPKI 格式导出 public 密钥。
第 1 步 - Web 加密端 (Bob):生成 EC 密钥对
以下 Web Crypto 代码创建一个 ECDH 密钥对并以 X.509/SPKI 格式导出 public 密钥(以及 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,'\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,'\n'));
};
一个可能输出的是下面的密钥对,在后面的课程中作为Web Crypto端的密钥对:
Bob's public: MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2X9cW7P2g4Db3BEgfjs8lwpgukPY4Qg3mcLwVJW+WwA7lbiz+N1MIL3y+JumBF1qIdyx24r5+Sr4c4iYsTWh2w==
Bob's private: MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg72fE/+7WX5aKAMiy8kTkCTVeGR8oOlKuoQ8iXTQWmxGhRANCAATZf1xbs/aDgNvcESB+OzyXCmC6Q9jhCDeZwvBUlb5bADuVuLP43UwgvfL4m6YEXWoh3LHbivn5KvhziJixNaHb
步骤 2 - C# 端 (Alice):从 Web Crypto 端导入 public 密钥并生成共享密钥
以下 C# 代码生成 ECDH 密钥对,导出 X.509/SPKI 格式的 public 密钥(以及 PKCS8 格式的私钥)并确定共享密钥。为获取共享密钥,Web Crypto 端的 X.509/SPKI 格式的 public 密钥以 ImportSubjectPublicKeyInfo()
.
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);
}
}
一个可能的输出是以下密钥对和共享秘密。该密钥对作为后续课程中C#端的密钥对使用:
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# 端导入 public 密钥并生成共享密钥
以下 Web Crypto 代码创建共享机密。为此,必须导入 C# 端的 public 密钥。请注意(类似于导出)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,'\n'));
};
// from
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 端,这导致共享秘密:
Bob's shared secret: hayYCAA23oC98d1SxhFpfiYgY5DVElmEno4851HtgKM=
按照C#端。
请注意,C# 端的 DeriveKeyMaterial()
并不是 return 实际的共享机密 S,而是共享机密的 SHA-256 哈希 H(S)。由于哈希不可逆,因此无法确定实际的共享秘密。因此,唯一的选择是通过使用 SHA-256 的显式哈希在 Web 加密端创建 H(S),另请参见 here。