diff --git a/include/jwt-cpp/jwt.h b/include/jwt-cpp/jwt.h index cef7870a..80d8b95e 100644 --- a/include/jwt-cpp/jwt.h +++ b/include/jwt-cpp/jwt.h @@ -1460,6 +1460,17 @@ namespace jwt { } else throw error::rsa_exception(error::rsa_error::no_key_provided); } + /** + * Construct new rsa algorithm + * + * \param key_pair openssl EVP_PKEY structure containing RSA key pair. The private part is optional. + * \param md Pointer to hash function + * \param name Name of the algorithm + */ + rsa(helper::evp_pkey_handle key_pair, const EVP_MD* (*md)(), std::string name) + : pkey(std::move(key_pair)), md(md), alg_name(std::move(name)) { + if (!pkey) { throw error::rsa_exception(error::rsa_error::no_key_provided); } + } /** * Sign jwt data * \param data The data to sign @@ -1570,6 +1581,22 @@ namespace jwt { throw error::ecdsa_exception(error::ecdsa_error::invalid_key_size); } + /** + * Construct new ecdsa algorithm + * + * \param key_pair openssl EVP_PKEY structure containing ECDSA key pair. The private part is optional. + * \param md Pointer to hash function + * \param name Name of the algorithm + * \param siglen The bit length of the signature + */ + ecdsa(helper::evp_pkey_handle key_pair, const EVP_MD* (*md)(), std::string name, size_t siglen) + : pkey(std::move(key_pair)), md(md), alg_name(std::move(name)), signature_length(siglen) { + if (!pkey) { throw error::ecdsa_exception(error::ecdsa_error::no_key_provided); } + size_t keysize = EVP_PKEY_bits(pkey.get()); + if (keysize != signature_length * 4 && (signature_length != 132 || keysize != 521)) + throw error::ecdsa_exception(error::ecdsa_error::invalid_key_size); + } + /** * Sign jwt data * \param data The data to sign diff --git a/tests/TokenTest.cpp b/tests/TokenTest.cpp index 4249d09c..8cf89d80 100644 --- a/tests/TokenTest.cpp +++ b/tests/TokenTest.cpp @@ -71,6 +71,18 @@ TEST(TokenTest, CreateTokenRS256) { token); } +TEST(TokenTest, CreateTokenEvpPkeyRS256) { + auto token = jwt::create().set_issuer("auth0").set_type("JWS").sign( + jwt::algorithm::rsa(jwt::helper::load_private_key_from_string(rsa_priv_key), EVP_sha256, "RS256")); + + ASSERT_EQ( + "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXUyJ9.eyJpc3MiOiJhdXRoMCJ9.VA2i1ui1cnoD6I3wnji1WAVCf29EekysvevGrT2GXqK1dDMc8" + "HAZCTQxa1Q8NppnpYV-hlqxh-X3Bb0JOePTGzjynpNZoJh2aHZD-GKpZt7OO1Zp8AFWPZ3p8Cahq8536fD8RiBES9jRsvChZvOqA7gMcFc4" + "YD0iZhNIcI7a654u5yPYyTlf5kjR97prCf_OXWRn-bYY74zna4p_bP9oWCL4BkaoRcMxi-IR7kmVcCnvbYqyIrKloXP2qPO442RBGqU7Ov9" + "sGQxiVqtRHKXZR9RbfvjrErY1KGiCp9M5i2bsUHadZEY44FE2jiOmx-uc2z5c05CCXqVSpfCjWbh9gQ", + token); +} + #if !defined(JWT_OPENSSL_1_0_0) TEST(TokenTest, CreateTokenRS256Encrypted) { // openssl genrsa -aes256 -out private.pem 2048 @@ -175,6 +187,28 @@ TEST(TokenTest, CreateTokenES256) { ASSERT_NO_THROW(jwt::verify().allow_algorithm(jwt::algorithm::es256(ecdsa256_pub_key, "", "", "")).verify(decoded)); } +TEST(TokenTest, CreateTokenEvpPkeyES256) { + + auto token = jwt::create().set_issuer("auth0").set_type("JWS").sign(jwt::algorithm::ecdsa( + jwt::helper::load_private_ec_key_from_string(ecdsa256_priv_key), EVP_sha256, "ES256", 64)); + + auto decoded = jwt::decode(token); + + ASSERT_THROW( + jwt::verify().allow_algorithm(jwt::algorithm::es256(ecdsa256_pub_key_invalid, "", "", "")).verify(decoded), + jwt::error::signature_verification_exception); + ASSERT_NO_THROW(jwt::verify().allow_algorithm(jwt::algorithm::es256(ecdsa256_pub_key, "", "", "")).verify(decoded)); +} + +TEST(TokenTest, CreateTokenEvpPkeyES256NoPrivate) { + ASSERT_THROW( + []() { + auto token = jwt::create().set_issuer("auth0").set_type("JWS").sign(jwt::algorithm::ecdsa( + jwt::helper::load_public_ec_key_from_string(ecdsa256_pub_key), EVP_sha256, "ES256", 64)); + }(), + jwt::error::signature_generation_exception); +} + TEST(TokenTest, CreateTokenES256NoPrivate) { ASSERT_THROW( []() { @@ -307,6 +341,23 @@ TEST(TokenTest, VerifyTokenRS256) { verify.verify(decoded_token); } +TEST(TokenTest, VerifyTokenEvpPkeyRS256) { + std::string token = + "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXUyJ9.eyJpc3MiOiJhdXRoMCJ9.VA2i1ui1cnoD6I3wnji1WAVCf29EekysvevGrT2GXqK1dDMc8" + "HAZCTQxa1Q8NppnpYV-hlqxh-X3Bb0JOePTGzjynpNZoJh2aHZD-GKpZt7OO1Zp8AFWPZ3p8Cahq8536fD8RiBES9jRsvChZvOqA7gMcFc4" + "YD0iZhNIcI7a654u5yPYyTlf5kjR97prCf_OXWRn-bYY74zna4p_bP9oWCL4BkaoRcMxi-IR7kmVcCnvbYqyIrKloXP2qPO442RBGqU7Ov9" + "sGQxiVqtRHKXZR9RbfvjrErY1KGiCp9M5i2bsUHadZEY44FE2jiOmx-uc2z5c05CCXqVSpfCjWbh9gQ"; + + auto verify = jwt::verify() + .allow_algorithm(jwt::algorithm::rsa(jwt::helper::load_private_key_from_string(rsa_priv_key), + EVP_sha256, "RS256")) + .with_issuer("auth0"); + + auto decoded_token = jwt::decode(token); + + verify.verify(decoded_token); +} + TEST(TokenTest, VerifyTokenRS256PublicOnly) { std::string token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXUyJ9.eyJpc3MiOiJhdXRoMCJ9.VA2i1ui1cnoD6I3wnji1WAVCf29EekysvevGrT2GXqK1dDMc8" @@ -321,6 +372,23 @@ TEST(TokenTest, VerifyTokenRS256PublicOnly) { verify.verify(decoded_token); } +TEST(TokenTest, VerifyTokenEvpPkeyRS256PublicOnly) { + std::string token = + "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXUyJ9.eyJpc3MiOiJhdXRoMCJ9.VA2i1ui1cnoD6I3wnji1WAVCf29EekysvevGrT2GXqK1dDMc8" + "HAZCTQxa1Q8NppnpYV-hlqxh-X3Bb0JOePTGzjynpNZoJh2aHZD-GKpZt7OO1Zp8AFWPZ3p8Cahq8536fD8RiBES9jRsvChZvOqA7gMcFc4" + "YD0iZhNIcI7a654u5yPYyTlf5kjR97prCf_OXWRn-bYY74zna4p_bP9oWCL4BkaoRcMxi-IR7kmVcCnvbYqyIrKloXP2qPO442RBGqU7Ov9" + "sGQxiVqtRHKXZR9RbfvjrErY1KGiCp9M5i2bsUHadZEY44FE2jiOmx-uc2z5c05CCXqVSpfCjWbh9gQ"; + + auto verify = jwt::verify() + .allow_algorithm(jwt::algorithm::rsa(jwt::helper::load_public_key_from_string(rsa_pub_key), + EVP_sha256, "RS256")) + .with_issuer("auth0"); + + auto decoded_token = jwt::decode(token); + + verify.verify(decoded_token); +} + TEST(TokenTest, VerifyTokenRS256PublicOnlyEncrypted) { // openssl genrsa -aes256 -out private.pem 2048 // openssl rsa -in private.pem -pubout -out public.pem @@ -548,6 +616,17 @@ TEST(TokenTest, VerifyTokenES256FailNoKey) { jwt::error::ecdsa_exception); } +TEST(TokenTest, VerifyTokenEvpPkeyES256FailNoKey) { + ASSERT_THROW( + []() { + auto verify = jwt::verify() + .allow_algorithm( + jwt::algorithm::ecdsa(jwt::helper::evp_pkey_handle{nullptr}, EVP_sha256, "ES256", 64)) + .with_issuer("auth0"); + }(), + jwt::error::ecdsa_exception); +} + TEST(TokenTest, VerifyTokenES256) { const std::string token = "eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJhdXRoMCJ9.4iVk3-Y0v4RT4_9IaQlp-8dZ_" "4fsTzIylgrPTDLrEvTHBTyVS3tgPbr2_IZfLETtiKRqCg0aQ5sh9eIsTTwB1g"; @@ -558,6 +637,17 @@ TEST(TokenTest, VerifyTokenES256) { verify.verify(decoded_token); } +TEST(TokenTest, VerifyTokenEvpPkeyES256) { + const std::string token = "eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJhdXRoMCJ9.4iVk3-Y0v4RT4_9IaQlp-8dZ_" + "4fsTzIylgrPTDLrEvTHBTyVS3tgPbr2_IZfLETtiKRqCg0aQ5sh9eIsTTwB1g"; + + auto verify = jwt::verify().allow_algorithm( + jwt::algorithm::ecdsa(jwt::helper::load_public_ec_key_from_string(ecdsa256_pub_key), EVP_sha256, "ES256", 64)); + auto decoded_token = jwt::decode(token); + + verify.verify(decoded_token); +} + TEST(TokenTest, VerifyTokenES256Fail) { const std::string token = "eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJhdXRoMCJ9.4iVk3-Y0v4RT4_9IaQlp-8dZ_" "4fsTzIylgrPTDLrEvTHBTyVS3tgPbr2_IZfLETtiKRqCg0aQ5sh9eIsTTwB1g";