Node-jsonwebtoken: Error in Verify(): PEM_read_bio_PUBKEY

Created on 16 Mar 2015  路  18Comments  路  Source: auth0/node-jsonwebtoken

On OSX 10.10.2, Node v0.12.0 and jsonwebtoken v4.1.0

Using jsonwebtoken to create my own RS256-signed tokens in the ActionHero framework. Loaded the public key with api.auth.publicKey = fs.readFileSync(path.join(__dirname, '../', api.config.auth.publicKey));.

I am able encode, but not decode. When calling require('jsonwebtoken').verify(token,, api.auth.publicKey) I get a PEM_read_bio_PUBKEY failed error. Seems to be in the JWS library, judging from the stack trace.

Full error trace (in an ActionHero flavor):

2015-03-16 11:09:31 - error: ! uncaught error from action: action:status
2015-03-16 11:09:31 - error: ! connection details:
2015-03-16 11:09:31 - error: !     action: "status"
2015-03-16 11:09:31 - error: !     remoteIP: "127.0.0.1"
2015-03-16 11:09:31 - error: !     type: "web"
2015-03-16 11:09:31 - error: !     params: {"action":"status","apiVersion":1}
2015-03-16 11:09:31 - error: ! Error: PEM_read_bio_PUBKEY failed
2015-03-16 11:09:31 - error: !     at Error (native)
2015-03-16 11:09:31 - error: !     at Verify.verify (crypto.js:356:23)
2015-03-16 11:09:31 - error: !     at Object.verify (/project/Documents/Repositories/server/node_modules/jsonwebtoken/node_modules/jws/node_modules/jwa/index.js:65:21)
2015-03-16 11:09:31 - error: !     at Object.jwsVerify [as verify] (/project/Documents/Repositories/server/node_modules/jsonwebtoken/node_modules/jws/lib/verify-stream.js:68:15)
2015-03-16 11:09:31 - error: !     at Object.module.exports.verify (/project/Documents/Repositories/server/node_modules/jsonwebtoken/index.js:113:17)
2015-03-16 11:09:31 - error: !     at Object.api.auth.isAuthenticated (/project/Documents/Repositories/server/initializers/1500_auth.js:38:18)
2015-03-16 11:09:31 - error: !     at /project/Documents/Repositories/server/initializers/1500_auth.js:50:22
2015-03-16 11:09:31 - error: !     at /project/Documents/Repositories/server/node_modules/actionhero/initializers/actionProcessor.js:135:15
2015-03-16 11:09:31 - error: !     at /project/Documents/Repositories/server/node_modules/actionhero/node_modules/async/lib/async.js:610:21
2015-03-16 11:09:31 - error: !     at /project/Documents/Repositories/server/node_modules/actionhero/node_modules/async
question

Most helpful comment

The problem is the way you format the public key.

The function is very strict on the formatting. Require begin and end lines, and line breaks at every 64 characters.

Something like this:
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDdlatRjRjogo3WojgGHFHYLugd
UWAY9iR3fy4arWNA1KoS8kVw33cJibXr8bvwUAUparCwlvdbH6dvEOfou0/gCFQs
HUfQrSDv+MuSUMAe8jzKE4qW+jK+xQU9a03GUnKHkkle+Q0pX/g6jXZ7r1/xAK5D
o2kQ+X5xK9cipRgEKwIDAQAB
-----END PUBLIC KEY-----

All 18 comments

Did you ever figure this out?

@brandwe: Unfortunately, no. Switched down to HS256. Would love to have resolution on it. We have a ticket to circle back at some point and see what we can do.

I can confirm this with the latest io.js on the latest yosemite, too. HS256 works and RS256 doesn't.

I got the same error and problem was a sign error. I've used node-rsa to generate the keys like this:

var key = new NodeRSA({b: 512, e: 5});

    key.setOptions({
        encryptionScheme: {
        scheme: 'pkcs1',
        label: 'Optimization-Service'
    },
    signingScheme: {
        saltLength: 25
    }
  });

  return {
        "private" : key.exportKey('pkcs1-private-pem'),
        "public"  : key.exportKey('pkcs8-public-pem')
  };

The important thing is scheme: 'pkcs1'.

Now you are able to use with no problem the keys on jwt sign and verify. Don't forget that sign as to use the private key and verify the public key.

jwt.sign

jwt.sign({name: user.name, stamp: user.tstamp}, PEM_PRIVATE, {
      algorithm: 'RS256'
});

jwt.verify

