openssh ed2215 private key format

i have generated an ed2215 private key as follows:

\$ ssh-keygen -o -a 100 -t ed25519 -f id_ed25519


and it looks like

-----BEGIN OPENSSH PRIVATE KEY-----
...
-----END OPENSSH PRIVATE KEY-----


where ... is truncated data.

although it feels like base64, i see that each truncated line has at most 64 characters, where regular base64 encoding does not hold that charactristics.

what is the format (or encoding) of the private key? (if i have the private key in byte array, which transformation do i need to apply before i write it to a file that coincide with openssh output)

• "like base64, i see that each truncated line has at most 64 characters"; actually, base64 has 64 different characters (not counting whitespace, which is ignored) - the number of characters per line is unimportant Jul 6, 2019 at 21:31
• Base64 is used in many formats under many standards but this format, with the dashes-BEGIN,END-type lines was originated by PEM and does require lines broken/wrapped (not truncated) every 64 characters; see tools.ietf.org/html/rfc1421#page-13 and tools.ietf.org/html/rfc7468#page-5 Jul 6, 2019 at 23:08

although it feels like base64, i see that each truncated line has at most 64 characters

It is base64 encoded. Remove the -----BEGIN OPENSSH PRIVATE KEY----- and -----END OPENSSH PRIVATE KEY----- and the new lines ("\n") and then you can parse as per https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.key?annotate=HEAD . Quoting that file:

This document describes the private key format for OpenSSH.

1. Overall format

The key consists of a header, a list of public keys, and
an encrypted list of matching private keys.

#define AUTH_MAGIC      "openssh-key-v1"

byte[]  AUTH_MAGIC
string  ciphername
string  kdfname
string  kdfoptions
int number of keys N
string  publickey1
string  publickey2
...
string  publickeyN
string  encrypted, padded list of private keys

2. KDF options for kdfname "bcrypt"

The options:

string salt
uint32 rounds

are concatenated and represented as a string.

3. Unencrypted list of N private keys

The list of privatekey/comment pairs is padded with the
bytes 1, 2, 3, ... until the total length is a multiple
of the cipher block size.

uint32  checkint
uint32  checkint
string  privatekey1
string  comment1
string  privatekey2
string  comment2
...
string  privatekeyN
string  commentN
char    1
char    2
char    3
...

Before the key is encrypted, a random integer is assigned
to both checkint fields so successful decryption can be
quickly checked by verifying that both checkint fields
hold the same value.

4. Encryption

The KDF is used to derive a key, IV (and other values required by
the cipher) from the passphrase. These values are then used to
encrypt the unencrypted list of private keys.

5. No encryption

For unencrypted keys the cipher "none" and the KDF "none"
are used with empty passphrases. The options if the KDF "none"
are the empty string.

• And for the algorithm-specific Ed25519 keys within this OpenSSH generic format, see RFC8032. Jul 6, 2019 at 23:13
• even though it's not marked as such, this new format is encrypted form of the private key right? Dec 6, 2019 at 15:41