Тонкий криптографический импорт ключа PCKS RSA приводит к ошибке ERR_OSSL_ASN1_WRONG_TAG

Я пробую тонкие криптографические методы в Node.js 18. Я хочу импортировать ключ RSA частный.

Я скопировал пример прямо из документов MDN (https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/importKey#pkcs_8_import) и переписал его, чтобы можно было запустить его в Node.js:

const { subtle } = require('crypto').webcrypto;

function str2ab(str) {
  const buf = new ArrayBuffer(str.length);
  const bufView = new Uint8Array(buf);
  for (let i = 0, strLen = str.length; i < strLen; i++) {
    bufView[i] = str.charCodeAt(i);
  }
  return buf;
}

// Example 2048 bit Key generated from https://travistidwell.com/jsencrypt/demo/
const pemEncodedKey = `-----BEGIN PRIVATE KEY-----
MIIEowIBAAKCAQBux5llZPW8B9b3n7lvw6wqeNUX/GKUB3iWN6FWWXRRlSdqvNkI2nGiiKE5lJv+ca507P/xPsj/ThmntnVPrwiN98FWlY8h7GPKAqPzzG+C+kOPpqvkZm10RHn/pjcdAi5QqBauMrZCis77x5O1ynJFu6DcDH/QM+0O4FCL+KHcahDhp/3Oc5O4cGDbGJcc8AsnD7d2Dv1yiFMDidKg2OuQIb/YvdxgO+DKB3Pjali3escD7b/Tj9iYQIhV3wEcXBFnenQGqz227bmtuRatyDgcOB5HL8k/YoxzTm7+1Qh2rhzJBe5zdZ86Ms7P7RPCg+od+wehzUowxWkxB372vWWfAgMBAAECggEAUxqRTKssXV5UOXctGVbk9QeodFH1ca8ZGzeoZKq+w+TsqPn6ptWYoaF1sUh2ra6CfVy9tDCxgDUKsfICl0BrXnUaKOYRdhVr1sOcUuxuSweLX1xdXv4n5izoiIwclDpqnD88pHmOmOSg2eiiOqIgj4dt6SXHTF1n3N0SD675XeuGmUVmNvw3V//FjpQqgAgVRsFoVI8rd3yxEeZRy/T8/lZWWQxt9232Ipcd91zHXFEvj+gQ8pAtyhLJ34dDJdT7Fs7eoDAkGqson1TedblGPbq+pieB59xGqciZgBQpcZNsfCuyzRA3skgd1Fu2NRnzgdDklKHnjEcUfib12nqTAQKBgQCyu77dAZezADejbCDxyRpHnGfp1Jz5p/Y/j9hvGEPl72aiFFZpD3Rbz3kPn9SayYVq5Zxjlt+KwuljNFppYeK0ZVZ2zWi1VmhrHwl+3yemf4ufltvCDCutDNLjjkt1rlwiz+eDmHSJiOFSEc5uJcFrwcAWDBKBQP1vn4VFqJ43fwKBgQCeq3tzAd/9W3ph7Dgv2ungimQ9vSZIfWawFnKyhfF5P7y8wJ6mgxnNv/BiuwqdqS5DY72EVq0tjwVvpHA6xCiWKYQ/9lhb9OJZsADbAaMRgKzuKCmpxJA2c6EKDQ2TK0/3WdLLnpuAXKcHP38sKJmKxm5wgRjC0pZLpWcK/xjh4QKBgQCEuxIxlAYxAz9OWHVauUqP1aIBr0fnywj/CPblAbMipZelU88b9EMoDzpLFRnQ3Uj8KonqF1fo93hUmMNvsSanav47+a0BxaqDqqfllRkf92Yb3O9T+q/Qsk5GeRymxxZbL+QxAN3CaWlTBjAz8kvilx7sAIkZfcb3xxI0udTNRwKBgD664SWI2jtaTToln9kbnVdOn27hNx91pIF9fn8iAWPEVSPyq0Z9klgLyEfgVsQaPNYburN1aSYX4zhONKinILytUUHQbQJ+AHcg5FWxgfzLeJL3gfFCaxl8AXDt1C4Y85aBBpvF6wiGmOp+qhKVQo7hAIyuHVH4276wd9qbHAVBAoGBAIqjXYfmtHRONXVtMCt5rlt3Y/Pe0KkA6vvfjGMnRKrmvEsHXh8Aj9ZPiTDxBKweJggg9BoJ/bs3juENIx5cJ76aiBYsdhw+4IGF79IfZm73k2ZC9exy9m69yp7GszmdZVJuiFlRoJFBMQm6oDH1+tUPW9M/76Ah8GpjkWx6dAcr
-----END PRIVATE KEY-----`;

const importPrivateKey = async () => {
  const pemHeader = "-----BEGIN PRIVATE KEY-----";
  const pemFooter = "-----END PRIVATE KEY-----";
  const pemContents = pemEncodedKey.substring(pemHeader.length, pemEncodedKey.length - pemFooter.length);
  const binaryDerString = Buffer.from(pemContents, 'base64').toString('binary');
  const binaryDer = str2ab(binaryDerString);

  const key = await subtle.importKey(
    "pkcs8",
    binaryDer,
    {
      name: "RSA-OAEP",
      hash: "SHA-256",
    },
    true,
    ["decrypt"]
  );
}

importPrivateKey();

Теперь, когда я запускаю этот файл, я получаю:

node:internal/crypto/keys:618
    handle.init(kKeyTypePrivate, data, format, type, passphrase);
           ^

Error: error:0D0680A8:asn1 encoding routines:asn1_check_tlen:wrong tag
    at createPrivateKey (node:internal/crypto/keys:618:12)
    at Object.rsaImportKey (node:internal/crypto/rsa:270:19)
    at SubtleCrypto.importKey (node:internal/crypto/webcrypto:513:10)
    at importPrivateKey (/Users/felix/Desktop/fts/js-api/bin/test.js:23:28)
    at Object.<anonymous> (/Users/felix/Desktop/fts/js-api/bin/test.js:35:1)
    at Module._compile (node:internal/modules/cjs/loader:1105:14)
    at Module._extensions..js (node:internal/modules/cjs/loader:1159:10)
    at Module.load (node:internal/modules/cjs/loader:981:32)
    at Module._load (node:internal/modules/cjs/loader:827:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:77:12) {
  opensslErrorStack: [
    'error:0D08303A:asn1 encoding routines:asn1_template_noexp_d2i:nested asn1 error',
    'error:0D07803A:asn1 encoding routines:asn1_item_embed_d2i:nested asn1 error'
  ],
  library: 'asn1 encoding routines',
  function: 'asn1_check_tlen',
  reason: 'wrong tag',
  code: 'ERR_OSSL_ASN1_WRONG_TAG'
}

Этот пример работает, когда я использую ключ из связанных документов MDN. Но не в том случае, если я использую свежесгенерированный.

Что здесь происходит не так?

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
22
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Ваш закрытый ключ несовместим: вы используете верхний и нижний колонтитулы ключа PKCS#8, но тело ключа в формате PKCS#1. Это можно проверить, например. в парсере ASN.1, таком как https://lapo.it/asn1js/.
Обратите внимание, что Демонстрация JSEncrypt генерирует закрытый ключ в формате PKCS#1 (включая заголовок и нижний колонтитул PKCS#1).

Поскольку API WebCrypto не поддерживает PKCS#1 (см. здесь), вам необходимо преобразовать ключ в ключ в формате PKCS8, например. с OpenSSL.
Ваш ключ в формате PKCS#8 (без разрывов строк в теле, поэтому его можно использовать непосредственно в коде JavaScript):

-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAG7HmWVk9bwH1vefuW/DrCp41Rf8YpQHeJY3oVZZdFGVJ2q82QjacaKIoTmUm/5xrnTs//E+yP9OGae2dU+vCI33wVaVjyHsY8oCo/PMb4L6Q4+mq+RmbXREef+mNx0CLlCoFq4ytkKKzvvHk7XKckW7oNwMf9Az7Q7gUIv4odxqEOGn/c5zk7hwYNsYlxzwCycPt3YO/XKIUwOJ0qDY65Ahv9i93GA74MoHc+NqWLd6xwPtv9OP2JhAiFXfARxcEWd6dAarPbbtua25Fq3IOBw4HkcvyT9ijHNObv7VCHauHMkF7nN1nzoyzs/tE8KD6h37B6HNSjDFaTEHfva9ZZ8CAwEAAQKCAQBTGpFMqyxdXlQ5dy0ZVuT1B6h0UfVxrxkbN6hkqr7D5Oyo+fqm1ZihoXWxSHatroJ9XL20MLGANQqx8gKXQGtedRoo5hF2FWvWw5xS7G5LB4tfXF1e/ifmLOiIjByUOmqcPzykeY6Y5KDZ6KI6oiCPh23pJcdMXWfc3RIPrvld64aZRWY2/DdX/8WOlCqACBVGwWhUjyt3fLER5lHL9Pz+VlZZDG33bfYilx33XMdcUS+P6BDykC3KEsnfh0Ml1PsWzt6gMCQaqyifVN51uUY9ur6mJ4Hn3EapyJmAFClxk2x8K7LNEDeySB3UW7Y1GfOB0OSUoeeMRxR+JvXaepMBAoGBALK7vt0Bl7MAN6NsIPHJGkecZ+nUnPmn9j+P2G8YQ+XvZqIUVmkPdFvPeQ+f1JrJhWrlnGOW34rC6WM0Wmlh4rRlVnbNaLVWaGsfCX7fJ6Z/i5+W28IMK60M0uOOS3WuXCLP54OYdImI4VIRzm4lwWvBwBYMEoFA/W+fhUWonjd/AoGBAJ6re3MB3/1bemHsOC/a6eCKZD29Jkh9ZrAWcrKF8Xk/vLzAnqaDGc2/8GK7Cp2pLkNjvYRWrS2PBW+kcDrEKJYphD/2WFv04lmwANsBoxGArO4oKanEkDZzoQoNDZMrT/dZ0suem4Bcpwc/fywomYrGbnCBGMLSlkulZwr/GOHhAoGBAIS7EjGUBjEDP05YdVq5So/VogGvR+fLCP8I9uUBsyKll6VTzxv0QygPOksVGdDdSPwqieoXV+j3eFSYw2+xJqdq/jv5rQHFqoOqp+WVGR/3Zhvc71P6r9CyTkZ5HKbHFlsv5DEA3cJpaVMGMDPyS+KXHuwAiRl9xvfHEjS51M1HAoGAPrrhJYjaO1pNOiWf2RudV06fbuE3H3WkgX1+fyIBY8RVI/KrRn2SWAvIR+BWxBo81hu6s3VpJhfjOE40qKcgvK1RQdBtAn4AdyDkVbGB/Mt4kveB8UJrGXwBcO3ULhjzloEGm8XrCIaY6n6qEpVCjuEAjK4dUfjbvrB32pscBUECgYEAiqNdh+a0dE41dW0wK3muW3dj897QqQDq+9+MYydEqua8SwdeHwCP1k+JMPEErB4mCCD0Ggn9uzeO4Q0jHlwnvpqIFix2HD7ggYXv0h9mbveTZkL17HL2br3KnsazOZ1lUm6IWVGgkUExCbqgMfX61Q9b0z/voCHwamORbHp0Bys=
-----END PRIVATE KEY-----

С этим форматом ключа код JavaScript работает, и ключ импортируется правильно.

Кстати, под NodeJS можно использовать Buffer напрямую, т.е. преобразование через бинарную строку в ArrayBuffer и, следовательно, также str2ab() не нужно:

...
const binaryDer = Buffer.from(pemContents, 'base64');
const key = await subtle.importKey(
    "pkcs8",
    binaryDer,
    {
        name: "RSA-OAEP",
        hash: "SHA-256",
    },
    true,
    ["decrypt"]
);
...

Обратите внимание, что даже в версии 18.1.0 API WebCrypto помечен как Стабильность: 1 - Экспериментальная (здесь).

Исправление можно запустить в режиме онлайн при повторении (под NodeJS v16.7.0): replit.com/@3hK8cL8H24hwiS7/TotalSandybrownCareware.

Topaco 17.05.2022 11:22

Большое спасибо, формат действительно проблема. Также спасибо за упоминание экспериментального флага, я не использую его в производстве.

ffritz 17.05.2022 18:33

Другие вопросы по теме