Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove duplicated key handlers #318

Merged
merged 19 commits into from
Dec 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 76 additions & 84 deletions include/jwt-cpp/jwt.h
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,13 @@ namespace jwt {
no_key_provided,
invalid_key_size,
invalid_key,
create_context_failed
create_context_failed,
cert_load_failed,
get_key_failed,
write_key_failed,
write_cert_failed,
convert_to_pem_failed,

};
/**
* \brief Error category for ECDSA errors
Expand All @@ -181,6 +187,11 @@ namespace jwt {
case ecdsa_error::invalid_key_size: return "invalid key size";
case ecdsa_error::invalid_key: return "invalid key";
case ecdsa_error::create_context_failed: return "failed to create context";
case ecdsa_error::cert_load_failed: return "error loading cert into memory";
case ecdsa_error::get_key_failed: return "error getting key from certificate";
case ecdsa_error::write_key_failed: return "error writing key data in PEM format";
case ecdsa_error::write_cert_failed: return "error writing cert data in PEM format";
case ecdsa_error::convert_to_pem_failed: return "failed to convert key to pem";
default: return "unknown ECDSA error";
}
}
Expand Down Expand Up @@ -492,39 +503,40 @@ namespace jwt {
/**
* \brief Extract the public key of a pem certificate
*
* \param certstr String containing the certificate encoded as pem
* \param pw Password used to decrypt certificate (leave empty if not encrypted)
* \param ec error_code for error_detection (gets cleared if no error occurred)
* \tparam error_category jwt::error enum category to match with the keys being used
* \param certstr String containing the certificate encoded as pem
* \param pw Password used to decrypt certificate (leave empty if not encrypted)
* \param ec error_code for error_detection (gets cleared if no error occurred)
*/
inline std::string extract_pubkey_from_cert(const std::string& certstr, const std::string& pw,
std::error_code& ec) {
template<typename error_category = error::rsa_error>
std::string extract_pubkey_from_cert(const std::string& certstr, const std::string& pw, std::error_code& ec) {
ec.clear();
auto certbio = make_mem_buf_bio(certstr);
auto keybio = make_mem_buf_bio();
if (!certbio || !keybio) {
ec = error::rsa_error::create_mem_bio_failed;
ec = error_category::create_mem_bio_failed;
return {};
}

std::unique_ptr<X509, decltype(&X509_free)> cert(
PEM_read_bio_X509(certbio.get(), nullptr, nullptr, const_cast<char*>(pw.c_str())), X509_free);
if (!cert) {
ec = error::rsa_error::cert_load_failed;
ec = error_category::cert_load_failed;
return {};
}
std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)> key(X509_get_pubkey(cert.get()), EVP_PKEY_free);
if (!key) {
ec = error::rsa_error::get_key_failed;
ec = error_category::get_key_failed;
return {};
}
if (PEM_write_bio_PUBKEY(keybio.get(), key.get()) == 0) {
ec = error::rsa_error::write_key_failed;
ec = error_category::write_key_failed;
return {};
}
char* ptr = nullptr;
auto len = BIO_get_mem_data(keybio.get(), &ptr);
if (len <= 0 || ptr == nullptr) {
ec = error::rsa_error::convert_to_pem_failed;
ec = error_category::convert_to_pem_failed;
return {};
}
return {ptr, static_cast<size_t>(len)};
Expand All @@ -533,13 +545,15 @@ namespace jwt {
/**
* \brief Extract the public key of a pem certificate
*
* \param certstr String containing the certificate encoded as pem
* \param pw Password used to decrypt certificate (leave empty if not encrypted)
* \throw rsa_exception if an error occurred
* \tparam error_category jwt::error enum category to match with the keys being used
* \param certstr String containing the certificate encoded as pem
* \param pw Password used to decrypt certificate (leave empty if not encrypted)
* \throw templated error_category's type exception if an error occurred
*/
inline std::string extract_pubkey_from_cert(const std::string& certstr, const std::string& pw = "") {
template<typename error_category = error::rsa_error>
std::string extract_pubkey_from_cert(const std::string& certstr, const std::string& pw = "") {
std::error_code ec;
auto res = extract_pubkey_from_cert(certstr, pw, ec);
auto res = extract_pubkey_from_cert<error_category>(certstr, pw, ec);
error::throw_if_error(ec);
return res;
}
Expand Down Expand Up @@ -674,38 +688,40 @@ namespace jwt {
*
* The string should contain a pem encoded certificate or public key
*
* \tparam error_category jwt::error enum category to match with the keys being used
* \param key String containing the certificate encoded as pem
* \param password Password used to decrypt certificate (leave empty if not encrypted)
* \param ec error_code for error_detection (gets cleared if no error occurs)
*/
inline evp_pkey_handle load_public_key_from_string(const std::string& key, const std::string& password,
std::error_code& ec) {
template<typename error_category = error::rsa_error>
evp_pkey_handle load_public_key_from_string(const std::string& key, const std::string& password,
std::error_code& ec) {
ec.clear();
auto pubkey_bio = make_mem_buf_bio();
if (!pubkey_bio) {
ec = error::rsa_error::create_mem_bio_failed;
ec = error_category::create_mem_bio_failed;
return {};
}
if (key.substr(0, 27) == "-----BEGIN CERTIFICATE-----") {
auto epkey = helper::extract_pubkey_from_cert(key, password, ec);
auto epkey = helper::extract_pubkey_from_cert<error_category>(key, password, ec);
if (ec) return {};
const int len = static_cast<int>(epkey.size());
if (BIO_write(pubkey_bio.get(), epkey.data(), len) != len) {
ec = error::rsa_error::load_key_bio_write;
ec = error_category::load_key_bio_write;
return {};
}
} else {
const int len = static_cast<int>(key.size());
if (BIO_write(pubkey_bio.get(), key.data(), len) != len) {
ec = error::rsa_error::load_key_bio_write;
ec = error_category::load_key_bio_write;
return {};
}
}

evp_pkey_handle pkey(PEM_read_bio_PUBKEY(
pubkey_bio.get(), nullptr, nullptr,
(void*)password.data())); // NOLINT(google-readability-casting) requires `const_cast`
if (!pkey) ec = error::rsa_error::load_key_bio_read;
if (!pkey) ec = error_category::load_key_bio_read;
return pkey;
}

Expand All @@ -714,52 +730,59 @@ namespace jwt {
*
* The string should contain a pem encoded certificate or public key
*
* \param key String containing the certificate or key encoded as pem
* \param password Password used to decrypt certificate or key (leave empty if not encrypted)
* \throw rsa_exception if an error occurred
* \tparam error_category jwt::error enum category to match with the keys being used
* \param key String containing the certificate encoded as pem
* \param password Password used to decrypt certificate (leave empty if not encrypted)
* \throw Templated error_category's type exception if an error occurred
*/
template<typename error_category = error::rsa_error>
inline evp_pkey_handle load_public_key_from_string(const std::string& key, const std::string& password = "") {
std::error_code ec;
auto res = load_public_key_from_string(key, password, ec);
auto res = load_public_key_from_string<error_category>(key, password, ec);
error::throw_if_error(ec);
return res;
}

/**
* \brief Load a private key from a string.
*
* \param key String containing a private key as pem
* \param password Password used to decrypt key (leave empty if not encrypted)
* \param ec error_code for error_detection (gets cleared if no error occurs)
* \tparam error_category jwt::error enum category to match with the keys being used
* \param key String containing a private key as pem
* \param password Password used to decrypt key (leave empty if not encrypted)
* \param ec error_code for error_detection (gets cleared if no error occurs)
*/
template<typename error_category = error::rsa_error>
inline evp_pkey_handle load_private_key_from_string(const std::string& key, const std::string& password,
std::error_code& ec) {
auto privkey_bio = make_mem_buf_bio();
if (!privkey_bio) {
ec = error::rsa_error::create_mem_bio_failed;
ec.clear();
auto private_key_bio = make_mem_buf_bio();
if (!private_key_bio) {
ec = error_category::create_mem_bio_failed;
return {};
}
const int len = static_cast<int>(key.size());
if (BIO_write(privkey_bio.get(), key.data(), len) != len) {
ec = error::rsa_error::load_key_bio_write;
if (BIO_write(private_key_bio.get(), key.data(), len) != len) {
ec = error_category::load_key_bio_write;
return {};
}
evp_pkey_handle pkey(
PEM_read_bio_PrivateKey(privkey_bio.get(), nullptr, nullptr, const_cast<char*>(password.c_str())));
if (!pkey) ec = error::rsa_error::load_key_bio_read;
PEM_read_bio_PrivateKey(private_key_bio.get(), nullptr, nullptr, const_cast<char*>(password.c_str())));
if (!pkey) ec = error_category::load_key_bio_read;
return pkey;
}

/**
* \brief Load a private key from a string.
*
* \param key String containing a private key as pem
* \param password Password used to decrypt key (leave empty if not encrypted)
* \throw rsa_exception if an error occurred
* \tparam error_category jwt::error enum category to match with the keys being used
* \param key String containing a private key as pem
* \param password Password used to decrypt key (leave empty if not encrypted)
* \throw Templated error_category's type exception if an error occurred
*/
template<typename error_category = error::rsa_error>
inline evp_pkey_handle load_private_key_from_string(const std::string& key, const std::string& password = "") {
std::error_code ec;
auto res = load_private_key_from_string(key, password, ec);
auto res = load_private_key_from_string<error_category>(key, password, ec);
error::throw_if_error(ec);
return res;
}
Expand All @@ -768,95 +791,64 @@ namespace jwt {
* \brief Load a public key from a string.
*
* The string should contain a pem encoded certificate or public key
*
* \deprecated Use the templated version load_private_key_from_string with error::ecdsa_error
*
* \param key String containing the certificate encoded as pem
* \param password Password used to decrypt certificate (leave empty if not encrypted)
* \param ec error_code for error_detection (gets cleared if no error occurs)
*/
inline evp_pkey_handle load_public_ec_key_from_string(const std::string& key, const std::string& password,
std::error_code& ec) {
ec.clear();
auto pubkey_bio = make_mem_buf_bio();
if (!pubkey_bio) {
ec = error::ecdsa_error::create_mem_bio_failed;
return {};
}
if (key.substr(0, 27) == "-----BEGIN CERTIFICATE-----") {
auto epkey = helper::extract_pubkey_from_cert(key, password, ec);
if (ec) return {};
const int len = static_cast<int>(epkey.size());
if (BIO_write(pubkey_bio.get(), epkey.data(), len) != len) {
ec = error::ecdsa_error::load_key_bio_write;
return {};
}
} else {
const int len = static_cast<int>(key.size());
if (BIO_write(pubkey_bio.get(), key.data(), len) != len) {
ec = error::ecdsa_error::load_key_bio_write;
return {};
}
}

evp_pkey_handle pkey(PEM_read_bio_PUBKEY(
pubkey_bio.get(), nullptr, nullptr,
(void*)password.data())); // NOLINT(google-readability-casting) requires `const_cast`
if (!pkey) ec = error::ecdsa_error::load_key_bio_read;
return pkey;
return load_public_key_from_string<error::ecdsa_error>(key, password, ec);
}

/**
* \brief Load a public key from a string.
*
* The string should contain a pem encoded certificate or public key
*
* \deprecated Use the templated version load_private_key_from_string with error::ecdsa_error
*
* \param key String containing the certificate or key encoded as pem
* \param password Password used to decrypt certificate or key (leave empty if not encrypted)
* \throw ecdsa_exception if an error occurred
*/
inline evp_pkey_handle load_public_ec_key_from_string(const std::string& key,
const std::string& password = "") {
std::error_code ec;
auto res = load_public_ec_key_from_string(key, password, ec);
auto res = load_public_key_from_string<error::ecdsa_error>(key, password, ec);
error::throw_if_error(ec);
return res;
}

/**
* \brief Load a private key from a string.
*
* \deprecated Use the templated version load_private_key_from_string with error::ecdsa_error
*
* \param key String containing a private key as pem
* \param password Password used to decrypt key (leave empty if not encrypted)
* \param ec error_code for error_detection (gets cleared if no error occurs)
*/
inline evp_pkey_handle load_private_ec_key_from_string(const std::string& key, const std::string& password,
std::error_code& ec) {
auto privkey_bio = make_mem_buf_bio();
if (!privkey_bio) {
ec = error::ecdsa_error::create_mem_bio_failed;
return {};
}
const int len = static_cast<int>(key.size());
if (BIO_write(privkey_bio.get(), key.data(), len) != len) {
ec = error::ecdsa_error::load_key_bio_write;
return {};
}
evp_pkey_handle pkey(
PEM_read_bio_PrivateKey(privkey_bio.get(), nullptr, nullptr, const_cast<char*>(password.c_str())));
if (!pkey) ec = error::ecdsa_error::load_key_bio_read;
return pkey;
return load_private_key_from_string<error::ecdsa_error>(key, password, ec);
}

/**
* \brief Load a private key from a string.
*
* \deprecated Use the templated version load_private_key_from_string with error::ecdsa_error
*
* \param key String containing a private key as pem
* \param password Password used to decrypt key (leave empty if not encrypted)
* \throw ecdsa_exception if an error occurred
*/
inline evp_pkey_handle load_private_ec_key_from_string(const std::string& key,
const std::string& password = "") {
std::error_code ec;
auto res = load_private_ec_key_from_string(key, password, ec);
auto res = load_private_key_from_string<error::ecdsa_error>(key, password, ec);
error::throw_if_error(ec);
return res;
}
Expand Down
2 changes: 1 addition & 1 deletion tests/HelperTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ TEST(HelperTest, ErrorCodeMessages) {
ASSERT_EQ(std::error_code(static_cast<jwt::error::rsa_error>(i)).message(),
std::error_code(static_cast<jwt::error::rsa_error>(-1)).message());

for (i = 10; i < 17; i++) {
for (i = 10; i < 22; i++) {
ASSERT_NE(std::error_code(static_cast<jwt::error::ecdsa_error>(i)).message(),
std::error_code(static_cast<jwt::error::ecdsa_error>(-1)).message());
}
Expand Down
14 changes: 13 additions & 1 deletion tests/Keys.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,19 @@ d3QtY3BwMQ8wDQYDVQQLDAZnaXRodWIxFDASBgNVBAMMC2V4YW1wbGUuY29tMCow
BQYDK2VwAyEAUdLe1SUWxc/95f39pfmuwe1SLHpFXf5gcRQlMH2sjgwwBQYDK2Vw
A0EAezYcLIUnyy86uUnAZdAMPn7wTruNKtG36GrTF3PF4dtdoGF1OV5DLnNK0Hbs
3GyYtaZs6AEHwDXl/INXu2zoCQ==
-----END CERTIFICATE-----)";
-----END CERTIFICATE-----
)";
// openssl x509 -outform der -in ed25519_certificate.pem -out ed25519_certificate.der
// openssl base64 -in ed25519_certificate.der -out ed25519_certificate.b64
std::string ed25519_certificate_base64_der = "MIIBjzCCAUECFCQlWQxMEMe4c3OOimH4/y+o/HpfMAUGAytlcDBqMQswCQYDVQQG"
"EwJDQTEPMA0GA1UECAwGUXVlYmVjMREwDwYDVQQHDAhNb250cmVhbDEQMA4GA1UE"
"CgwHand0LWNwcDEPMA0GA1UECwwGZ2l0aHViMRQwEgYDVQQDDAtleGFtcGxlLmNv"
"bTAeFw0yMDA3MzAyMTIwMDBaFw0yMjA2MzAyMTIwMDBaMGoxCzAJBgNVBAYTAkNB"
"MQ8wDQYDVQQIDAZRdWViZWMxETAPBgNVBAcMCE1vbnRyZWFsMRAwDgYDVQQKDAdq"
"d3QtY3BwMQ8wDQYDVQQLDAZnaXRodWIxFDASBgNVBAMMC2V4YW1wbGUuY29tMCow"
"BQYDK2VwAyEAUdLe1SUWxc/95f39pfmuwe1SLHpFXf5gcRQlMH2sjgwwBQYDK2Vw"
"A0EAezYcLIUnyy86uUnAZdAMPn7wTruNKtG36GrTF3PF4dtdoGF1OV5DLnNK0Hbs"
"3GyYtaZs6AEHwDXl/INXu2zoCQ==";
std::string ed448_priv_key = R"(-----BEGIN PRIVATE KEY-----
MEcCAQAwBQYDK2VxBDsEOZNyV4kIWehIWSsPCnDEZbBF+g2WoUgUwox8eQJTq8Hz
y4okU+JZAV8RqQ270fJL/Safvvc1SbbF1A==
Expand Down
Loading
Loading