How to Use Hexadecimal Ed25519 Public Keys in Node.js
Friday, Jan 28th 2022
When dealing with Ed25519 in most cryptographic packages, signing keys and verify keys are encoded in hexadecimal. But unlike most, Node uses DER-encoding, and does not support hexadecimal encoded keys. Node not supporting hex-encoded public keys is a big point of confusion for Keygen users licensing apps built on Node.
Today, we're going to learn how to use a hex Ed25519 key in Node.
Using your DER-encoded Ed25519 key
To save our users the headache, we've gone ahead and included a DER-encoded Ed25519 public key, available in your account settings. You can utilize this in Node, avoiding the pitfalls that the rest of this article attempts to work around.
But if you're curious, or you're using an Ed25519 key that isn't from Keygen, read on to learn how to convert a hexadecimal key to DER format.
Converting a hex key to DER format
The DER format is made up of a few parts. First, is the IOD, which is more or less a sequence of bytes identifying the cryptographic algorithm.
Ed25519 has the following OID:
06 03 2B 65 70
We can use this OID to manually convert our hex-encoded public key into a DER-encoded key. In addition, we'll need to do a bit more magic by creating a sequence of bytes that define the key's length, which is always 32 bytes for Ed25519 public keys.
function toDER(hex) { const key = Buffer.from(hex, 'hex') // Ed25519's OID const oid = Buffer.from([0x06, 0x03, 0x2B, 0x65, 0x70]) // Create a byte sequence containing the OID and key const elements = Buffer.concat([ Buffer.concat([ Buffer.from([0x30]), // Sequence tag Buffer.from([oid.length]), oid, ]), Buffer.concat([ Buffer.from([0x03]), // Bit tag Buffer.from([key.length + 1]), Buffer.from([0x00]), // Zero bit key, ]), ]) // Wrap up by creating a sequence of elements const der = Buffer.concat([ Buffer.from([0x30]), // Sequence tag Buffer.from([elements.length]), elements, ]) return der}
Now if we try out our toDER
function, we should be able to convert our
hex-encoded verify key to DER-encoding, and use it in Node's crypto.createPublicKey
function. This allows us to use crypto.verify(...)
, for example.
const key = toDER('6d28cf8e17e4682fbe6285e72b21aa26f094d8dbd18f7828358f822b428d069f')const verifyKey = crypto.createPublicKey({ format: 'der', type: 'spki', key,})
But if we pass in a couple different public keys, we may notice a pattern.
toDER('6d28cf8e17e4682fbe6285e72b21aa26f094d8dbd18f7828358f822b428d069f')// => <Buffer 30 2a 30 05 06 03 2b 65 70 03 21 00// 6d 28 cf 8e 17 e4 68 2f be 62 85 e7// 2b 21 aa 26 f0 94 d8 db d1 8f 78 28// 35 8f 82 2b 42 8d 06 9f>toDER('51e46ec0a8dac6b00b921e80d53dd90cd922a7a7bda8bef32edef4b112da1716')// => <Buffer 30 2a 30 05 06 03 2b 65 70 03 21 00// 51 e4 6e c0 a8 da c6 b0 0b 92 1e 80// d5 3d d9 0c d9 22 a7 a7 bd a8 be f3// 2e de f4 b1 12 da 17 16>toDER('08058583b5a58f5c847090d36cea298907ccf0de37b65ba63e10ed862e2cdc3b')// => <Buffer 30 2a 30 05 06 03 2b 65 70 03 21 00// 08 05 85 83 b5 a5 8f 5c 84 70 90 d3// 6c ea 29 89 07 cc f0 de 37 b6 5b a6// 3e 10 ed 86 2e 2c dc 3b>
The pattern? They all start with the following byte sequence!
30 2a 30 05 06 03 2b 65 70 03 21 00
This is the Ed25519 OID, and our sequence of bytes indicating that the key is
32 bytes in length. Since this will always be the case for Ed25519 verify keys,
that means we can simplify things, avoiding our toDER
function
altogether!
const key = Buffer.concat([ Buffer.from('302a300506032b6570032100', 'hex'), // Static value Buffer.from('6d28cf8e17e4682fbe6285e72b21aa26f094d8dbd18f7828358f822b428d069f', 'hex'),]) const verifyKey = crypto.createPublicKey({ format: 'der', type: 'spki', key,})
To use the public key to verify a signature, you'd use it like so.
const ok = crypto.verify(null, dataBytes, verifyKey, signatureBytes)
Converting a DER key to hex format
Now what about the reverse? Thankfully, converting a DER-encoded key to hexadecimal
is much, much more straight forward. Since our DER key is simply a Buffer
, we can
easily convert to hex
, or even base64
.
key.toString('hex').substring(24)// => 6d28cf8e17e4682fbe6285e72b21aa26f094d8dbd18f7828358f822b428d069f
We removed the first 24 characters of the string, the DER prefix.
Conclusion
Today, we covered how your Keygen account comes with a hexadecimal Ed25519 key, as well as a DER-encoded Ed25519 verify key for use within Node. And we learned how to convert Ed25519 verify keys between hexadecimal and DER encoding.
We also learned that Node, unlike so many other programming languages, does not support hexadecimal Ed25519 public keys.
Until next time.
If you find any errors in my code, or if you can think of ways to improve things, ping me via Twitter.