Issue
This working RSA OpenSSL code
void SwapBytes( unsigned char *pv, size_t n )
{
unsigned char *p = pv;
size_t lo, hi;
for ( lo = 0, hi = n - 1; hi > lo; lo++, hi-- )
{
char tmp = p[lo];
p[lo] = p[hi];
p[hi] = tmp;
}
}
void RSA(unsigned char *plaintext, unsigned char *ciphertext)
{
BIGNUM *bnN = NULL;
BIGNUM *bnE = NULL;
RSA *keys = RSA_new();
BN_hex2bn(&bnN, modulus);
BN_hex2bn(&bnE, public_exp);
RSA_set0_key(keys, bnN, bnE, NULL);
int modulus_size = RSA_size(keys);
SwapBytes(plaintext, modulus_size);
int cipher_len = RSA_public_encrypt(modulus_size, plaintext, ciphertext, keys, RSA_NO_PADDING);
RSA_free(keys);
SwapBytes(ciphertext, modulus_size);
}
when compiled produces deprecated warnings such as
/mnt/c/Projects/src/rsa.cpp:37:102: warning: ‘int RSA_public_encrypt(int, const unsigned char*, unsigned char*, RSA*, int)’ is deprecated: Since OpenSSL 3.0 [-Wdeprecated-declarations]
37 | int cipher_len = RSA_public_encrypt(modulus_size, plaintext, ciphertext, keys, RSA_NO_PADDING);
which can be suppressed using this compiler option
-Wno-deprecated-declarations
However, the OpenSSL dev team notes:
Use of the low level APIs has been informally discouraged by the OpenSSL dev team for a long time. However in OpenSSL 3.0 this is made more formal. All such low level APIs have been deprecated. You may still use them in your applications, but you may start to see deprecation warnings during compilation (dependent on compiler support for this). Deprecated APIs may be removed from future versions of OpenSSL so you are strongly encouraged to update your code to use the high level APIs instead.
Question
It's suggested to replace the above low level API's with EVP.
Is there an RSA example to replace the above code with OpenSSL EVP functions?
Solution
I'm no openssl expert, but going through the hard to read DOC's I figured out the below conversation which from my testing generates the same output as as your function (assuming no SwapBytes is called as you don't provide that).
It can be broken down into three parts I think.
- setting up PARAMS array with the key parameters using OSSL_PARAM_BLD_xx functions
- creating the RSA key from the PARAMS array using using EVP_PKEY_fromdata function
- encrypting the data using EVP_PKEY_xxx functions
Sample:
#include <cassert>
#include <openssl/evp.h>
#include <openssl/rsa.h>
#include <openssl/param_build.h>
template<typename T, typename D>
std::unique_ptr<T, D> make_handle(T* handle, D deleter)
{
return std::unique_ptr<T, D>{handle, deleter};
}
void newRSA(unsigned char *plaintext, unsigned char *ciphertext, int length, char const* modulus, char const* public_exp)
{
// build BIGNUM values
BIGNUM* num{ nullptr };
BN_hex2bn(&num, modulus);
assert(num != nullptr);
auto bnN = make_handle(num, BN_free);
num = nullptr;
BN_hex2bn(&num, public_exp);
assert(num != nullptr);
auto bnE = make_handle(num, BN_free);
// Build params to create PARAM array
auto params_build = make_handle(OSSL_PARAM_BLD_new(), OSSL_PARAM_BLD_free);
assert(params_build.get() != nullptr);
auto result = OSSL_PARAM_BLD_push_BN(params_build.get(), "n", bnN.get());
assert(result == 1);
result = OSSL_PARAM_BLD_push_BN(params_build.get(), "e", bnE.get());
assert(result == 1);
result = OSSL_PARAM_BLD_push_BN(params_build.get(), "d", nullptr);
assert(result == 1);
// create PARAMS array
auto params = make_handle(OSSL_PARAM_BLD_to_param(params_build.get()), OSSL_PARAM_free);
// cleanup params build up
params_build.reset();
bnN.reset();
bnE.reset();
// Create RSA key from params
auto ctx = make_handle(EVP_PKEY_CTX_new_from_name(nullptr, "RSA", nullptr), EVP_PKEY_CTX_free);
assert(ctx.get() != nullptr);
result = EVP_PKEY_fromdata_init(ctx.get());
assert(result == 1);
EVP_PKEY *key = nullptr;
result = EVP_PKEY_fromdata(ctx.get(), &key, EVP_PKEY_KEYPAIR, params.get());
assert(result == 1);
auto keys = make_handle(key, EVP_PKEY_free);
// cleanup params
params.reset();
// RSA_size equivalent
int modulus_size = (EVP_PKEY_get_bits(keys.get()) + 7) / 8;
assert(length == modulus_size);
// setup encryption from the key generated above
auto enc_ctx = make_handle(EVP_PKEY_CTX_new(keys.get(), nullptr), EVP_PKEY_CTX_free);
assert(enc_ctx.get() != nullptr);
result = EVP_PKEY_encrypt_init(enc_ctx.get());
assert(result == 1);
// encrypt using RSA_NO_PADDING
result = EVP_PKEY_CTX_set_rsa_padding(enc_ctx.get(), RSA_NO_PADDING);
assert(result == 1);
// encrypt
size_t outlen = length;
result = EVP_PKEY_encrypt(enc_ctx.get(), ciphertext, &outlen, plaintext, length);
assert(result == 1);
assert(outlen == length);
}
Answered By - Shane Powell Answer Checked By - Marilyn (WPSolving Volunteer)