Home > Software design >  Subtle Crypto Importing PCKS RSA Key leads to ERR_OSSL_ASN1_WRONG_TAG
Subtle Crypto Importing PCKS RSA Key leads to ERR_OSSL_ASN1_WRONG_TAG

Time:05-18

I'm trying out the subtle crypto methods in Node.js 18. I want to import a private RSA Key.

I copied the example straight from the MDN docs (https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/importKey#pkcs_8_import) and rewrote it to be able to run it in 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();

Now when I run this file, I get:

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'
}

This example does work when I use the key from the linked MDN docs. But not if I use a freshly generated one.

What is going wrong here?

CodePudding user response:

Your private key is inconsistent: You use the header and footer of a PKCS#8 key, but the body of a PKCS#1 formatted key. This can be verified e.g. in an ASN.1 parser like https://lapo.it/asn1js/.
Note that the JSEncrypt demo generates a PKCS#1 formatted private key (inclusive PKCS#1 header and footer).

Since WebCrypto API does not support PKCS#1 (see here), you need to convert the key to a PKCS8 formatted key, e.g. with OpenSSL.
Your key in PKCS#8 format (without line breaks in the body, so it can be used directly in JavaScript code) is:

-----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-----

With this key format the JavaScript code works and the key is imported correctly.

By the way, under NodeJS you can use Buffer directly, i.e. the conversion via a binary string into an ArrayBuffer and thus also str2ab() are not needed:

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

Note that even under v18.1.0, the WebCrypto API is marked Stability: 1 - Experimental (here).

  • Related