I generated an ED25519 key pair using OpenSSL as follows:
openssl genpkey -algorithm ed25519 -out private.pem
openssl pkey -in private.pem -pubout -out public.pem
Based on this answer, I managed to extract the 32 bytes ED25519 key from the private PEM file with:
openssl asn1parse -in private.pem -offset 14
Output:
0:d=0 hl=2 l= 32 prim: OCTET STRING [HEX DUMP]:1E9D38B5274152A78DFF1A86FA464CEADC1F4238CA2C17060C3C507349424A34
So the 32 byte key (encoded as 64 hex characters) is:
1E9D38B5274152A78DFF1A86FA464CEADC1F4238CA2C17060C3C507349424A34
How can I do the same for the PEM public file public.pem?
And how could I create a valid public PEM file for that 32-byte key?
Note: My ultimate goal is to create a public ED25519 PEM file from a known 32 byte public ED25519 key. (without knowing the private key)
OpenSSL provides a built-in method to print out the 32-byte hex keys for both private and public keys, whether they are in the DER or PEM format:
openssl pkey [-pubin] [-inform pem/der] -in [KEYFILE] [-noout] -text
The main options are:
See the OpenSSL pkey documentation for more details.
Example usage:
##### Print out 32-byte keys in hex for private/public DER/PEM keys:
echo "=== Private DER key ==="
openssl pkey -inform der -in private.der -noout -text
echo "=== Private PEM key ==="
openssl pkey -inform pem -in private.pem -noout -text
echo "=== Public DER key ==="
openssl pkey -pubin -inform der -in public.der -noout -text
echo "=== Public PEM key ==="
openssl pkey -pubin -inform pem -in public.pem -noout -text
Corresponding output:
=== Private DER key ===
ED25519 Private-Key:
priv:
b7:4e:b3:2c:d8:05:ed:82:a3:58:0e:fa:59:21:cd:
b2:0a:ad:7f:4b:f6:41:09:05:7e:84:61:11:55:5e:
a3:e9
pub:
d1:64:89:3b:1c:e1:17:17:ab:48:22:4f:fe:26:10:
2e:9c:9c:13:61:36:90:ee:ed:38:29:ab:a6:7d:6f:
b7:87
=== Private PEM key ===
ED25519 Private-Key:
priv:
b7:4e:b3:2c:d8:05:ed:82:a3:58:0e:fa:59:21:cd:
b2:0a:ad:7f:4b:f6:41:09:05:7e:84:61:11:55:5e:
a3:e9
pub:
d1:64:89:3b:1c:e1:17:17:ab:48:22:4f:fe:26:10:
2e:9c:9c:13:61:36:90:ee:ed:38:29:ab:a6:7d:6f:
b7:87
=== Public DER key ===
ED25519 Public-Key:
pub:
d1:64:89:3b:1c:e1:17:17:ab:48:22:4f:fe:26:10:
2e:9c:9c:13:61:36:90:ee:ed:38:29:ab:a6:7d:6f:
b7:87
=== Public PEM key ===
ED25519 Public-Key:
pub:
d1:64:89:3b:1c:e1:17:17:ab:48:22:4f:fe:26:10:
2e:9c:9c:13:61:36:90:ee:ed:38:29:ab:a6:7d:6f:
b7:87
If you leave out the -noout
option, you simply also get the PEM (ASCII version) of the key printed out as well.
Example:
$ openssl pkey -pubin -inform pem -in public.pem -text
-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEA0WSJOxzhFxerSCJP/iYQLpycE2E2kO7tOCmrpn1vt4c=
-----END PUBLIC KEY-----
ED25519 Public-Key:
pub:
d1:64:89:3b:1c:e1:17:17:ab:48:22:4f:fe:26:10:
2e:9c:9c:13:61:36:90:ee:ed:38:29:ab:a6:7d:6f:
b7:87
$ cat public.pem
-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEA0WSJOxzhFxerSCJP/iYQLpycE2E2kO7tOCmrpn1vt4c=
-----END PUBLIC KEY-----
If you need to store the 32-byte keys in a variable, it can be more convenient to convert the key files to the binary DER format and then use xxd to extract them. The 32-byte keys are simply the last 32 bytes of the DER files, whether it is the private or public key.
Here we use the openssl pkey
utility again:
# For private keys
openssl pkey -inform pem -in private.pem -outform der -out private.der
# For public keys
openssl pkey -pubin -inform pem -in public.pem -pubout -outform der -out public.der
New relevant options:
Here is what the contents of the private and public DER files look like in hexadecimal:
$ xxd private.der
00000000: 302e 0201 0030 0506 032b 6570 0422 0420 0....0...+ep.".
00000010: b74e b32c d805 ed82 a358 0efa 5921 cdb2 .N.,.....X..Y!..
00000020: 0aad 7f4b f641 0905 7e84 6111 555e a3e9 ...K.A..~.a.U^..
$ xxd public.der
00000000: 302a 3005 0603 2b65 7003 2100 d164 893b 0*0...+ep.!..d.;
00000010: 1ce1 1717 ab48 224f fe26 102e 9c9c 1361 .....H"O.&.....a
00000020: 3690 eeed 3829 aba6 7d6f b787 6...8)..}o..
The last 32-bytes in each case are the keys, i.e. in this case:
To extract them and save them to a variable, you can use this command:
xxd -plain -cols 32 -s -32 DERFILE
This will print out the last 32 bytes (-s -32
: seek 32 bytes from the end) on one line (-cols 32
: 32 byte columns per line), without the extra offset and ASCII information that xxd prints ou by default (-plain
option).
Example usage:
$ xxd -plain -cols 32 -s -32 private.der
b74eb32cd805ed82a3580efa5921cdb20aad7f4bf64109057e846111555ea3e9
$ xxd -plain -cols 32 -s -32 public.der
d164893b1ce11717ab48224ffe26102e9c9c13613690eeed3829aba67d6fb787
You can check that these correspond to the openssl pkey
output we got earlier with the -text
option.
Store output in a variable:
$ hex=$(xxd -plain -cols 32 -s -32 public.der)
$ echo "32-byte hex public key: ${hex}"
32-byte hex public key: d164893b1ce11717ab48224ffe26102e9c9c13613690eeed3829aba67d6fb787
Based on the xxd output of the DER files seen before, we know that DER files are simply a binary header followed by the 32-byte key. The headers are different for private and public keys, but independent of the 32-byte keys, i.e. any such header followed by 32 bytes is a valid DER file.
The headers are:
This makes creating the DER files easy and they can then be converted to PEM files using openssl pkey
.
Note: I do not know why the headers are this way, but it seems to have to do with the RFC 8410 specification. See also this stackoverflow answer.
Let's assume our 32-byte public key is d164893b1ce11717ab48224ffe26102e9c9c13613690eeed3829aba67d6fb787 in hex format.
First, we create a valid DER file by simply appending those bytes to the standard header of a DER file:
header="30:2A:30:05:06:03:2B:65:70:03:21:00" # Necessary header for the OpenSSL DER format
hex="d164893b1ce11717ab48224ffe26102e9c9c13613690eeed3829aba67d6fb787"
echo "${header}${hex}" | xxd -revert -plain > public.manual.der
Next, we convert it to the PEM format with:
openssl pkey -pubin -inform der -in public.manual.der -pubout -out public.manual.pem
Let's assume our 32-byte private key is b74eb32cd805ed82a3580efa5921cdb20aad7f4bf64109057e846111555ea3e9 in hex format.
First, we create a valid DER file by simply appending those bytes to the standard header of a DER file:
header="30:2e:02:01:00:30:05:06:03:2b:65:70:04:22:04:20" # Necessary header for the OpenSSL DER format
hex="b74eb32cd805ed82a3580efa5921cdb20aad7f4bf64109057e846111555ea3e9"
echo "${header}${hex}" | xxd -revert -plain > private.manual.der
Next, we convert it to the PEM format with:
openssl pkey -inform der -in private.manual.der -out private.manual.pem
$ echo "Hello world!" > data.txt
$ openssl pkeyutl -sign -inkey private.manual.pem -rawin -in data.txt -out data.txt.sig
$ openssl pkeyutl -verify -pubin -inkey public.manual.pem -sigfile data.txt.sig -rawin -in data.txt
Signature Verified Successfully