jwt.verify(token, PEM_PUBLIC, {algorithms: ['RS256'], ignoreExpiration: true}...

Could it be that you are using a ssh-rsa key?

Experiencing the same issue here. Private key was encoded into a PEM file via PKCS1 while the public key was encoded via PKIX. As far as I can see, there is no way to encode my public key using PKCS1 as well, resulting in the same error:

[Error: PEM_read_bio_PUBKEY failed]

Seems using the following openssl command:

openssl rsa -in private_key_filename -pubout -outform PEM -out public_key_output_filename

I was able to use the private key to output the public key in the correct PEM format, and was able to get the verify step to work.

I guess this is a bit old, but there actually is a fix for this. I ran into this myself on Mavericks. The issue is the version of OpenSSL installed with Mavericks (and older versions of OSX); the man pages would have you believe that everything is dandy, but it isn't. You either need to update OpenSSL using Homebrew (brew install openssl, I believe) or if you don't use Homebrew, you need to upgrade to the latest version here: http://www.openssh.com/portable.html

Upgrading the underlying binaries solved the issue. This is not a problem with the JSONWebToken library; essentially, the dependencies end up relying on system binaries to perform key manipulation. Apple just needs to update their stuff.

I really hope this helps someone. As a bonus, upgrading means that you can actually use ssh-keygen to read, generate, and convert ECDSA keys.

Public key needs to be in PKCS8 (OpenSSL default) format.

You can export it by running the following command:
ssh-keygen -e -m PKCS8 -f /path/to/key.pub > /path/to/converted_key.pub

Don't know if this is the scope of this project, but maybe, it would be great to add support for different key formats, so we have out-of-the-box conversion when verifying, say:

jwt.verify(token, PEM_PUBLIC, { format: 'PKCS8', algorithms: ['RS256'], ignoreExpiration: true });

The problem is the way you format the public key.

The function is very strict on the formatting. Require begin and end lines, and line breaks at every 64 characters.

Something like this:
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDdlatRjRjogo3WojgGHFHYLugd
UWAY9iR3fy4arWNA1KoS8kVw33cJibXr8bvwUAUparCwlvdbH6dvEOfou0/gCFQs
HUfQrSDv+MuSUMAe8jzKE4qW+jK+xQU9a03GUnKHkkle+Q0pX/g6jXZ7r1/xAK5D
o2kQ+X5xK9cipRgEKwIDAQAB
-----END PUBLIC KEY-----

@migara thanks a lot ..it really helped...just wasted 2 hour on this.

@migara @krvikash35 Check the command I posted above

@andresmatasuarez yeah i checked that, that was useful. thanks

For me and anyone else who has this same issue, I found this function which seems to have done the necessary conversion.

function convertCertificate (cert) {
    //Certificate must be in this specific format or else the function won't accept it
    var beginCert = "-----BEGIN CERTIFICATE-----";
    var endCert = "-----END CERTIFICATE-----";

    cert = cert.replace("\n", "");
    cert = cert.replace(beginCert, "");
    cert = cert.replace(endCert, "");

    var result = beginCert;
    while (cert.length > 0) {

        if (cert.length > 64) {
            result += "\n" + cert.substring(0, 64);
            cert = cert.substring(64, cert.length);
        }
        else {
            result += "\n" + cert;
            cert = "";
        }
    }

    if (result[result.length ] != "\n")
        result += "\n";
    result += endCert + "\n";
    return result;
}

^^Thanks so much @chekkan!

Took me a while scratching my head when this didn't quite work for me until I realized that I needed to replace the 'CERTIFICATE' with 'PUBLIC KEY' in my case.

The below worked for me (Heavily inspired from the code above leveraging ES6):

const formatKey = key => {
  const beginKey = "-----BEGIN PUBLIC KEY-----";
  const endKey = "-----END PUBLIC KEY-----";

  const sanatizedKey = key.replace(beginKey, '').replace(endKey, '').replace('\n', '')

  const keyArray = sanatizedKey.split('').map((l, i) => {
    const position = i + 1
    const isLastCharacter = sanatizedKey.length === position
    if(position % 64 === 0 || isLastCharacter) {
      return l + '\n'
    }
    return l
  })

  return `${beginKey}\n${keyArray.join('')}${endKey}\n`
}

module.exports = formatKey

thanks so much @chekkan and @mattdknapp!

i spent hours trying to figure out how to validate an RS256 token generated by keycloak, and this thread saved me big time!

Thanks @jedd-ahyoung ! Updating openssl fixed it for me :)

I had this issue with the error message [Error: PEM_read_bio_PUBKEY failed] and it turned out to be a simple issue of having inlined the string for the key and prepending it with a \n. Just making sure the very first character was a - solved the issue for me.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

p-brighenti picture p-brighenti  路  4Comments

usamamashkoor picture usamamashkoor  路  4Comments

BarukhOr picture BarukhOr  路  4Comments

dwelle picture dwelle  路  3Comments

AndreOneti picture AndreOneti  路  3Comments