c++opensslrsa

Generate RSA key pairs using openssl 3.0 in c++


Could anyone share a simple example to generate key pairs using openssl 3.0 c++ library? The official site is no good, a lot of functions are deprecated but still listed there, and gpt generated code failed at EVP_PKEY_copy_parameters. This is the code I got from gpt, which doesn't work.

#include <iostream>
#include <memory>
#include <openssl/err.h>
#include <openssl/pem.h>
#include <openssl/rsa.h>
#include <string>
#include <vector>

// Custom deleter for EVP_PKEY
struct EVP_PKEY_Deleter {
  void operator()(EVP_PKEY *pkey) const { EVP_PKEY_free(pkey); }
};

// Type alias for unique_ptr with EVP_PKEY and custom deleter
using Key = std::unique_ptr<EVP_PKEY, EVP_PKEY_Deleter>;

std::pair<Key, Key> generate_rsa_keys() {
  EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, nullptr);
  if (!ctx) {
    throw std::runtime_error("Failed to create context");
  }

  EVP_PKEY *pkey_raw = EVP_RSA_gen(2048);
  if (!pkey_raw) {
    EVP_PKEY_CTX_free(ctx);
    throw std::runtime_error("Key generation failed");
  }

  EVP_PKEY *public_key_raw = EVP_PKEY_new();
  if (!public_key_raw) {
    EVP_PKEY_free(pkey_raw);
    EVP_PKEY_CTX_free(ctx);
    throw std::runtime_error("Failed to create public key");
  }

  if (EVP_PKEY_copy_parameters(public_key_raw, pkey_raw) <= 0) {
    EVP_PKEY_free(pkey_raw);
    EVP_PKEY_free(public_key_raw);
    EVP_PKEY_CTX_free(ctx);
    throw std::runtime_error("Failed to copy parameters");
  }

  Key pkey(pkey_raw);
  Key public_key(public_key_raw);

  return {std::move(pkey), std::move(public_key)};
}

int main() {
    auto [pkey, public_key] = generate_rsa_keys();
}

edit: For any rookie still looking, here's another alternative beside accepted answer. https://github.com/leventkaragol/libcpp-crypto

OpenSSL's document surely didn't help...


Solution

  • It's a bit tricky to find everything in the documentation but in the end new API requires slightly less coding than deprecated one. You can e.g. follow these steps:

    Complete example (without errors checks):

    constexpr size_t kBits = 1024;
    
    int main() {
        EVP_PKEY* key = EVP_RSA_gen(kBits);
        BIO* bio = BIO_new(BIO_s_mem());
        EVP_PKEY_print_public(bio, key, 4, nullptr);
        // I hardcoded 2056, because I'm lazy, but probably there is some way to query the number of available bytes from BIO
        std::string keyStr(2056, 0);
        std::cout << "num bytes read: " << BIO_read(bio, keyStr.data(), keyStr.size()) << std::endl;
        std::cout << "keyStr: " << keyStr << std::endl;
        BIO_free_all(bio);
        EVP_PKEY_free(key);
    }