Search code examples
encryptionopensslpublic-key-encryptionelliptic-curvejwk

How to transform a jwk to public key over openssl


I have a JWE and in header an ephemeral public key. So I have coordinate X and Y.

My question in shell mode how to convert the JWK to ECC public key in pem format.

For example, here a jwk

{"epk":{"kty":"EC","crv":"P-256","x":"GCl--lQHb7NKYU3jXpKVI_BYaTlALT5JFPdl3sbB9mY","y":"ADRX25PBSlZJE79drET0ARtRqZAkUIMNt9aa2bbjBYY"}}

After I do this

> # I convert the x coordonate from base64url to base64
> echo -n -e "GCl++lQHb7NKYU3jXpKVI/BYaTlALT5JFPdl3sbB9mY" | base64 -d | hexdump

0000000 2918 fa7e 0754 b36f 614a e34d 925e 2395
0000010 58f0 3969 2d40 493e f714 de65 c1c6 66f6
0000020

> echo -n -e "ADRX25PBSlZJE79drET0ARtRqZAkUIMNt9aa2bbjBYY" | base64 -d | hexdump

0000000 3400 db57 c193 564a 1349 5dbf 44ac 01f4
0000010 511b 90a9 5024 0d83 d6b7 d99a e3b6 8605
0000020

So the public key is 04 2918 fa7e 0754 b36f 614a e34d 925e 2395 58f0 3969 2d40 493e f714 de65 c1c6 66f6 3400 db57 c193 564a 1349 5dbf 44ac 01f4 511b 90a9 5024 0d83 d6b7 d99a e3b6 8605

But I have the curve, the public key, I want to know how to generate a pem?

In other words,

I have a file mykey.pub which contains this:

-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAETGUwowNEfqQ0LtHiMXJPY+Os5pXc
lsYCRPOi3F6K0n4k1RjJ7PGp/9RhZy3XS1yn1Qlu4hoCClHcc9rPXPn4fQ==
-----END PUBLIC KEY-----

I execute this command to display the public key:

> openssl ec -in mykey.pub -pubin -text -noout
read EC key
Public-Key: (256 bit)
pub:
    04:4c:65:30:a3:03:44:7e:a4:34:2e:d1:e2:31:72:
    4f:63:e3:ac:e6:95:dc:96:c6:02:44:f3:a2:dc:5e:
    8a:d2:7e:24:d5:18:c9:ec:f1:a9:ff:d4:61:67:2d:
    d7:4b:5c:a7:d5:09:6e:e2:1a:02:0a:51:dc:73:da:
    cf:5c:f9:f8:7d
ASN1 OID: prime256v1
NIST CURVE: P-256

So my question is if I have this data

> openssl ec -in mykey.pub -pubin -text -noout
read EC key
Public-Key: (256 bit)
pub:
    04:4c:65:30:a3:03:44:7e:a4:34:2e:d1:e2:31:72:
    4f:63:e3:ac:e6:95:dc:96:c6:02:44:f3:a2:dc:5e:
    8a:d2:7e:24:d5:18:c9:ec:f1:a9:ff:d4:61:67:2d:
    d7:4b:5c:a7:d5:09:6e:e2:1a:02:0a:51:dc:73:da:
    cf:5c:f9:f8:7d
ASN1 OID: prime256v1
NIST CURVE: P-256

how to retrieve this in shell mode

-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAETGUwowNEfqQ0LtHiMXJPY+Os5pXc
lsYCRPOi3F6K0n4k1RjJ7PGp/9RhZy3XS1yn1Qlu4hoCClHcc9rPXPn4fQ==
-----END PUBLIC KEY-----

