Anton Johansson

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]:

  1. 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].
  2. The second part AAAAC3NzaC1lZDI1NTE5AAAAIDuydlSZR2LNo2jXavRFkyb3w2J/dADSfAS/qaCqza1d is the key, which we will soon get into.
  3. 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.