OpenSSH public key file format?

I am having trouble parsing an OpenSSH public key file. I believe (but I am not certain) the format is detailed in RFC 4253, The Secure Shell (SSH) Transport Layer Protocol Section 6.6, Public Key Algorithms.

In the case of a RSA key, the RFC says:

The "ssh-rsa" key format has the following specific encoding:

string    "ssh-rsa"
mpint     e
mpint     n

Here the 'e' and 'n' parameters form the signature key blob.

Here is where the problems begin. The document does not provide a grammar, and does not define what string and mpint are. Which leads to:

$ cat rsa.ssh.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDSNM6RVVmwN3y0NurIQnmZgjcx5K5zzZu9nDqopW4J
In/mr8OYZI3heSJShnIM8EThvwVGXXXyyJVRQAvRHYFO4DxS6bufSNWr3BxBGaGYlYxI9mgvQnT6+MzE
3oZyEMdQNPlV5VfbileXlrPoAk1TkGdVdhwdLJMI2B4KUyMf+Q== jwalton@test

And then:

$ echo 'AAAAB3NzaC1yc2EAAAADAQABAAAAgQDSNM6RVVmwN3y0NurIQnmZgjcx5K5zzZu9nDqopW4
JIn/mr8OYZI3heSJShnIM8EThvwVGXXXyyJVRQAvRHYFO4DxS6bufSNWr3BxBGaGYlYxI9mgvQnT6+M
zE3oZyEMdQNPlV5VfbileXlrPoAk1TkGdVdhwdLJMI2B4KUyMf+Q==' | base64 -d > rsa.bin

And finally:

$ hexdump -C rsa.bin
00000000  00 00 00 07 73 73 68 2d  72 73 61 00 00 00 03 01  |....ssh-rsa.....|
00000010  00 01 00 00 00 81 00 d2  34 ce 91 55 59 b0 37 7c  |........4..UY.7||
00000020  b4 36 ea c8 42 79 99 82  37 31 e4 ae 73 cd 9b bd  |.6..By..71..s...|
00000030  9c 3a a8 a5 6e 09 22 7f  e6 af c3 98 64 8d e1 79  |.:..n.".....d..y|
00000040  22 52 86 72 0c f0 44 e1  bf 05 46 5d 75 f2 c8 95  |"R.r..D...F]u...|
00000050  51 40 0b d1 1d 81 4e e0  3c 52 e9 bb 9f 48 d5 ab  |[email protected].<R...H..|
00000060  dc 1c 41 19 a1 98 95 8c  48 f6 68 2f 42 74 fa f8  |..A.....H.h/Bt..|
00000070  cc c4 de 86 72 10 c7 50  34 f9 55 e5 57 db 8a 57  |....r..P4.U.W..W|
00000080  97 96 b3 e8 02 4d 53 90  67 55 76 1c 1d 2c 93 08  |.....MS.gUv..,..|
00000090  d8 1e 0a 53 23 1f f9                              |...S#..|
00000097

So there seems to be undocumented fields in the public key file. The RFC does not appear to refer to other documents for the definitions of the fields. The RFC also fails to document the private key file. I am stalled at the moment.

Where does OpenSSH define the fields used in its key files?


Solution 1:

So there seems to be undocumented fields in the public key file. The RFC does not appear to refer to other documents for the definitions of the fields.

They are defined in RFC 4251 "The Secure Shell (SSH) Protocol Architecture", section 5.

The RFC also fails to document the private key file.

The SSH protocol does not document any file formats at all. It only defines serialization of public keys when they are sent as part of the SSH protocol (e.g. when a client sends SSH­_MSG­_USER­AUTH to offer its public key).

So because the private key is never sent over the network as part of SSH protocol, its serialization does not need to be part of the spec either – only the signatures made by that key need to have a defined format.

Where does OpenSSH define the fields used in its key files?

For public keys, OpenSSH most likely has chosen to re-use the same RFC 4253 format for storing them in files because it's the most convenient option (i.e. it already has the serialization code anyway). It's not required to do so by spec, and indeed most other clients have their own formats.

Because OpenSSH uses OpenSSL for the cryptographic code (algorithms, key generation), previous versions of OpenSSH simply stored private keys using whatever format the OpenSSL functions offered – which was DER-serialized PKCS#1 'RSAPrivateKey' format (also commonly known as PEM format) most of the time. See RFC 3447 for the ASN.1 definition of the format.

(OpenSSL itself now prefers storing private keys in PKCS#8 format, which means OpenSSH can load those keys as well, although it does not write them. See RFC 5208 for the ASN.1 definition of the container format.)

You can recognize the PKCS#1 format by the "BEGIN RSA PRIVATE KEY" header, and PKCS#8 by the "BEGIN PRIVATE KEY" header. You can use dumpasn1 or openssl asn1parse to investigate their contents, as well as openssl rsa and openssl pkey.

Recent versions of OpenSSH have invented a new, custom format for private key files. The container format is documented in PROTOCOL.key, and the individual key formats are [probably?] the same as used by ssh-agent, which is documented in draft-miller-ssh-agent. This format uses the RFC 4251 data types.


Other SSH software often has different formats. For example, PuTTY uses "PPK" format (which is documented somewhere, I remember, but oddly I can't find where) for storing both public and private keys. Its Public-Lines field stores the same RFC 4253 public key, while the private fields are custom.

There is also RFC 4716, which claims to be "The SSH Public Key Format", but it is not generally considered to be an integral part of SSH. (SSH.COM, SecureCRT, and probably MultiNet SSH use this format.)