Solution

  • Let me first briefly describe your target format. What you are seeking to obtain is a PEM encoded SubjectPublicKeyInfo (SPKI) file. PEM encoding is essentially DER encoding (which is a binary format) and then base64 encoded with a header and footer. The SPKI structure is defined in RFC5280 (see section 4.1):

    https://www.rfc-editor.org/rfc/rfc5280#section-4.1

       SubjectPublicKeyInfo  ::=  SEQUENCE  {
            algorithm            AlgorithmIdentifier,
            subjectPublicKey     BIT STRING  }
    

    So the first chunk of bytes in the binary DER encoding consists of a header that identifies the algorithm being used (and part of that includes the curve). The last bytes are the raw public key (which is an encoded x and y co-ordinate on the curve).

    Since your sample key is for the same curve as the key you want to create it will have the same AlgorithmIdentifier header bytes. Taking your mykey.pub file we can convert it to the binary DER format:

    $ openssl ec -in mykey.pub -pubin -outform DER -out key.der
    

    Lets look at the contents:

    $ hexdump -C key.der
    00000000  30 59 30 13 06 07 2a 86  48 ce 3d 02 01 06 08 2a  |0Y0...*.H.=....*|
    00000010  86 48 ce 3d 03 01 07 03  42 00 04 4c 65 30 a3 03  |.H.=....B..Le0..|
    00000020  44 7e a4 34 2e d1 e2 31  72 4f 63 e3 ac e6 95 dc  |D~.4...1rOc.....|
    00000030  96 c6 02 44 f3 a2 dc 5e  8a d2 7e 24 d5 18 c9 ec  |...D...^..~$....|
    00000040  f1 a9 ff d4 61 67 2d d7  4b 5c a7 d5 09 6e e2 1a  |....ag-.K\...n..|
    00000050  02 0a 51 dc 73 da cf 5c  f9 f8 7d                 |..Q.s..\..}|
    0000005b
    

    You can see the first bytes of the public key that you printed out above starting at offset 0x1a (i.e. 26 bytes in): 04 4c 65 30 .... The raw public key data extends all the way the end of the file and is 65 bytes long. This consists of a leading 0x04 byte followed by 32 bytes of x co-ordinate and 32 bytes of y co-ordinate. The leading 0x04 tells us how the co-ordinate is represented. 0x04 means "uncompressed" - which is handy because that's the simplest for us to deal with. We will also use uncompressed format for our target key. Therefore we need to take the first 26 bytes of header plus the 0x04 byte (so 27 bytes in total) from our sample key:

    $ head -c 27 key.der >key.head
    

    And just to check we got what we were expecting:

    $ hexdump -C key.head
    00000000  30 59 30 13 06 07 2a 86  48 ce 3d 02 01 06 08 2a  |0Y0...*.H.=....*|
    00000010  86 48 ce 3d 03 01 07 03  42 00 04                 |.H.=....B..|
    0000001b
    

    Now we create the x and y elements of our co-ordinate, which you already did:

    $ echo -n "GCl++lQHb7NKYU3jXpKVI/BYaTlALT5JFPdl3sbB9mY=" | base64 -d >key.x
    $ echo -n "ADRX25PBSlZJE79drET0ARtRqZAkUIMNt9aa2bbjBYY=" | base64 -d >key.y
    

    And then put all the elements together:

    cat key.head key.x key.y >keynew.der
    

    We can convert the new key in DER format to PEM format:

    $ openssl ec -in keynew.der -inform DER -pubin -out keynew.pem
    

    Which gives us:

    -----BEGIN PUBLIC KEY-----
    MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEGCl++lQHb7NKYU3jXpKVI/BYaTlA
    LT5JFPdl3sbB9mYANFfbk8FKVkkTv12sRPQBG1GpkCRQgw231prZtuMFhg==
    -----END PUBLIC KEY-----
    

    And just to check it looks sane:

    $ openssl ec -in keynew.pem -pubin -noout -text
    read EC key
    Public-Key: (256 bit)
    pub:
        04:18:29:7e:fa:54:07:6f:b3:4a:61:4d:e3:5e:92:
        95:23:f0:58:69:39:40:2d:3e:49:14:f7:65:de:c6:
        c1:f6:66:00:34:57:db:93:c1:4a:56:49:13:bf:5d:
        ac:44:f4:01:1b:51:a9:90:24:50:83:0d:b7:d6:9a:
        d9:b6:e3:05:86
    ASN1 OID: prime256v1
    NIST CURVE: P-256