首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >共享ECDH秘密,Browser + NodeJS

共享ECDH秘密,Browser + NodeJS
EN

Stack Overflow用户
提问于 2021-05-20 13:45:03
回答 1查看 601关注 0票数 2

我试图使用椭圆曲线Diffie-Hellman键在浏览器和NodeJS之间创建一个共享的秘密。如果我将浏览器公钥导出为raw,那么一切都正常,但我需要将密钥导出为spki,然后NodeJS就会为此而疯狂。

在浏览器中,我这样做:

代码语言:javascript
复制
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中,我这样做:

代码语言:javascript
复制
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应用程序中,我可以导入这样的微妙模块:

代码语言:javascript
复制
const { subtle } = require("crypto").webcrypto;

但是..。正如@Topaco所指出的,从Nodev16.2.0开始,这个API仍然是实验性的,可能会发生变化。有关更多信息和文档链接,请参见@Topaco的答复。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 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代码是:

代码语言:javascript
复制
// 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。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/67621606

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档