Photo by Kelly Sikkema on Unsplash
Why some encryption keys don't work with the jsonwebtoken NPM package
Specifically, PKIX formatted keys.
Table of contents
One would think that any DER encoded keypair should work with the jsonwebtoken package (which I will from now on just call JWT) from Auth0, but annoying, that is not the case.
See, JWT relies upon Node's crypto library to determine if your key is valid or not... Node does not allow certain DER encoding methods for private & public keys.
Luckily, golang's crypto library only really gives us two DER encoding options for RSA keys. PKCS1
and PKCS8
for private keys, and PKCS1
and PKIX
for public keys.
Now the PKCS#
keys work just fine on Node, the issue is the PKIX
keys.
These keys aren't actually encoded for the same type of use as the PKCS#
encoded keys are... they're meant for the internet, specifically, certificates. At least, according to this stackoverflow post from April 17th, 2018.
So, the PKIX
keys are a different structure and use case than the PKCS
keys.
They're meant for certificates, not simpler keys.
So, don't use them for anything which just needs a key.
How did I figure this out
If you're wondering how I figured this out, it's because Folderr's CLI is written in Go, and I tried using it to setup Folderr's database, and it just kept failing. Folderr would say the key was not asymmetrical, which, apparently it wasn't, because it was meant for a certificate.
There's a simple way to replicate this issue and see it for yourself
Use something like a modified version of foldcli's GenKeys function (modified to put out both PKIX and PKCS1 public keys)
Try the
jwt-keytest
with the PKCS1 public key, and PKIX public key
Replication
Example of a modified GenKeys function (generates 1 private key, and 2 public keys):
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
)
// returns Private Key, Public Key (PKCS1), Public Key (PKIX), and error
func GenKeys() ([]byte, []byte, []byte, error) {
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, nil, nil, err
}
// Sanity check the privateKey
if privateKey.Validate() != nil {
return nil, nil, nil, privateKey.Validate()
}
// You can use PKCS1, I'm sure it doesn't matter.
privBytes, err := x509.MarshalPKCS8PrivateKey(privateKey)
if err != nil {
return nil, nil, nil, err
}
privBlock := pem.Block{
Type: "RSA PRIVATE KEY",
Headers: nil,
Bytes: privBytes,
}
privatePem := pem.EncodeToMemory(&privBlock)
// Works with nodes jsonwebtoken library
pubBytes := x509.MarshalPKCS1PublicKey(&privateKey.PublicKey)
pubBlock := pem.Block{
Type: "RSA PUBLIC KEY",
Headers: nil,
Bytes: pubBytes,
}
publicPem := pem.EncodeToMemory(&pubBlock)
// Does not work with node's jsonwebtoken library
pubPKIX, err := x509.MarshalPKIXPublicKey(&privateKey.PublicKey)
if err != nil {
return nil, nil, nil, err
}
pubPKIXBlock := pem.Block{
Type: "RSA PUBLIC KEY",
Headers: nil,
Bytes: pubPKIX,
}
pubPKIXPem := pem.EncodeToMemory(&pubPKIXBlock)
return privatePem, publicPem, pubPKIXPem, nil
}
You'll need to manage the saving files yourself, but, this will generate the correct keys
You can use Folderr's JWT Keytest repo to test these keys