Mercurial > hg > orthanc-education
view Sources/Security/RSAPrivateKey.cpp @ 77:80b663d5f8fe default tip
replaced boost::math::iround() by Orthanc::Math::llround()
| author | Sebastien Jodogne <s.jodogne@gmail.com> |
|---|---|
| date | Tue, 27 Jan 2026 17:05:03 +0100 |
| parents | 0f8c46d755e2 |
| children |
line wrap: on
line source
/** * SPDX-FileCopyrightText: 2024-2026 Sebastien Jodogne, EPL UCLouvain, Belgium * SPDX-License-Identifier: AGPL-3.0-or-later */ /** * Orthanc for Education * Copyright (C) 2024-2026 Sebastien Jodogne, EPL UCLouvain, Belgium * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU Affero General Public License * as published by the Free Software Foundation, either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. **/ #include "RSAPrivateKey.h" #include "../HttpToolbox.h" #include "OpenSSLSerializationContext.h" #include "SecurityConstants.h" #include <Toolbox.h> #include <cassert> #include <openssl/core_names.h> #include <openssl/pem.h> #include <openssl/rsa.h> static void GetBignumFromKey(std::string& data, const EVP_PKEY* key, const std::string& parameterName) { PointerRAII<BIGNUM> bignum(BN_free); if (!EVP_PKEY_get_bn_param(key, parameterName.c_str(), &bignum.GetValue()) || bignum.GetValue() == NULL) { throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); } int size = BN_num_bytes(bignum.GetValue()); if (size <= 0) { throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); } data.resize(size); assert(!data.empty()); if (!BN_bn2bin(bignum.GetValue(), reinterpret_cast<unsigned char*>(&data[0]))) { throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); } } void RSAPrivateKey::Generate(unsigned int bits) { key_.Clear(); PointerRAII<EVP_PKEY_CTX> context(EVP_PKEY_CTX_free); context.Assign(EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL)); if (EVP_PKEY_keygen_init(context.GetValue()) != 1 || EVP_PKEY_CTX_set_rsa_keygen_bits(context.GetValue(), bits) != 1 || EVP_PKEY_generate(context.GetValue(), &key_.GetValue()) != 1) { throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); } } void RSAPrivateKey::SerializePrivate(std::string& pem) { if (!IsValid()) { throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); } OpenSSLSerializationContext context; if (!PEM_write_bio_PrivateKey(context.GetValue(), key_.GetValue(), NULL, NULL, 0, NULL, NULL)) { throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "Failed to write private key to BIO"); } context.Write(pem); } void RSAPrivateKey::SerializePublic(std::string& pem) { if (!IsValid()) { throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); } OpenSSLSerializationContext context; if (!PEM_write_bio_PUBKEY(context.GetValue(), key_.GetValue())) { throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "Failed to write public key to BIO"); } context.Write(pem); } void RSAPrivateKey::Unserialize(const std::string& pem) { PointerRAII<BIO> bio(BIO_free, 1 /* success code of BIO_free() */); bio.Assign(BIO_new_mem_buf(pem.empty() ? NULL : pem.c_str(), pem.size())); key_.Assign(PEM_read_bio_PrivateKey(bio.GetValue(), NULL, NULL, NULL)); if (EVP_PKEY_base_id(key_.GetValue()) != EVP_PKEY_RSA) { throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, "The PEM does not contain a RSA private key"); } PointerRAII<EVP_PKEY_CTX> context(EVP_PKEY_CTX_free); context.Assign(EVP_PKEY_CTX_new(key_.GetValue(), NULL)); if (EVP_PKEY_private_check(context.GetValue()) != 1) { throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, "The PEM contains a RSA public key, not a private key"); } } void RSAPrivateKey::SignRS256(std::string& signature, const std::string& buffer) { if (!IsValid()) { throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); } PointerRAII<EVP_MD_CTX> context(EVP_MD_CTX_free); context.Assign(EVP_MD_CTX_new()); size_t size; if (EVP_DigestSignInit(context.GetValue(), NULL, EVP_sha256(), NULL, key_.GetValue()) != 1 || EVP_DigestSignUpdate(context.GetValue(), buffer.empty() ? NULL : buffer.c_str(), buffer.size()) != 1 || EVP_DigestSignFinal(context.GetValue(), NULL, &size) != 1) { throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "Cannot sign a memory buffer"); } signature.resize(size); if (size != 0 && EVP_DigestSignFinal(context.GetValue(), reinterpret_cast<unsigned char*>(&signature[0]), &size) != 1) { throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "Cannot sign a memory buffer"); } } void RSAPrivateKey::GetExponent(std::string& exponent) { if (!IsValid()) { throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); } GetBignumFromKey(exponent, key_.GetValue(), OSSL_PKEY_PARAM_RSA_E); } void RSAPrivateKey::GetModulus(std::string& modulus) { if (!IsValid()) { throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); } GetBignumFromKey(modulus, key_.GetValue(), OSSL_PKEY_PARAM_RSA_N); } void RSAPrivateKey::Export_JWKS_RS256(Json::Value& target, const std::string& keyId) { // https://stytch.com/blog/understanding-jwks/ target = Json::objectValue; target[JWKS_FIELD_KTY] = "RSA"; target[JWKS_FIELD_ALG] = "RS256"; target[JWKS_FIELD_USE] = "sig"; target[JWKS_FIELD_KID] = keyId; std::string exponent, modulus; GetExponent(exponent); GetModulus(modulus); std::string b64; HttpToolbox::EncodeBase64Url(b64, exponent); target[JWKS_FIELD_E] = b64; HttpToolbox::EncodeBase64Url(b64, modulus); target[JWKS_FIELD_N] = b64; } void RSAPrivateKey::ForgeJWT(std::string& jwt, const std::string& keyId, const Json::Value& payload) { Json::Value header; header[JWKS_FIELD_TYP] = "JWT"; header[JWKS_FIELD_ALG] = "RS256"; header[JWKS_FIELD_KID] = keyId; std::string headerString, payloadString; Orthanc::Toolbox::WriteFastJson(headerString, header); Orthanc::Toolbox::WriteFastJson(payloadString, payload); std::string headerBase64, payloadBase64; HttpToolbox::EncodeBase64Url(headerBase64, headerString); HttpToolbox::EncodeBase64Url(payloadBase64, payloadString); std::string signature; SignRS256(signature, headerBase64 + "." + payloadBase64); std::string signatureBase64; HttpToolbox::EncodeBase64Url(signatureBase64, signature); jwt = headerBase64 + "." + payloadBase64 + "." + signatureBase64; }
