如何使用OpenSSL验证JSON Web令牌?

时间:2016-11-12 05:04:21

标签: qt openssl jwt

我想用OpenSSL验证JSON Web Tokens (JWT),但我无法让验证成功。以下是使用Verifying JWT signed with the RS256 algorithm using public key in C#中的令牌和密钥的代码:

QByteArray token       = "eyJraWQiOiIxZTlnZGs3IiwiYWxnIjoiUlMyNTYifQ.ewogImlzcyI6ICJodHRwOi8vc2VydmVyLmV4YW1wbGUuY29tIiwKICJzdWIiOiAiMjQ4Mjg5NzYxMDAxIiwKICJhdWQiOiAiczZCaGRSa3F0MyIsCiAibm9uY2UiOiAibi0wUzZfV3pBMk1qIiwKICJleHAiOiAxMzExMjgxOTcwLAogImlhdCI6IDEzMTEyODA5NzAsCiAiY19oYXNoIjogIkxEa3RLZG9RYWszUGswY25YeENsdEEiCn0.XW6uhdrkBgcGx6zVIrCiROpWURs-4goO1sKA4m9jhJIImiGg5muPUcNegx6sSv43c5DSn37sxCRrDZZm4ZPBKKgtYASMcE20SDgvYJdJS0cyuFw7Ijp_7WnIjcrl6B5cmoM6ylCvsLMwkoQAxVublMwH10oAxjzD6NEFsu9nipkszWhsPePf_rM4eMpkmCbTzume-fzZIi5VjdWGGEmzTg32h3jiex-r5WTHbj-u5HL7u_KP3rmbdYNzlzd1xWRYTUs4E8nOTgzAUwvwXkIQhOh5TPcSMBYy6X3E7-_gr9Ue6n4ND7hTFhtjYs3cjNKIA08qm5cpVYFMFMG6PkhzLQ);
QByteArray rsaModulus  = QByteArray::fromBase64("w7Zdfmece8iaB0kiTY8pCtiBtzbptJmP28nSWwtdjRu0f2GFpajvWE4VhfJAjEsOcwYzay7XGN0b-X84BfC8hmCTOj2b2eHT7NsZegFPKRUQzJ9wW8ipn_aDJWMGDuB1XyqT1E7DYqjUCEOD1b4FLpy_xPn6oV_TYOfQ9fZdbE5HGxJUzekuGcOKqOQ8M7wfYHhHHLxGpQVgL0apWuP2gDDOdTtpuld4D2LK1MZK99s9gaSjRHE8JDb1Z4IGhEcEyzkxswVdPndUWzfvWBBWXWxtSUvQGBRkuy1BHOa4sP6FKjWEeeF7gm7UMs2Nm2QUgNZw6xvEDGaLk4KASdIxRQ");
QByteArray rsaExponent = QByteArray::fromBase64("AQAB");

QList<QByteArray> tokenParts = token.split('.');
QByteArray header = tokenParts[0];
QByteArray body   = tokenParts[1];
QByteArray sig    = tokenParts[2];

QByteArray pad = sig.length() % 4 ? "" : QByteArray("====").left(4 - (sig.length() % 4));
QByteArray decodedSig = QByteArray::fromBase64(sig.replace('_', '/').replace('-', '+') + pad);

BIGNUM *rsaModulusBn  = BN_bin2bn((unsigned char *)rsaModulus.data(), rsaModulus.length(), NULL);
BIGNUM *rsaExponentBn = BN_bin2bn((unsigned char *)rsaExponent.data(), rsaExponent.length(), NULL);

RSA *rsa = RSA_new();
if (rsa == NULL) printf("cannot allocate RSA\n");
rsa->n = rsaModulusBn;
rsa->e = rsaExponentBn;

EVP_PKEY *key = EVP_PKEY_new();
if (1 != EVP_PKEY_set1_RSA(key, rsa)) ERR_print_errors_fp(stdout);

EVP_MD_CTX *ctx = EVP_MD_CTX_create();
if (ctx == NULL) printf("cannot allocate MD CTX\n");

if (1 != EVP_DigestVerifyInit(ctx, NULL, EVP_sha256(), NULL, key)) ERR_print_errors_fp(stdout);
if (1 != EVP_DigestVerifyUpdate(ctx, (unsigned char *)header.data(), header.length())) ERR_print_errors_fp(stdout);
if (1 != EVP_DigestVerifyUpdate(ctx, ".", 1)) ERR_print_errors_fp(stdout);
if (1 != EVP_DigestVerifyUpdate(ctx, (unsigned char *)body.data(), body.length())) ERR_print_errors_fp(stdout);
if (1 != EVP_DigestVerifyFinal(ctx, (unsigned char *)decodedSig.data(), decodedSig.length()))
{
  printf("failure\n");
  ERR_print_errors_fp(stdout);
}
else
{
  printf("success\n");
}

if (ctx) EVP_MD_CTX_destroy(ctx);
if (key) EVP_PKEY_free(key);
if (rsa) RSA_free(rsa);

如果它有用,node jwt-to-pem表示上面的PEM格式的键是:

-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEAw7Zdfmece8iaB0kiTY8pCtiBtzbptJmP28nSWwtdjRu0f2GFpajv
WE4VhfJAjEsOcwYzay7XGN0b+X84BfC8hmCTOj2b2eHT7NsZegFPKRUQzJ9wW8ip
n/aDJWMGDuB1XyqT1E7DYqjUCEOD1b4FLpy/xPn6oV/TYOfQ9fZdbE5HGxJUzeku
GcOKqOQ8M7wfYHhHHLxGpQVgL0apWuP2gDDOdTtpuld4D2LK1MZK99s9gaSjRHE8
JDb1Z4IGhEcEyzkxswVdPndUWzfvWBBWXWxtSUvQGBRkuy1BHOa4sP6FKjWEeeF7
gm7UMs2Nm2QUgNZw6xvEDGaLk4KASdIxRQIDAQAB
-----END RSA PUBLIC KEY-----

