Search code examples
openssljwtcryptographykeyprivate-key

How to create private key for JWT ES384


I'm trying to create a JWT token on JWT.io with ES384 algorithm.

I tried creating a private key in a couple of ways (mostly found online):

openssl ecparam -name secp384r1 -genkey -noout -out privatekey
ssh-keygen -t ecdsa -b 384 -f privatekey

I also tried formatting the key in pkcs8 as such:

openssl pkcs8 -topk8 -in privatekey -out private.pem

The public key I always created with:

openssl ec -in privatekey -pubout -out publickey

But in the end, on JWT.io, it always returns 'invalid signature'. I think I'm missing some basic understanding of what is expected for the 'private key' field in jwt.io but can't figure it out. Any idea what I'm doing wrong?


As an example:

openssl ecparam -name secp384r1 -genkey -noout -out testprivatekey
openssl ec -in testprivatekey -pubout -out testpublickey

Gives me following keys:

-----BEGIN EC PRIVATE KEY-----
MIGkAgEBBDBkjWOV0LAn2iaHna4uSIY5yoaA5KOvAoGqr9sUNabrbfMuQXDnSUng
xsfTymDJGICgBwYFK4EEACKhZANiAAQySjyVeqVk20TQ8mw0vKZU9jDd0C27virc
BcqHKH+iAYOP76814HxFhC+K5v4eV9uIowUAXi/mdKM5UQq5YW1f21ETM6i9lg31
+SKMx6to499aJe5LJVxTbbuVKADrrgY=
-----END EC PRIVATE KEY-----

-----BEGIN PUBLIC KEY----- 
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEMko8lXqlZNtE0PJsNLymVPYw3dAtu74q 
3AXKhyh/ogGDj++vNeB8RYQviub+HlfbiKMFAF4v5nSjOVEKuWFtX9tREzOovZYN 
9fkijMeraOPfWiXuSyVcU227lSgA664G
-----END PUBLIC KEY-----

When added both in jwt.io: 'invalid signature'.


Solution

  • The curve you're using is correct for use with the ES384 JWS algorithm. The only obstacle is the key encoding/format.

    Due to limitations in the Web Cryptography API jwt.io only supports PKCS#8 for EC private keys and SPKI for EC public keys.

    The following are example ES384 keys that work as input

    -----BEGIN PRIVATE KEY-----
    MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDCuAVir5v+2gZv7A3dR
    EKT977pKPY+1S+h58Xbzir2gqdUKLuyCUCYJmQ6/7ac4B4ShZANiAASndkjwMCbG
    fwEnf3fpjkwdEtdMCDpLEI2G4fokES6J66JxRj3CpmTwLrdJkiPiG0B6pKO+zVft
    4j1XajyxhSmyuPpZQo7KaoW2QLEzBZC4M+1ko4cLd9JaSNC9//vcYf4=
    -----END PRIVATE KEY-----
    
    -----BEGIN PUBLIC KEY-----
    MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEp3ZI8DAmxn8BJ3936Y5MHRLXTAg6SxCN
    huH6JBEuieuicUY9wqZk8C63SZIj4htAeqSjvs1X7eI9V2o8sYUpsrj6WUKOymqF
    tkCxMwWQuDPtZKOHC3fSWkjQvf/73GH+
    -----END PUBLIC KEY-----
    

    The above was generated with

    openssl ecparam -name secp384r1 -genkey -noout -out sec1_ec_p384_private.pem
    openssl pkcs8 -topk8 -nocrypt -in sec1_ec_p384_private.pem -out ec_p384_private.pem
    rm sec1_ec_p384_private.pem
    openssl ec -in ec_p384_private.pem -pubout -out ec_p384_public.pem
    

    It also supports JWK as input.

    (private)

    {
      "kty": "EC",
      "x": "p3ZI8DAmxn8BJ3936Y5MHRLXTAg6SxCNhuH6JBEuieuicUY9wqZk8C63SZIj4htA",
      "y": "eqSjvs1X7eI9V2o8sYUpsrj6WUKOymqFtkCxMwWQuDPtZKOHC3fSWkjQvf_73GH-",
      "crv": "P-384",
      "d": "rgFYq-b_toGb-wN3URCk_e-6Sj2PtUvoefF284q9oKnVCi7sglAmCZkOv-2nOAeE"
    }
    

    and (public)

    {
      "kty": "EC",
      "x": "p3ZI8DAmxn8BJ3936Y5MHRLXTAg6SxCNhuH6JBEuieuicUY9wqZk8C63SZIj4htA",
      "y": "eqSjvs1X7eI9V2o8sYUpsrj6WUKOymqFtkCxMwWQuDPtZKOHC3fSWkjQvf_73GH-",
      "crv": "P-384"
    }