Home > Blockchain >  Unable to import OpenSSL generated private key into web API crypto.subtle.importKey
Unable to import OpenSSL generated private key into web API crypto.subtle.importKey

Time:08-31

I'm creating a private EC key using the command openssl ecparam -name secp256r1 -genkey -noout -out k1.pem. I'm trying to import it using crypto.subtle.importKey as described in this documentation.

I.e. after running OpenSSL I have the private key

-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIOuQ2Du5Q3bE0wzYwqImGjbb6zASbof36MVeRQMTfOA5oAoGCCqGSM49
AwEHoUQDQgAErSz9FIO1sLPciJjHbj/EduYNClaeCBhQuU5ZPh24YRvHG5FXLNiV
1dPIB02KyKXiYjvLro586uYDkYzaPkzbOw==
-----END EC PRIVATE KEY-----

and from Chrome console

let binary = atob("MHcCAQEEIOuQ2Du5Q3bE0wzYwqImGjbb6zASbof36MVeRQMTfOA5oAoGCCqGSM49AwEHoUQDQgAErSz9FIO1sLPciJjHbj/EduYNClaeCBhQuU5ZPh24YRvHG5FXLNiV1dPIB02KyKXiYjvLro586uYDkYzaPkzbOw==")
let der = str2ab(binary)
let key = crypto.subtle.importKey(
    'pkcs8',
    der,
    {
      name: 'ECDSA',
      namedCurve: 'P-256',
    },
    false,
    ['sign']
  );

getting a Uncaught (in promise) Error from the last command.


It's worth mentioning that when I use Java libraries through Clojure to generate the EC key I can import it successfully using the same js code:

(ns vouch.crypto
  (:import (java.security KeyPairGenerator SecureRandom KeyPair)
           (org.bouncycastle.jce ECNamedCurveTable)
           (java.util Base64)))

(defn generate-ec-key-pair
  "Generate an Elliptic Curve Key Pair"
  ^KeyPair []
  (let [ec-spec (ECNamedCurveTable/getParameterSpec "secp256r1")
        ^KeyPairGenerator generator (KeyPairGenerator/getInstance "ECDSA" "BC")]
    (.initialize generator ec-spec (SecureRandom.))
    (.generateKeyPair generator)))

(defn bytes->hex
  "Convert a seq of bytes into a hex encoded string."
  ^String [^bytes bytes]
  (apply str (for [b bytes] (format "x" b))))

(defn- base64-encode
  ^String [^bytes bytes-to-encode]
  (-> (Base64/getEncoder)
      (.encode bytes-to-encode)
      (String. StandardCharsets/UTF_8)))

(comment
  (let [key-pair           (vouch.crypto/generate-ec-key-pair)
        private-key        (.getPrivate key-pair)
        private-key-base64 (-> private-key .getEncoded vouch.crypto/base64-encode)]
    (println (str "-----BEGIN EC PRIVATE KEY-----\n" private-key-base64 "\n-----END EC PRIVATE KEY-----"))))

CodePudding user response:

This is a private EC key in SEC1 format, which is not supported by WebCrypto, s. here for the supported formats.
A format supported by WebCrypto for private keys is PKCS#8 (which, by the way, you have already used in your WebCrypto code as the 1st parameter in the importKey() call), so a possible fix would be to convert the key to this format, e.g. with OpenSSL:

openssl pkcs8 -topk8 -nocrypt -in <path to input-sec1-pem> -out <path to output-pkcs8-pem>

which gives:

-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg65DYO7lDdsTTDNjC
oiYaNtvrMBJuh/foxV5FAxN84DmhRANCAAStLP0Ug7Wws9yImMduP8R25g0KVp4I
GFC5Tlk HbhhG8cbkVcs2JXV08gHTYrIpeJiO8uujnzq5gORjNo TNs7
-----END PRIVATE KEY-----

Both formats represent the same key (i.e. encapsulate the same key parameters, i.e. the same raw private key and the same raw public key) as can be verified in an ASN.1 parser (e.g. https://lapo.it/asn1js/)

With the key in PKCS#8 format the import is successful:

(async () => {

    function b642ab(base64_string){
        return Uint8Array.from(window.atob(base64_string), c => c.charCodeAt(0));
    }

    let der = b642ab("MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg65DYO7lDdsTTDNjCoiYaNtvrMBJuh/foxV5FAxN84DmhRANCAAStLP0Ug7Wws9yImMduP8R25g0KVp4IGFC5Tlk HbhhG8cbkVcs2JXV08gHTYrIpeJiO8uujnzq5gORjNo TNs7")
    let key = await crypto.subtle.importKey(
        'pkcs8',
        der,
        {
            name: 'ECDSA',
            namedCurve: 'P-256',
        },
        false,
        ['sign']
    );

    console.log(key);

})();

  • Related