How to transform an OpenSSH key into JWK
In this post I'll demonstrate the steps to transform one of the keys linked to my github account from the OpenSSH
format into a JWK
. The key is an Ed25519
key, which has been rising in popularity lately.
The OpenSSH format
My github key in the OpenSSH
format looks like this
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDuydlSZR2LNo2jXavRFkyb3w2J/dADSfAS/qaCqza1d
Let's break down the contents of the key as defined in "The Secure Shell (SSH) Transport Layer Protocol" [RFC4253]:
- The first part ("
ssh-ed25519
") identifies the key format. This specifies how the key is encoded, which is covered in "Ed25519 and Ed448 Public Key Algorithms for the Secure Shell (SSH) Protocol" [RFC8709]. - The second part
AAAAC3NzaC1lZDI1NTE5AAAAIDuydlSZR2LNo2jXavRFkyb3w2J/dADSfAS/qaCqza1d
is the key, which we will soon get into. - A third optional part may be included. This part is a comment, and not relevant in this case.
Parsing the key
Let's drill into the structure of the key according to [RFC8709]
The "ssh-ed25519" key format has the following encoding:
string "ssh-ed25519"
string key
Simple enough. It's important to note that the definition of a string
here is according to the SSH Protocol Architecture [RFC4521]
string
[...]
They are stored as a uint32 containing its length
(number of bytes that follow) and zero (= empty string) or more
bytes that are the value of the string. Terminating null
characters are not used.
So we need to keep in mind that the length is prefixed in a string
.
Converting the key (the second part of the file content) from base 64 to hex yields
# 10 as uint32, the length of "ssh-ed25519"
00 00 00 0b
# The string "ssh-ed25519"
73 73 68 2d 65 64 32 35 35 31 39
# 32 as uint32, which is the length of the key
00 00 00 20
# Key bytes
3b b2 76 54 99 47 62 cd a3 68 d7 6a f4 45 93 26 f7 c3 62 7f 74 00 d2 7c 04 bf a9 a0 aa cd ad 5d
My raw key bytes are then
3b b2 76 54 99 47 62 cd a3 68 d7 6a f4 45 93 26
f7 c3 62 7f 74 00 d2 7c 04 bf a9 a0 aa cd ad 5d
Assembling the JWK
The last step is to encode these bytes into URL-safe base 64, and put the resulting string in a JWK JSON body along with the required parameters as defined in [RFC8037]
{
"crv": "Ed25519",
"kty": "OKP",
"x": "O7J2VJlHYs2jaNdq9EWTJvfDYn90ANJ8BL-poKrNrV0"
}
Presto, we have the same key material represented as a JWK.