我试图使用椭圆曲线Diffie-Hellman键在浏览器和NodeJS之间创建一个共享的秘密。如果我将浏览器公钥导出为raw,那么一切都正常,但我需要将密钥导出为spki,然后NodeJS就会为此而疯狂。
在浏览器中,我这样做:
async function generateDHKeys() {
const key_ECDH = await window.crypto.subtle.generateKey(
{ name: 'ECDH', namedCurve: 'P-256' },
true,
['deriveKey'],
);
const publicKeyData = await window.crypto.subtle.exportKey(
'spki',
key_ECDH.publicKey,
);
const publicKeyBytes = new Uint8Array(publicKeyData);
publicKeyB64 = btoa(String.fromCharCode.apply(null, publicKeyBytes));
const privateKeyData = await window.crypto.subtle.exportKey(
'pkcs8',
key_ECDH.privateKey,
);
const privateKeyBytes = new Uint8Array(privateKeyData);
privateKeyB64 = btoa(String.fromCharCode.apply(null, privateKeyBytes));
privateKeyBytes.fill(0);
return { publicKeyB64, privateKeyB64 };
}
const {publicKeyB64} = await generateDHKeys();所以,现在我已经导出了公钥并将其转换为Base64。然后将其发送到NodeJS服务器,并尝试创建一个共享秘密:
在NodeJS中,我这样做:
export function generateDHKeys(foreignPublicKeyB64) {
const ecdh = crypto.createECDH("prime256v1");
ecdh.generateKeys();
const publicKeyB64 = ecdh.getPublicKey("base64");
const privateKeyB64 = ecdh.getPrivateKey("base64");
const sharedSecretB64 = ecdh.computeSecret(foreignPublicKeyB64, "base64", "base64");
const sharedSecretHashB64 = crypto
.createHash("sha256")
.update(sharedSecretB64, "base64")
.digest("base64");
return { publicKeyB64, privateKeyB64, sharedSecretB64, sharedSecretHashB64 };
}我得到一个错误,说“公钥对指定的曲线无效”。
但是,如果在浏览器代码中我将密钥导出为raw (而不是spki),那么它可以工作.
如何在浏览器中将公钥导出为spki,然后使用它在NodeJS中生成共享秘密?或者,如何将Base64 SPKI公钥转换为Node中的原始密钥?
编辑已经发现,Node v15.0.0+确实支持浏览器Crypto,这意味着我的浏览器JS可以简单地在Node上下文中复制和运行。与在浏览器中访问window.crypto.subtle不同,在Node应用程序中,我可以导入这样的微妙模块:
const { subtle } = require("crypto").webcrypto;但是..。正如@Topaco所指出的,从Nodev16.2.0开始,这个API仍然是实验性的,可能会发生变化。有关更多信息和文档链接,请参见@Topaco的答复。
发布于 2021-05-20 20:41:02
据我所知,NodeJS密码模块不支持ECDH上下文中公钥的X.509/SPKI格式,而只支持原始密钥。但是,可以从X.509/SPKI密钥派生原始密钥。
用WebCrypto代码生成的X.509/SPKI密钥封装了原始(更准确地说是未压缩的)密钥0x04 ++,该密钥在最后被本地化。对于P-256又名prime256v1,最后65个字节对应于原始键。前部对于不同的P-256键是相同的。
这样,在NodeJS代码中,可以将P-256的原始键确定为X.509/SPKI键的最后65个字节。
类似地,X.509/SPKI密钥的前端可以与用NodeJS代码生成的原始密钥连接,从而将原始密钥转换为X.509/SPKI格式。
用于此的NodeJS代码是:
// Convert the SPKI key of the WebCrypto side into the raw format
var webcryptoSpkiB64 = 'MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEPF2r2yyMp/PykPZEt6v8WFAvnrf5FsI3UnpEYsbKo7UKVKB8k2hfxxhjKw8p9nulNaRo472hTcEqsSbsGcr5Dg==';
var webcryptoRawB64 = Buffer.from(webcryptoSpkiB64, 'base64').slice(-65).toString('base64'); // the last 65 bytes
// Calculate the shared secret for the NodeJS side
var { publicKeyB64, privateKeyB64, sharedSecretB64, sharedSecretHashB64 } = generateDHKeys(webcryptoRawB64);
// Convert the raw key of the NodeJS side into the SPKI format
var nodejsSpkiB64 = Buffer.concat([
Buffer.from(webcryptoSpkiB64, 'base64').slice(0, -65), // all bytes except the last 65
Buffer.from(publicKeyB64, 'base64')]
).toString('base64');
console.log("Shared secret:", sharedSecretB64);
console.log("SPKI:", nodejsSpkiB64); // will be sent to the WebCrypto side and used there to calculate the shared secret其中generateDHKeys()是发布在问题中的函数。
编辑:,正如OP注释中所指出的,WebCrypto API现在是NodeJS的一部分,因此X.509/SPKI密钥也通过NodeJS中的WebCrypto API在ECDH上下文中得到支持。但是,需要注意的是,WebCrypto API在当前的NodeJS版本v16.0.2中具有1级稳定性(试验性)。这意味着非向后兼容的更改或删除是可能的。此外,当前的LTS版本(v14.17.0)不包括WebCrypto API。
https://stackoverflow.com/questions/67621606
复制相似问题