Let's walk through it. First, we generate a private key in Ed25519:
$ openssl genpkey -algorithm ED25519 -out ed25519.pem
$ cat ed25519.pem
-----BEGIN PRIVATE KEY-----
-----END PRIVATE KEY-----
We read the man page on
-i This option will read an unencrypted private (or public)
key file in the format specified by the -m option and print
an OpenSSH compatible private (or public) key to stdout.
and think, "oh, so we just do this":
$ ssh-keygen -i -f ed25519.pem -m pkcs8
do_convert_from_pkcs8: ed25519.pem is not a recognised public key format
Nope, maybe the documentation lies and the error is instructive.
So, we grab the public key corresponding to that private key:
$ openssl pkey -in ed25519.pem -pubout > ed25519.pub
$ cat ed25519.pub
-----BEGIN PUBLIC KEY-----
-----END PUBLIC KEY-----
and try again, this time giving the public key:
$ ssh-keygen -i -f ed25519.pub -m pkcs8
do_convert_from_pkcs8: unsupported pubkey type 1087
Dang it. Why?!
Well, it's a bug:
OpenSSH doesn't currently support reading or writing Ed25519 keys in any format other than the OpenSSH native key format.
Not all libcrypto implementations support Ed25519 keys, in particular LibreSSL does not.
This patch adds support for reading PKCS8 Ed25519 keys on recent OpenSSL, but it can't be upstreamed until LibreSSL supports these keys too.
So, full stop, Ed25519 support is incomplete in the
openssl available upstream (which is what we're familiar with in Linux, for example).
We have two possible choices:
- Compile a patched version of Portable OpenSSH and convert using the command above; or,
- Use the alternative tool mentioned by the bug reporter,
sshpk, to convert.
Let's try the latter, since it's just a NPM package:
$ npm install -g sshpk
$ sshpk-conv ed25519.pem -t ssh -p
-----BEGIN OPENSSH PRIVATE KEY-----
-----END OPENSSH PRIVATE KEY-----
and there we go: the private key in OpenSSH format.