Skip to content

JWT signed with EdDSA fails to be validated with nodejs/jose #492

Closed
@ericchaves

Description

@ericchaves

Version(s) affected

3.2.8

Description

Hi folks, I'm issuing a JWT signed with EdDSA (ed25519) using web-token\jwt-framework and trying to validate it using nodej's jose but the validation fails complaining that signature is invalid.

With jwt-framework I can verify a token issued and signed by nodej's jose using the same EdDSA key and I can also verify with jwt-framework a JWT signed by jwt-framework in jwt-framework with this EdDSA key.

Other EC algorithms like ES256 also works perfectly between the two libs. I can also validate other tokens issued in other languages using EdDSA in nodejs. So far only EdDSA tokens signed in PHP are failing.

Can someone help me figure out if I'm doing something wrong or if there is some interoperability issue between those two implementations?

Thanks in advance for any help and congrats for the great work done so far!

How to reproduce

Forgive me if you find any typos. Had to copy and paste partial lines of code.

create an ed25519 key pair using openssl:

openssl genpkey -algorithm Ed25519 -out ed25519_private_key.pem
openssl pkcs8 -topk8 -nocrypt -in ed25519_private_key.pem -out ed25519_private_key_pkcs8.pem
openssl pkey -in ed25519_private_key.pem -pubout -out ed25519_public_key.pem

Issue a signed JWS with web-token/jwt-framework and write it to file.

use Jose\Component\KeyManagement\JWKFactory;
use Jose\Component\Core\AlgorithmManager;
use Jose\Component\Signature\JWSVerifier;
use Jose\Component\Core\JWK;
use Jose\Component\Signature\Algorithm\EdDSA;
use Jose\Component\Signature\Algorithm\ES256;
use Jose\Component\Signature\JWSBuilder;
use Jose\Component\Signature\Serializer\CompactSerializer as JWSCompactSerializer;
use Jose\Component\Signature\Serializer\JWSSerializer;

//CONST ALG = 'ES256';
//const CHAVE_PRIVADA_PKCS8  = './keys/ES256/ecdsa-p256_private_key_pkcs8.pem';
//const CHAVE_PUBLICA        = './keys/ES256/ecdsa-p256_public_key.pem';

CONST ALG = 'EdDSA';
const CHAVE_PRIVADA_PKCS8 = './keys/EdDSA/ed25519_private_key_pkcs8.pem';
const CHAVE_PUBLICA = './keys/EdDSA/ed25519_public_key.pem';

$chavePrivada = JWKFactory::createFromKeyFile(CHAVE_PRIVADA_PKCS8);
$chavePublica = JWKFactory::createFromKeyFile(CHAVE_PUBLICA);

$payload = [
    'exp' => time() + 60 * 50,
    'iss' => 'rebels',
    'sub' => '78286616731',
    'aud' => 'ghost',
    'iat' => time(),
    'name' => 'Ezra Bridger',
    'deviceid' => '3c512309-57ca-47bf-8543-c2f1cec1189a',
];

$algorithmManager = new AlgorithmManager([
    new ES256(),
    new EdDSA(),
]);
$jwsBuilder = new JWSBuilder($algorithmManager);
$jws = $jwsBuilder
    ->create()
    ->withPayload(json_encode($payload))
    ->addSignature($chavePrivada, ['alg' => ALG])
    ->build();

$serializer = new JWSCompactSerializer();
$token = $serializer->serialize($jws, 0);
file_put_contents('./output/php-jws.'. ALG . '.txt', $token);

// $token = file_get_contents('./output/node-jws.txt');
$jwsVerifier = new JWSVerifier($algorithmManager);
$jwsVerifier->verifyWithKey($jws, $chavePrivada, 0);

read it on nodejs and validate the JWT

import { jwtVerify,  importSPKI, decodeProtectedHeader } from 'jose';
import { readFileSync } from 'fs';

async function validateJWT(filename, publicKeyFile) {
  const jwt = readFileSync(filename).toString();
  const protectedHeader = decodeProtectedHeader(jwt);
  console.log(protectedHeader);
  const publicKeyPartner = await importSPKI(readFileSync(publicKeyFile).toString());
  const verifiedJWT = await jwtVerify(jwt, publicKeyPartner);
  return verifiedJWT;
}
validateJWT('./output/php-jws.ES256.txt', './keys/ES256/ecdsa-p256_public_key.pem');
validateJWT('./output/php-jws.EdDSA.txt', './keys/EdDSA/ed25519_public_key.pem');

To generate ES256 keys with openssl:

openssl ecparam -genkey -name prime256v1 -noout -out ecdsa-p256-private.pem
openssl pkcs8 -topk8 -nocrypt -in ecdsa-p256-private.pem -out ecdsa-p256-private.p8
openssl ec -in ecdsa-p256-private.pem -pubout -out ecdsa-p256-public.pem

Possible Solution

No response

Additional Context

Node version: v20.5.0
node jose version: 4.13.1
Error message from nodes

file:///home/user/jwt/node_modules/jose/dist/node/esm/jws/flattened/verify.js:87
        throw new JWSSignatureVerificationFailed();
              ^

JWSSignatureVerificationFailed: signature verification failed
    at flattenedVerify (file:///home/user/jwt/node_modules/jose/dist/node/esm/jws/flattened/verify.js:87:15)
    at async compactVerify (file:///home/user/jwt/node_modules/jose/dist/node/esm/jws/compact/verify.js:15:22)
    at async jwtVerify (file:///home/user/jwt/node_modules/jose/dist/node/esm/jwt/verify.js:6:22)
    at async validateJWT (file:///home/user/jwt/node-read.mjs:9:23) {
  code: 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED'
}

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions