Как преобразовать необработанные представления пары ключей ECDH в веб-ключ JSON?

В настоящее время я пытаюсь передать пару открытых / закрытых ключей, сгенерированных через ECDH, представленных в виде шестнадцатеричных строк, в функцию importKey Web Crypto API.

Я получаю эти ключи из внешнего источника, но сгенерировал аналогичные ключи через 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 в importKey.

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, так как "необработанный" вариант принимает только открытые ключи EC.

const importedPrvKey = await crypto.subtle.importKey(
  'raw',
  hexToUintArray('4bd22700ec3450b5f27e47ba70c233a680c981ab02c1432a859ae23111bef377'),
  {
    name: 'ECDH',
    namedCurve: 'P-256'
  },
  true,
  []
);

Я знаю, что могу легко импортировать ключи, если они находятся в формате JSON Web Key, но мне неизвестен способ его преобразования из необработанного формата в формат JWK или любой другой импортируемый формат, который принимает Web Crypto API. .


person Frelia    schedule 02.07.2019    source источник


Ответы (1)


Мне удалось решить эту проблему, просмотрев исходный код файла pem-to-jwk исходный код библиотеки. Сама библиотека обеспечивает преобразование из PEM в JWK.

Параметр «d» - это ArrayBuffer закрытого ключа, закодированный в кодировке Base64. Параметр «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,
  []
);
person Frelia    schedule 02.07.2019