验证失败:

10857314492266413637:error:04091077:rsa routines:INT_RSA_VERIFY:wrong signature length:rsa_sign.c:186:

我非常感谢有经验的人查看我的代码,因为我不知道如何调试它而没有一个可比较的实例。

1 个答案:

答案 0 :(得分:1)

事实证明,RSA密钥模数和指数也是base64 URL编码。这是有效的代码,horaay:

QByteArray token       = "eyJraWQiOiIxZTlnZGs3IiwiYWxnIjoiUlMyNTYifQ.ewogImlzcyI6ICJodHRwOi8vc2VydmVyLmV4YW1wbGUuY29tIiwKICJzdWIiOiAiMjQ4Mjg5NzYxMDAxIiwKICJhdWQiOiAiczZCaGRSa3F0MyIsCiAibm9uY2UiOiAibi0wUzZfV3pBMk1qIiwKICJleHAiOiAxMzExMjgxOTcwLAogImlhdCI6IDEzMTEyODA5NzAsCiAiY19oYXNoIjogIkxEa3RLZG9RYWszUGswY25YeENsdEEiCn0.XW6uhdrkBgcGx6zVIrCiROpWURs-4goO1sKA4m9jhJIImiGg5muPUcNegx6sSv43c5DSn37sxCRrDZZm4ZPBKKgtYASMcE20SDgvYJdJS0cyuFw7Ijp_7WnIjcrl6B5cmoM6ylCvsLMwkoQAxVublMwH10oAxjzD6NEFsu9nipkszWhsPePf_rM4eMpkmCbTzume-fzZIi5VjdWGGEmzTg32h3jiex-r5WTHbj-u5HL7u_KP3rmbdYNzlzd1xWRYTUs4E8nOTgzAUwvwXkIQhOh5TPcSMBYy6X3E7-_gr9Ue6n4ND7hTFhtjYs3cjNKIA08qm5cpVYFMFMG6PkhzLQ";
QByteArray rsaModulus  = QByteArray::fromBase64("w7Zdfmece8iaB0kiTY8pCtiBtzbptJmP28nSWwtdjRu0f2GFpajvWE4VhfJAjEsOcwYzay7XGN0b-X84BfC8hmCTOj2b2eHT7NsZegFPKRUQzJ9wW8ipn_aDJWMGDuB1XyqT1E7DYqjUCEOD1b4FLpy_xPn6oV_TYOfQ9fZdbE5HGxJUzekuGcOKqOQ8M7wfYHhHHLxGpQVgL0apWuP2gDDOdTtpuld4D2LK1MZK99s9gaSjRHE8JDb1Z4IGhEcEyzkxswVdPndUWzfvWBBWXWxtSUvQGBRkuy1BHOa4sP6FKjWEeeF7gm7UMs2Nm2QUgNZw6xvEDGaLk4KASdIxRQ", QByteArray::Base64UrlEncoding);
QByteArray rsaExponent = QByteArray::fromBase64("AQAB", QByteArray::Base64UrlEncoding);

QList<QByteArray> tokenParts = token.split('.');
QByteArray header = tokenParts[0];
QByteArray body   = tokenParts[1];
QByteArray sig    = QByteArray::fromBase64(tokenParts[2], QByteArray::Base64UrlEncoding);

BIGNUM *rsaModulusBn  = BN_bin2bn((unsigned char *)rsaModulus.data(), rsaModulus.length(), NULL);
BIGNUM *rsaExponentBn = BN_bin2bn((unsigned char *)rsaExponent.data(), rsaExponent.length(), NULL);

RSA *rsa = RSA_new();
if (rsa == NULL) printf("cannot allocate RSA\n");

#if defined(OPENSSL_1_1_0)
if (1 != RSA_set0_key(rsa, rsaModulusBn, rsaExponentBn, NULL); ERR_print_errors_fp(stdout);
#else
rsa->n = rsaModulusBn;
rsa->e = rsaExponentBn;
#endif

EVP_PKEY *key = EVP_PKEY_new();
if (1 != EVP_PKEY_set1_RSA(key, rsa)) ERR_print_errors_fp(stdout);

EVP_MD_CTX *ctx = EVP_MD_CTX_create();
if (ctx == NULL) printf("cannot allocate MD CTX\n");

if (1 != EVP_DigestVerifyInit(ctx, NULL, EVP_sha256(), NULL, key)) ERR_print_errors_fp(stdout);
if (1 != EVP_DigestVerifyUpdate(ctx, (unsigned char *)header.data(), header.length())) ERR_print_errors_fp(stdout);
if (1 != EVP_DigestVerifyUpdate(ctx, ".", 1)) ERR_print_errors_fp(stdout);
if (1 != EVP_DigestVerifyUpdate(ctx, (unsigned char *)body.data(), body.length())) ERR_print_errors_fp(stdout);
if (1 != EVP_DigestVerifyFinal(ctx, (unsigned char *)sig.data(), sig.length()))
{
  printf("failure\n");
  ERR_print_errors_fp(stdout);
}
else
{
  printf("success\n");
}

if (ctx) EVP_MD_CTX_destroy(ctx);
if (key) EVP_PKEY_free(key);
if (rsa) RSA_free(rsa);