Проверка JWT с использованием jsonwebtoken в node.js с токеном, сгенерированным jose4j, не выполняется

Я пытаюсь проверить веб-токен json, созданный jose4j, используя jsonwebtoken в node.js, и вижу следующую ошибку:

[Ошибка: сбой PEM_read_bio_PUBKEY]

Код jose4j в основном взят прямо из примера:

RsaJsonWebKey key = RsaJwkGenerator.generateJwk(2048);
key.setKeyId("global.authenticated");

byte[] raw = key.getKey().getEncoded();
Base64.Encoder encoder = Base64.getEncoder();
System.out.printf("Public Key [%s]\n", encoder.encodeToString(raw));

JwtClaims claims = new JwtClaims();
claims.setIssuer("global.gen");
claims.setAudience("global.cons");
claims.setExpirationTimeMinutesInTheFuture(12 * 60);
claims.setGeneratedJwtId();
claims.setIssuedAtToNow();
claims.setNotBeforeMinutesInThePast(2);
claims.setSubject("nim");
claims.setClaim("role", "tester");

JsonWebSignature jws = new JsonWebSignature();
jws.setPayload(claims.toJson());
jws.setKey(key.getPrivateKey());
jws.setKeyIdHeaderValue(key.getKeyId());
jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256);
String token = jws.getCompactSerialization();
System.out.printf("Generated Token [%s]\n", token);


JwtConsumer jwtConsumer = new JwtConsumerBuilder()
    .setRequireExpirationTime() // the JWT must have an expiration time
    .setAllowedClockSkewInSeconds(30) // allow some leeway in validating time based claims to account for clock skew
    .setRequireSubject() // the JWT must have a subject claim
    .setExpectedIssuer("global.gen") // whom the JWT needs to have been issued by
    .setExpectedAudience("global.cons") // to whom the JWT is intended for
    .setVerificationKey(key.getKey()) // verify the signature with the public key
    .build(); // create the JwtConsumer instance

try {
  //  Validate the JWT and process it to the Claims
  JwtClaims jwtClaims = jwtConsumer.processToClaims(token);
  System.out.println("JWT validation succeeded! " + jwtClaims);
} catch (InvalidJwtException e) {
  // InvalidJwtException will be thrown, if the JWT failed processing or validation in anyway.
  // Hopefully with meaningful explanations(s) about what went wrong.
  System.out.println("Invalid JWT! " + e);
}

Итак, внутренне токен проверяется нормально. Однако, когда я копирую токен и ключ (например, ниже из прогона), выдается сообщение об указанной выше ошибке:

var jwt        = require('jsonwebtoken'); // used to create, sign, and verify tokens

var key = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkRWAQ0O9LgBoHNAB5m1X8e1sPTzKmBTPCFTSRTzw0AjZozIbN4nIp/3jnQHTbcY0Bf5MDWmtdheSK1a+ew34YcgN2b9Shr+3yZv9PJ97i7gRCqOnI7jbm7PXFBNw1I4aMYc6tV7TKFzvx6008/nYvN3Jey6Z8ItS/FLQRDkV9m/WQkhJpYgvmD6qiwj9d+un+moBQ5/PPgn7Qkg5GyxZUy9PsblUDSrIA0bEiv/wQOXCYUvL9OFzxTUSeIHpdGibhPQVxX3Jnpr293Iq/mOKn3ZO+xBID26m3L8+ik64wte041y1S4HHaE9Q082ai/uBduAwIHcJY5VAHborZYCSaQIDAQAB';
var token = 'eyJraWQiOiJnbG9iYWwuYXV0aGVudGljYXRlZCIsImFsZyI6IlJTMjU2In0.eyJpc3MiOiJnbG9iYWwuZ2VuIiwiYXVkIjoiZ2xvYmFsLmNvbnMiLCJleHAiOjE0NDMwNjMyMDgsImp0aSI6InpweF9ERW8tX1h2Q1hnZmNZTUpiZ0EiLCJpYXQiOjE0NDMwMjAwMDgsIm5iZiI6MTQ0MzAxOTg4OCwic3ViIjoibmltIiwicm9sZSI6InRlc3RlciJ9.inEebSQ8jYPQsTpHnvw-gMpoNbJl5ErUkS8FtkDagWrwijUgG8XYYP8FLi2ZCpdgDqUsP6nE1iG0_2wWuL7B7C7wUpZlrqR2bEOG2cXK9s26VqNAXu8I7BTDaZBKmdOt1aFVWozGsN8iUCsQ7Yt9-GfvNRP1yeOoMgpOxf_wVa0QVzsV18aVi_oSeiMqOkQ_6n7JOjFVdiURm0ew4vh5TBaMcEcS35a9jtPxuFR_Z_FaLUk0g06PDVKcdsK1-FYRAGBlRGDkea8Hs9Zh-ZIxgcs2QfWzq5PSsIKum1dWqNLW04ullWmlbAO-5d0V0NAnkh4FFoi3N7AedvkILJgbqA';

jwt.verify(token, key, { algorithms: ['RS256'] }, function(err, decoded) {
  if (err)
    console.log(err);
  else
    console.log(decoded);
});

