9

I'm having a problem where when I export a key from 1password, and use it with ssh -i, I get

Load key "/home/user/.ssh/private_ed25519": invalid format

Doing a google search on this, it seems to be a problem where 1password stores their ED25519 key in PKCS#8, from their developer

Copying a private key will copy it in PKCS#8--the format 1Password stores the key. Unfortunately, OpenSSH does not support ED25519 keys in PKCS#8, only in OpenSSH format. Downloading a key will convert it to OpenSSH format. The reason RSA keys work is because OpenSSH does support RSA keys in PKCS#8.

They suggest using a "Download" feature, but I don't have that feature and many other people also don't have that feature. But there should be more than one way to skin this cat,

You can create an ed25519 keyfile in PKCS#8 with,

openssl genpkey -algorithm ed25519 -out private.pem

Is there any method to convert a private Ed25519 key (private.pem in the above) from PKCS#8 to the OpenSSH format?

When I try, with ssh-keygen, I get

$ ssh-keygen -i -f private.pem -m pkcs8
do_convert_from_pkcs8: private.pem is not a recognised public key format
0

4 Answers 4

17
+50

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-----
MC4CAQAwBQYDK2VwBCIEID+U9VakauO4Fsv4b/znpDHcdYg74U68siZjnWLPn7Q1
-----END PRIVATE KEY-----

We read the man page on ssh-keygen -i:

 -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-----
MCowBQYDK2VwAyEA0IgiJOLeGDwumzkm+wi82nP7wvmbnaoWKmNNHMaq/Mw=
-----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:

  1. Compile a patched version of Portable OpenSSH and convert using the command above; or,
  2. 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-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACDQiCIk4t4YPC6bOSb7CLzac/vC+ZudqhYqY00cxqr8zAAAAJAyR1o6Mkda
OgAAAAtzc2gtZWQyNTUxOQAAACDQiCIk4t4YPC6bOSb7CLzac/vC+ZudqhYqY00cxqr8zA
AAAEA/lPVWpGrjuBbL+G/856Qx3HWIO+FOvLImY51iz5+0NdCIIiTi3hg8Lps5JvsIvNpz
+8L5m52qFipjTRzGqvzMAAAAC2VkMjU1MTkucGVtAQI=
-----END OPENSSH PRIVATE KEY-----

and there we go: the private key in OpenSSH format.

3
  • 1
    Wow, I would have bountied this more for this quality of answer. Thanks a ton all around. The only follow-up question I have is wtf does a patch to openssl require landing in libressl too? If they're different projects, why does one require support in the other? Commented Jan 17, 2023 at 15:55
  • 1
    May also be worth pointing out that it looks like LibreSSL got ed25519 on December 12, when 3.7 landed ftp.openbsd.org/pub/OpenBSD/LibreSSL/… Commented Jan 17, 2023 at 16:21
  • 1
    (@EvanCarroll) and as the now-updated bug indicates, OpenSSH supports it as of 9.6 in Dec. 2023 Commented Jan 10 at 4:00
4

Not my (original) work, copied from another thread when I was trying to find out something related (bugs fixed / added by me)

-- a shell script that converts openssl-generated ed25519 private key to a format understood by openssh

tested the following way:

$ openssl genpkey -algorithm ED25519 -out ed25519.pem
$ ./eddsa-ssl-to-ossh.sh ed25519.pem > okey
$ ssh-keygen -l -f okey
#!/bin/sh

set -euf

ssl_priv=$(cat ${1:+"$1"})

pub64=$(echo "$ssl_priv" | openssl pkey -pubout -outform der 2>/dev/null | dd bs=12 skip=1 status=none | base64)

test "$pub64" || { echo "Cannot get public key" >&2; exit 1; }

priv64=$(echo "$ssl_priv" | grep -v '^-' | base64 -d | dd bs=16 skip=1 status=none | base64)

echo '-----BEGIN OPENSSH PRIVATE KEY-----'

{
    printf openssh-key-v1'\000\000\000\000\004'none'\000\000\000\004'none'\000\000\000\000\000\000\000\001\000\000\000'3
    printf '\000\000\000\013'ssh-ed25519'\000\000\000 '
    echo $pub64 | base64 -d
    printf '\000\000\000'
    printf '\210\000\000\000\000\000\000\000\000'
    printf '\000\000\000\013'ssh-ed25519'\000\000\000 '
    echo $pub64 | base64 -d
    printf '\000\000\000@'
    echo $priv64| base64 -d
    echo $pub64 | base64 -d
    printf '\000\000\000\000\001\002\003\004\005'
} | base64

echo '-----END OPENSSH PRIVATE KEY-----'
2
  • Hi @Tomi, I know you provided your answer 6 months ago but can you explain where are those printf lines coming from? Or if you can point me to the right place so I'll find out myself.. Many thanks in advance!
    – Greg0ry
    Commented Aug 26, 2023 at 22:54
  • 1
    I don't think I ever got explanation -- It was a while before I wrote that comment I saw the text in some stack* page (the above was converted (back) from one perl program where I used it). Most of those separated substrings crearly contain length parameters (also ' ' for 32 and @ for 64).
    – Tomi
    Commented Nov 6, 2023 at 11:20
0

Since this question is tagged with 1Password and seems to be specifically related to Ed25519 keys in 1Password, not converting an Ed25519 key encoded pkcs8 to the OpenSSH format in general, I thought I could provide an alternative answer. This answer assumes OP is using the 1Password CLI app given the link in the question refers to 1Password service accounts which are intended for use on the CLI app (note that this will work for standard accounts too).

Using the 1Password CLI app

Instead of using a secret reference that you usually copy from the 1Password UI app that comes in the format of op://{vault_name}/{item_name}/{field_name}, instead you can copy the item's UUID by right-clicking it in the UI app and selecting the "Copy item UUID" option, then use:

op item get "item_uuid" --reveal --fields 'label=private key'

this will print the private key in the normal OpenSSH format you'd expect and that ssh-add will accept. Of course, you'd have to replace item_uuid with the UUID you copied from the app.

Using the Windows 1Password CLI app through WSL

If you'd like to use this with op.exe (the Windows CLI app for 1Password), but inside WSL, then there is some additional work to remove Windows's standard line endings (or else you'll still get the invalid format error). For that, you can pipe it through sed on the Linux side using:

op.exe item get "item_uuid" --reveal --fields 'label=private key' | sed -e '/^"/d' -e 's/\r//g'

Again, you'd still have to replace item_uuid with the UUID you copied from the app and then you can proceed to use it as you wish.

2
  • I think OP uses Linux and not Windows with or without WSL Commented Apr 2 at 19:52
  • Yeah, that was more of a bonus inclusion as it was the situation I found myself in and thought it may still help someone else.
    – PurplProto
    Commented Apr 3 at 8:55
-1

Yes, there is a method to convert a private Ed25519 key from PKCS#8 to the OpenSSH format. You can use the ssh-keygen command-line tool that comes with OpenSSH to convert the key. The basic syntax of the command is as follows:

ssh-keygen -i -f keyfile.pem -m pkcs8

Where keyfile.pem is the file name of your PKCS#8 private key. The -i flag tells ssh-keygen to import a key, the -f flag specifies the input file and the -m flag specifies the key format as pkcs8.

When you run this command, ssh-keygen will convert the key and output it to the terminal. You can then redirect the output to a file to save it in the OpenSSH format:

ssh-keygen -i -f keyfile.pem -m pkcs8 > openssh-keyfile.pem

If you're trying to convert the key to be used in ssh-agent or ssh-add, you'll have to remove the passphrase from the key first, otherwise it will still prompt you for it when adding the key to the agent

The below command will prompt you to enter a new passphrase:

ssh-keygen -i -f keyfile.pem -m pkcs8 | ssh-keygen -p -m pem
1
  • 2
    This doesn't work on ed25519 keys. Try it openssl genpkey -algorithm ed25519 -out keyfile.pem; ssh-keygen -i -f keyfile2.pem -m pkcs8 Commented Jan 16, 2023 at 17:37

You must log in to answer this question.

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