如何通过 openssl 将 jwk 转换为 public 键

How to transform a jwk to public key over openssl

我有一个 JWE,在 header 中有一个临时的 public 密钥。 所以我有坐标 X 和 Y。

我在 shell 模式下的问题是如何将 JWK 转换为 pem 格式的 ECC public 密钥。

比如这里有一个jwk

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

在我这样做之后

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

所以 public 键是 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

但是我有曲线,public键,我想知道如何生成pem?

换句话说,

我有一个文件 mykey.pub,其中包含:

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

我执行这条命令来显示public键:

> 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

所以我的问题是我是否有这些数据

> 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

如何在 shell 模式下检索此内容

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

首先让我简要描述一下您的目标格式。您要获取的是 PEM 编码的 SubjectPublicKeyInfo (SPKI) 文件。 PEM 编码本质上是 DER 编码(这是一种二进制格式),然后使用 header 和页脚进行 base64 编码。 SPKI 结构在 RFC5280 中定义(参见第 4.1 节):

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

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

因此二进制 DER 编码中的第一个字节块包含一个 header,它标识正在使用的算法(其中一部分包括曲线)。最后一个字节是原始 public 密钥(这是曲线上编码的 x 和 y co-ordinate)。

由于您的示例密钥与您要创建的密钥使用相同的曲线,因此它将具有相同的 AlgorithmIdentifier header 字节。使用您的 mykey.pub 文件,我们可以将其转换为二进制 DER 格式:

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

来看内容:

$ 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

您可以看到上面打印出的 public 密钥的第一个字节从偏移量 0x1a 开始(即 26 个字节):04 4c 65 30 ...。原始 public 密钥数据一直延伸到文件末尾,长度为 65 字节。这包括一个前导的 0x04 字节,后跟 32 个字节的 x co-ordinate 和 32 个字节的 y co-ordinate。前导 0x04 告诉我们 co-ordinate 是如何表示的。 0x04 表示“未压缩”——这很方便,因为这对我们来说是最容易处理的。我们还将为我们的目标密钥使用未压缩格式。因此,我们需要从示例密钥中取出 header 的前 26 个字节加上 0x04 字节(因此总共 27 个字节):

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

只是为了检查我们是否得到了我们所期望的:

$ 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

现在我们创建 co-ordinate 的 x 和 y 元素,您已经这样做了:

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

然后把所有的元素放在一起:

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

我们可以将 DER 格式的新密钥转换为 PEM 格式:

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

这给了我们:

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

只是为了检查它是否正常:

$ 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