The OpenSSH Private Key Format
Published 2018-12-5Traditionally OpenSSH has used the OpenSSL-compatible formats PKCS#1 (for RSA) and SEC1 (for EC) for Private keys.
This week I discovered that it now has its own format too,
which is the default output format for some installations of ssh-keygen
.
After peeking at the binary I found, much to my dismay - and very much unlike the ssh public key format (RFC 4253) - that OpenSSH private key format is not intuitively obvious, I headed to les googles.
I searched high and low (or at least past page 2, which is a distinguished mark of true dedication), but found no useful information to assauge my curiosity (and habit).
In lieu of the docs I turned to the source. With a combination of the concentrated efforts of my best code sluething and reverse engineering skills, I believe I have (here below) produced the most complete documentation the Internet has to offer on the subject.
So, without further ado...
OpenSSH Private Keys
On the outside it's PEM encoded. It looks like this:
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS
1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQR9WZPeBSvixkhjQOh9yCXXlEx5CN9M
yh94CJJ1rigf8693gc90HmahIR5oMGHwlqMoS7kKrRw+4KpxqsF7LGvxAAAAqJZtgRuWbY
EbAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBH1Zk94FK+LGSGNA
6H3IJdeUTHkI30zKH3gIknWuKB/zr3eBz3QeZqEhHmgwYfCWoyhLuQqtHD7gqnGqwXssa/
EAAAAgBzKpRmMyXZ4jnSt3ARz0ul6R79AXAr5gQqDAmoFeEKwAAAAOYWpAYm93aWUubG9j
YWwBAg==
-----END OPENSSH PRIVATE KEY-----
But, unlike most PEMs, there's no DER inside.
Instead it's the "proprietary" OpenSSH format, which looks like this:
"openssh-key-v1"0x00 # NULL-terminated "Auth Magic" string
32-bit length, "none" # ciphername length and string
32-bit length, "none" # kdfname length and string
32-bit length, nil # kdf (0 length, no kdf)
32-bit 0x01 # number of keys, hard-coded to 1 (no length)
32-bit length, sshpub # public key in ssh format
32-bit length, keytype
32-bit length, pub0
32-bit length, pub1
32-bit length for rnd+prv+comment+pad
64-bit dummy checksum? # a random 32-bit int, repeated
32-bit length, keytype # the private key (including public)
32-bit length, pub0 # Public Key parts
32-bit length, pub1
32-bit length, prv0 # Private Key parts
... # (number varies by type)
32-bit length, comment # comment string
padding bytes 0x010203 # pad to blocksize (see notes below)
As you can see (maybe) there's
- A format ID prefix
- Encryption headers
- An unused number for number of keys in the block
- An rfc4253-style ssh public key
- An private key somewhat modeled after the rfc4253 style
- A comment
- Padding for aligning private key to the blocksize
Note that the blocksize
is 8 (for unencrypted keys, at least).
The RFC 4253 SSH Public Key format, is used for both the embedded public key and embedded private key key, with the caveat that the private key has a header and footer that must be sliced:
- RSA private keys swap
e
andn
forn
ande
. - 8 bytes of unused checksum bytes as a header
- n bytes (between 0 and 7) of padding
- bytes > 0x00 and < 0x08 must be trimmed (from the right)
- the padding must be a (right-trimmed) substring of
0x01020304050607
- (that includes the empty substring)
- if the last byte isn't padding, it's part of the comment (0x21 to 0x7e)
Reference Material
The canonical source code is only available via tarball (.tar.gz).
However, there's also a well-maintained fork (Portable OpenSSH) which has perfectly linkable source code and among them I found this to be the file of greatest interest:
https://github.com/openssh/openssh-portable/blob/master/sshkey.c
Even more particularly, these were the most interesting functions:
sshkey_parse_private2
sshkey_private_deserialize
sshkey_private_serialize_opt
I don't quite remember where, but another piece of information I
discovered is that when the key isn't encrypted (cipher and kdf
values are "none" and "none") the blocksize
is 8 bytes and the
value of CLFLAG_NONE is also 8:
blocksize = 8
CFLAG_NONE = 00001000
By AJ ONeal
Did I make your day?
Buy me a coffee
(you can learn about the bigger picture I'm working towards on my patreon page )