如何将ECDH密钥对的原始表示形式转换为JSON Web密钥?

时间:2019-07-02 06:56:54

标签: javascript cryptography jwk

当前,我正在尝试将通过ECDH生成的一对公共/私有密钥(表示为十六进制字符串)传递到Web Crypto API的importKey函数中。

我正在从外部来源接收这些密钥,但是我已经通过node.js生成了相似的密钥进行测试。曲线为prime256v1。作为参考,我用来测试获得的公钥是04b71388fced2daee34793f74a7dfa982e37ce539a728233bcadaec298fc4ee422165b8db13e657f9c7b27b35364f523ad11fab29d717606140cc6312ec2c685cc,私钥是4bd22700ec3450b5f27e47ba70c233a680c981ab02c1432a859ae23111bef377

const crypto = require('crypto');
const ecdh = crypto.createECDH('prime256v1');
ecdh.generateKeys();
console.log('ecdh p256 pubkey', ecdh.getPublicKey('hex'));
console.log('ecdh p256 prvkey', ecdh.getPrivateKey('hex'));

通过raw的{​​{1}}选项成功导入了公钥。

importKey

但是,由于“ raw”选项仅接受EC公钥,因此私钥无法通过相同的方法导入,因为它以错误const hexToUintArray = hex => { const a = []; for (let i = 0, len = hex.length; i < len; i += 2) { a.push(parseInt(hex.substr(i, 2), 16)); } return new Uint8Array(a); } const importedKey = await crypto.subtle.importKey( 'raw', hexToUintArray('04b71388fced2daee34793f74a7dfa982e37ce539a728233bcadaec298fc4ee422165b8db13e657f9c7b27b35364f523ad11fab29d717606140cc6312ec2c685cc'), { name: 'ECDH', namedCurve: 'P-256' }, true, [] ); 失败。

DataError: Data provided to an operation does not meet requirements

我知道,如果密钥为JSON Web密钥格式,则能够轻松导入,但是我不知道将其从原始格式转换为JWK格式或Web上任何其他可导入格式的方法。加密API接受。

1 个答案:

答案 0 :(得分:0)

通过查看pem-to-jwk库源代码的源代码来设法解决这个问题。该库本身提供了从PEM到JWK的转换。

“ d”参数是私钥的ArrayBuffer,在Base64中用url编码。 “ x”参数是ArrayBuffer中未压缩的公共密钥的前半部分,其URL编码为Base64字符串。 “ y”参数是ArrayBuffer中未压缩的公共密钥的后半部分,其URL编码为Base64字符串。

const publicKeyHex = '04b71388fced2daee34793f74a7dfa982e37ce539a728233bcadaec298fc4ee422165b8db13e657f9c7b27b35364f523ad11fab29d717606140cc6312ec2c685cc';
const privateKeyHex = '4bd22700ec3450b5f27e47ba70c233a680c981ab02c1432a859ae23111bef377';

const hexToUintArray = hex => {
  const a = [];
  for (let i = 0, len = hex.length; i < len; i += 2) {
    a.push(parseInt(hex.substr(i, 2), 16));
  }
  return new Uint8Array(a);
}

const hexToArrayBuf = hex => {
  return hexToUintArray(hex).buffer;
}

const arrayBufToBase64UrlEncode = buf => {
  let binary = '';
  const bytes = new Uint8Array(buf);
  for (var i = 0; i < bytes.byteLength; i++) {
      binary += String.fromCharCode(bytes[i]);
  }
  return window.btoa(binary)
    .replace(/\//g, '_')
    .replace(/=/g, '')
    .replace(/\+/g, '-');
}

const jwkConv = (prvHex, pubHex) => ({
  kty: "EC",
  crv: "P-256",
  d: arrayBufToBase64UrlEncode(hexToArrayBuf(prvHex)),
  x: arrayBufToBase64UrlEncode(hexToArrayBuf(pubHex).slice(1, 33)),
  y: arrayBufToBase64UrlEncode(hexToArrayBuf(pubHex).slice(33, 66))
});

const importedPrivateKey = await crypto.subtle.importKey(
  'jwk',
  jwkConv(privateKeyHex, publicKeyHex),
  {
    name: 'ECDH',
    namedCurve: 'P-256'
  },
  true,
  []
);