i have generated an ed2215 private key as follows:

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

and it looks like


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)

  • $\begingroup$ "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 $\endgroup$
    – poncho
    Jul 6, 2019 at 21:31
  • 2
    $\begingroup$ 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 $\endgroup$ Jul 6, 2019 at 23:08

1 Answer 1


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
    char    padlen % 255

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.
  • 2
    $\begingroup$ And for the algorithm-specific Ed25519 keys within this OpenSSH generic format, see RFC8032. $\endgroup$ Jul 6, 2019 at 23:13
  • $\begingroup$ even though it's not marked as such, this new format is encrypted form of the private key right? $\endgroup$
    – Woodstock
    Dec 6, 2019 at 15:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.