Есть ли какая-то магия, которую мне не хватает (в jose4j или jsonwebtoken), которая позволит проверить сгенерированный токен открытым ключом?

Кстати, вставка токена в jwt.io правильно декодирует заголовок и полезную нагрузку, однако подпись не может быть проверена с использованием того же открытого ключа. Я предполагаю, что проблема действительно в стороне jose4j, но не уверен.


person Nim    schedule 23.09.2015    source источник


Ответы (2)


Я добавлю это в качестве ответа, если кто-то найдет что-то похожее. Проблема связана с форматом ключа, переданного в jsonwebtoken. Это не может быть просто открытый ключ в виде простого текста, он должен точно соответствовать формату PEM. Итак, как только я конвертирую ключ в файл PEM, в частности, сделав следующие изменения:

byte[] raw = key.getKey().getEncoded();
Base64.Encoder encoder = Base64.getMimeEncoder(64, new byte[]{'\n'});

А затем обертывание

-----BEGIN PUBLIC KEY-----
<Key>
-----END PUBLIC KEY-----

Затем передача полученного файла в jsonwebtoken позволяет продолжить аутентификацию. Я не понимал, что сертификат, переданный на этап проверки, должен быть отформатирован настолько строго (т.е. с размерами строк и обертками!)

ПРИМЕЧАНИЕ: пакет bouncycastle имеет класс PemObject и PemWriter, что должно несколько упростить запись файла, однако я не хотел использовать другой пакет только для этого. Может быть, разработчики jose4j добавят для этого небольшой класс в свой пакет ..

person Nim    schedule 24.09.2015
comment
Я поддерживаю jose4j, и, хотя форматирование PEM действительно выходит за рамки библиотеки, я рассмотрю возможность добавления некоторых простых утилит для определенных вещей, таких как получение открытого ключа в кодировке PEM. Было бы хорошо также побудить пользователей auth0 поддерживать JWK, который представляет собой хороший JSON. на основе способа кодирования ключей. - person Brian Campbell; 24.09.2015
comment
Спасибо! И я просто вставил это, чтобы отслеживать функцию кодирования PEM bitbucket.org/b_c/jose4j/issues / 37 - person Brian Campbell; 24.09.2015
comment
В следующем выпуске будет KeyPairUtil.pemEncode(PublicKey publicKey) вспомогательный метод, который сделает это. Тоже расшифровка. - person Brian Campbell; 12.11.2015
comment
jose4j v0.5.0 был недавно выпущен и имеет KeyPairUtil.pemEncode(PublicKey publicKey), а также fromPemEncoded(String pem), которые помогут преобразовать объекты PublicKey в / из ключей, закодированных в PEM. - person Brian Campbell; 08.03.2016

Кажется, что jsonwebtoken очень строго относится не только к верхнему и нижнему колонтитулам -----BEGIN PUBLIC KEY-----, но и к разрывам строк.

Ключ вроде

MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkRWAQ0O9LgBoHNAB5m1X8e1sPTzKmBTPCFTSRTzw0AjZozIbN4nIp/3jnQHTbcY0Bf5MDWmtdheSK1a+ew34YcgN2b9Shr+3yZv9PJ97i7gRCqOnI7jbm7PXFBNw1I4aMYc6tV7TKFzvx6008/nYvN3Jey6Z8ItS/FLQRDkV9m/WQkhJpYgvmD6qiwj9d+un+moBQ5/PPgn7Qkg5GyxZUy9PsblUDSrIA0bEiv/wQOXCYUvL9OFzxTUSeIHpdGibhPQVxX3Jnpr293Iq/mOKn3ZO+xBID26m3L8+ik64wte041y1S4HHaE9Q082ai/uBduAwIHcJY5VAHborZYCSaQIDAQAB

должен выглядеть так, чтобы он работал с jsonwebtoken (обратите внимание на верхний колонтитул, нижний колонтитул и разрывы строк):

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkRWAQ0O9LgBoHNAB5m1X
8e1sPTzKmBTPCFTSRTzw0AjZozIbN4nIp/3jnQHTbcY0Bf5MDWmtdheSK1a+ew34
YcgN2b9Shr+3yZv9PJ97i7gRCqOnI7jbm7PXFBNw1I4aMYc6tV7TKFzvx6008/nY
vN3Jey6Z8ItS/FLQRDkV9m/WQkhJpYgvmD6qiwj9d+un+moBQ5/PPgn7Qkg5GyxZ
Uy9PsblUDSrIA0bEiv/wQOXCYUvL9OFzxTUSeIHpdGibhPQVxX3Jnpr293Iq/mOK
n3ZO+xBID26m3L8+ik64wte041y1S4HHaE9Q082ai/uBduAwIHcJY5VAHborZYCS
aQIDAQAB
-----END PUBLIC KEY-----

Эта функция выполняет всю работу за меня:

function base64toPem(base64)
{
    for(var result="", lines=0;result.length-lines < base64.length;lines++) {
        result+=base64.substr(result.length-lines,64)+"\n"
    }

    return "-----BEGIN PUBLIC KEY-----\n" + result + "-----END PUBLIC KEY-----";
}
person AndreasPizsa    schedule 15.02.2016