3

I generated a private key using libressl 3.0 using curve secp521r1.

openssl genpkey -aes128 -algorithm EC -pkeyopt ec_paramgen_curve:secp521r1 -pkeyopt ec_param_enc:named_curve

As far as I know this curve is the same that ssh-keygen uses with ssh-keygen -t ecdsa -b 521 and from the documentation I know ssh-keygen now uses aes128 to encrypt the private key.

The size of the libressl private key is 534 and the size of the ssh-keygen one is 736.

My question is: what does ssh-keygen does extra that increments the size of the key?

Also: are both exchangable? can one private key generated in one be converted to the other?

1 Answer 1

4

TLDR: there are many different formats for privatekey files

And it depends partly on the version of OpenSSH, which you didn't give.

Historically, except for long-obsolete protocol version 1 (and key type 'rsa1'), OpenSSH used (the libcrypto part of) OpenSSL to do most of its crypto operations -- including storing privatekeys (or in effect keypairs) in files, using the 'traditional' or 'legacy' formats defined by SSLeay/OpenSSL. OpenSSL also supports 'new' (since about 1998!) PKCS8 formats, which are (somewhat) more secure. There are thus 4 PEM formats supported by OpenSSL for "ECDSA" (more exactly X9-style Weierstrass EC which covers both X9.62 ECDSA and X9.63 ECDH) of which OpenSSH generates only 2 but (using libcrypto) can read all. When ssh-keygen encrypts OpenSSL-format files, recent versions do use AES-128-CBC, but this has not always been true.

Starting with release 6.5 in 2014-01, OpenSSH added its own 'new' file format, partly to provide better security than the OpenSSL legacy formats (when encrypted) and partly to support the then-new algorithm Ed25519, which OpenSSL did not then support at all. ssh-keygen had an option -o to request new format for any keytype other than ed25519 (or rsa1), and you will find many Stack Qs and As from the last several years referencing, and usually recommending, this option. 'New' format files are encrypted with AES256-CBC by default, which can be overridden with -Z ciphername, but the man page has apparently not been updated for either of these. Starting with release 7.8 in 2018-08, OpenSSH now defaults to 'new' format for all keytypes, although you can use -m pem to request OpenSSL format for anything other than ed25519. If you check man ssh-keygen on your system (unless it's Windows) it should describe one of these cases, probably the latter.

(There is also PKCS12 aka PFX format, which OpenSSL can use for a privatekey, but is normally used only to combine a privatekey (or several) with one or more X.509 certificate(s) for that(those) key(s), which is a different situation and not applicable to SSH, which never uses X.509 certificates. Other programs or systems use other formats: 'commercial'/Tectia SSH has its own format, and PuTTY has its own format PPK = PuTTY Private Key. Et cetera, et cetera, Yul Brynner. All of these are semantically equivalent, and given suitable tools can be interconverted.)

All of the privatekey formats used by OpenSSH are PEM-style; the binary data is encoded into text as base64 with header and trailer lines in the form

-----BEGIN (something)-----
(sometimes some headers here)
(data encoded in base64, broken into lines of 64 characters)
-----END (something)-----

so you can tell which format you have just by looking at that dashes-BEGIN line. See near-crossdupes https://security.stackexchange.com/questions/39279/stronger-encryption-for-ssh-keys or more briefly https://security.stackexchange.com/questions/129724/how-to-check-if-an-ssh-private-key-has-passphrase-or-not and https://security.stackexchange.com/questions/200935/how-do-i-determine-if-an-existing-ssh-private-key-is-secure (mine).

The differences: the size of 736 chars for an ecdsa-p521 key matches the OpenSSH new format unencrypted -- are you sure you gave it a passphrase? -- and 534 matches the PKCS8-encrypted format created by OpenSSL 1.0.2, which IIRC is when LibreSSL forked. (OpenSSL 1.1.0 up changes the PKCS8 encryption to use HMAC-SHA256 in PBKDF2, which makes the file slightly bigger. OpenSSL 1.1.0 up also makes param_enc=named the default so you no longer need to specify it.) The OpenSSH new format is larger than the PKCS8-enc format mostly because, for no obvious reason, it stores the publickey value (for ECDSA a curve point in X9.62 format) twice -- once in the section of the file specified for publickey, and again in the section specified for privatekey. In addition OpenSSH format has most of its metadata as text strings, and all length fields 4 bytes, while PKCS8 (like the OpenSSL legacy formats) uses ASN.1 with variable-length length fields mostly only 1 byte, and metadata mostly binary, especially 'OIDs' for the algorithms used.)

Also: are both exchangable? can one private key generated in one be converted to the other?

Yes and no. As noted OpenSSH using OpenSSL can read (but not write) PKCS8, so ssh-keygen can convert it to OpenSSH-new by making a null change to it, i.e. 'changing' the password to the existing value; from 6.5 to 7.7 you must specify -o and from 7.8 up it is the default. Going the other way, ssh-keygen can convert to OpenSSL-legacy, but not directly to PKCS8, you need to use openssl to do that, either explicitly with pkcs8 -topk8 or in 1.0.0 up (which by now should be everybody, and definitely all LibreSSL) just pkey. (The former defaults to encrypted and the latter to unencrypted, but both can be overridden.)

1
  • thank you for the comprehensive answer, I am learning a lot
    – Vicfred
    Aug 15, 2019 at 5:17

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .