# Botan

# OpenSSL 1.1 to Botan 3.x Migration

This aims to be a rough guide for migrating applications from OpenSSL 1.1 to Botan 3.x.

This guide attempts to be, but is not, complete. If you run into a problem while migrating code that does not seem to be described here, please open an issue on GitHub.

Note

The OpenSSL code snippets in this guide may not be 100% correct. They are intended to show the differences in using OpenSSL’s and Botan’s APIs rather to be a complete and correct example.

## General Remarks

- Botan is a C++ library, whereas OpenSSL is a C library

- Botan also provides a C API for most of its functionality, but it is not a 1:1 mapping of the C++ API

- With OpenSSL’s API, there are sometimes multiple ways to achieve the same result, whereas Botan’s API is more consistent

- OpenSSL’s API is mostly underdocumented, whereas Botan targets 100% Doxygen coverage for all public API

- It is often hard to find example code for OpenSSL, whereas Botan provides many examples and lots of test code.

## X.509

Consider the following application code that uses OpenSSL to verify a certificate chain consisting of an end-entity certificate, two untrusted intermediate certificates, and a trusted root certificate.

    #include <openssl/x509.h>
    #include <openssl/pem.h>
    #include <openssl/ssl.h>

    int main() {
        // Create a new X.509 store
        X509_STORE *store = X509_STORE_new();

        // Load the root certificate
        FILE* rootCertFileHandle = fopen("root.crt", "r");
        X509* rootCert = PEM_read_X509(rootCertFileHandle, NULL, NULL, NULL);
        X509_STORE_add_cert(store, rootCert);
        fclose(rootCertFileHandle);

        // Create a new X.509 store context
        X509_STORE_CTX *ctx = X509_STORE_CTX_new();
        X509_STORE_CTX_init(ctx, store, NULL, NULL);

        // Load the intermediate certificates
        FILE* intermediateCertFileHandle1 = fopen("int2.crt", "r");
        FILE* intermediateCertFileHandle2 = fopen("int1.crt", "r");
        X509* intermediateCert1 = PEM_read_X509(intermediateCertFileHandle1, NULL, NULL, NULL);
        X509* intermediateCert2 = PEM_read_X509(intermediateCertFileHandle2, NULL, NULL, NULL);
        X509_STORE_CTX_trusted_stack(ctx, sk_X509_new_null());
        sk_X509_push(X509_STORE_CTX_get0_untrusted(ctx), intermediateCert1);
        sk_X509_push(X509_STORE_CTX_get0_untrusted(ctx), intermediateCert2);
        fclose(intermediateCertFileHandle1);
        fclose(intermediateCertFileHandle2);

        // Load the end-entity certificate
        FILE* endEntityCertFileHandle = fopen("ee.crt", "r");
        X509* endEntityCert = PEM_read_X509(endEntityCertFileHandle, NULL, NULL, NULL);
        X509_STORE_CTX_set_cert(ctx, endEntityCert);
        fclose(endEntityCertFileHandle);

        // Verify the certificate chain
        int result = X509_verify_cert(ctx);
        if(result != 1) {
            // Verification failed
            X509_STORE_CTX_free(ctx);
            X509_STORE_free(store);
            return -1;
        }

        // Verification succeeded
        X509_STORE_CTX_free(ctx);
        X509_STORE_free(store);
        return 0;
    }

First, we create a new `X509_STORE` object and add the trusted root certificate. Then we add the intermediate certificates to the untrusted certificate stack. Finally, we set the end-entity certificate and call `X509_verify_cert()` to verify the whole certificate chain.

Here is the equivalent C++ code using Botan:

    #include <botan/certstor_system.h>
    #include <botan/x509cert.h>
    #include <botan/x509path.h>

    int main() {
       // Create a certificate store and add a locally trusted CA certificate
       Botan::Certificate_Store_In_Memory customStore;
       customStore.add_certificate(Botan::X509_Certificate("root.crt"));

       // Additionally trust all system-specific CA certificates
       Botan::System_Certificate_Store systemStore;
       const std::vector<Botan::Certificate_Store*> trusted_roots{&customStore, &systemStore};

       // Load the end entity certificate and two untrusted intermediate CAs from file
       std::vector<Botan::X509_Certificate> end_certs;
       end_certs.emplace_back(Botan::X509_Certificate("ee.crt"));    // The end-entity certificate, must come first
       end_certs.emplace_back(Botan::X509_Certificate("int2.crt"));  // intermediate 2
       end_certs.emplace_back(Botan::X509_Certificate("int1.crt"));  // intermediate 1

       // Optional: Set up restrictions, e.g. min. key strength, maximum age of OCSP responses
       const Botan::Path_Validation_Restrictions restrictions;

       // Optional: Specify usage type, compared against the key usage in end_certs[0]
       const Botan::Usage_Type usage = Botan::Usage_Type::UNSPECIFIED;

       // Optional: Specify hostname, if not empty, compared against the DNS name in end_certs[0]
       const std::string hostname;

       const Botan::Path_Validation_Result validationResult =
          Botan::x509_path_validate(end_certs, restrictions, trusted_roots, hostname, usage);

       if(!validationResult.successful_validation()) {
          // call validationResult.result() to get the overall status code
          return -1;
       }

       return 0;  // Verification succeeded
    }

First, we create a `Certificate_Store_In_Memory` object and add the trusted root certificate. Additionally, we use `System_Certificate_Store` to load all trusted root certificates from the operating system’s certificate store to trust. Botan provides several different Certificate Stores, including certificate stores that load certificates from a directory or from an SQL database. It even provides an interface for implementing your own certificate store. Then we add the end-entity certificate and the intermediate certificates to the `end_certs` chain. Optionally, we can set up path validation restrictions, specify usage and hostname for DNS, and then call `x509_path_validate()` to verify the certificate chain.

## Random Number Generation

Consider the following application code to generate random bytes using OpenSSL.

    #include <openssl/rand.h>
    #include <iostream>

    int main() {
        unsigned char buffer[16]; // Buffer to hold 16 random bytes

        if(RAND_bytes(buffer, sizeof(buffer)) != 1) {
            std::cerr << "Error generating random bytes.\n";
            return 1;
        }

        // Print the random bytes in hexadecimal format
        for(int i = 0; i < sizeof(buffer); i++) {
            printf("%02X", buffer[i]);
        }
        printf("\n");

        return 0;
    }

This example uses the `RAND_bytes()` function to generate 16 random bytes, e.g., for a 128-bit AES key, and prints it on the console.

Here is the equivalent C++ code using Botan:

    #include <botan/auto_rng.h>
    #include <botan/hex.h>
    #include <iostream>

    int main() {
        Botan::AutoSeeded_RNG rng;

        const Botan::secure_vector<uint8_t> buffer = rng.random_vec(16);

        // Print the random bytes in hexadecimal format
        std::cout << Botan::hex_encode(buffer) << std::endl;

        return 0;
    }

This snippet uses the `AutoSeeded_RNG` class to generate the 16 random bytes. Botan provides different Random Number Generators, including system-specific as well as system-independent software and hardware-based generators, and a comprehensive interface for implementing your own random number generator, if required. `AutoSeeded_RNG` is the recommended random number generator for most applications. The `random_vec()` function returns the requested number of random bytes passed. Botan provides a `hex_encode()` function that converts the random bytes to a hexadecimal string.

## Hash Functions

Consider the following application code to hash some data using OpenSSL.

    #include <openssl/evp.h>
    #include <openssl/sha.h>
    #include <iostream>
    #include <vector>

    void printHash(EVP_MD_CTX* ctx, const std::string& name) {
        unsigned char hash[EVP_MAX_MD_SIZE];
        unsigned int lengthOfHash = 0;

        EVP_DigestFinal_ex(ctx, hash, &lengthOfHash);

        std::cout << name << ": ";
        for(unsigned int i = 0; i < lengthOfHash; ++i) {
            std::cout << std::hex << std::setw(2) << std::setfill('0') << (int)hash[i];
        }
        std::cout << std::endl;
    }

    int main() {
        EVP_MD_CTX *ctx1 = EVP_MD_CTX_new();
        EVP_MD_CTX *ctx2 = EVP_MD_CTX_new();
        EVP_MD_CTX *ctx3 = EVP_MD_CTX_new();

        EVP_DigestInit_ex(ctx1, EVP_sha256(), NULL);
        EVP_DigestInit_ex(ctx2, EVP_sha384(), NULL);
        EVP_DigestInit_ex(ctx3, EVP_sha3_512(), NULL);

        std::vector<uint8_t> buffer(2048);
        while(std::cin.good()) {
            std::cin.read(reinterpret_cast<char*>(buffer.data()), buffer.size());
            std::streamsize bytesRead = std::cin.gcount();

            EVP_DigestUpdate(ctx1, buffer.data(), bytesRead);
            EVP_DigestUpdate(ctx2, buffer.data(), bytesRead);
            EVP_DigestUpdate(ctx3, buffer.data(), bytesRead);
        }

        printHash(ctx1, "SHA-256");
        printHash(ctx2, "SHA-384");
        printHash(ctx3, "SHA-3-512");

        EVP_MD_CTX_free(ctx1);
        EVP_MD_CTX_free(ctx2);
        EVP_MD_CTX_free(ctx3);

        return 0;
    }

This example uses the `EVP_DigestInit_ex()`, `EVP_DigestUpdate()`, and `EVP_DigestFinal_ex()` functions to hash data using SHA-256, SHA-384, and SHA-3-512. The `printHash()` function is used to print the hash values in hexadecimal format.

Here is the equivalent C++ code using Botan:

    #include <botan/hash.h>
    #include <botan/hex.h>

    #include <iostream>

    int main() {
       const auto hash1 = Botan::HashFunction::create_or_throw("SHA-256");
       const auto hash2 = Botan::HashFunction::create_or_throw("SHA-384");
       const auto hash3 = Botan::HashFunction::create_or_throw("SHA-3");
       std::vector<uint8_t> buf(2048);

       while(std::cin.good()) {
          // read STDIN to buffer
          std::cin.read(reinterpret_cast<char*>(buf.data()), static_cast<std::streamsize>(buf.size()));
          const auto readcount = static_cast<size_t>(std::cin.gcount());
          // update hash computations with read data
          hash1->update(std::span{buf}.first(readcount));
          hash2->update(std::span{buf}.first(readcount));
          hash3->update(std::span{buf}.first(readcount));
       }
       std::cout << "SHA-256: " << Botan::hex_encode(hash1->final()) << '\n';
       std::cout << "SHA-384: " << Botan::hex_encode(hash2->final()) << '\n';
       std::cout << "SHA-3: " << Botan::hex_encode(hash3->final()) << '\n';
       return 0;
    }

This example uses the `HashFunction` interface to hash data using SHA-256, SHA-384, and SHA-3-512. The `hash()` function is used to hash the data and the `output_length()` function is used to determine the length of the hash value. Botan provides a comprehensive list of hash functions, including all SHA-2 and SHA-3 variants, as well as message authentication codes and key derivation functions.

## Symmetric Encryption

Consider the following application code to encrypt some data with AES using OpenSSL.

    #include <openssl/aes.h>
    #include <openssl/evp.h>
    #include <iostream>
    #include <iomanip>

    int main() {
        // Hex-encoded key and plaintext block
        const char* key_hex = "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F";
        const char* plaintext_hex = "00112233445566778899AABBCCDDEEFF";

        // Convert hex-encoded key and plaintext block to binary
        unsigned char key[32], plaintext[16];
        for(int i = 0; i < 32; i++) {
            sscanf(&key_hex[i*2], "%02x", &key[i]);
        }
        for(int i = 0; i < 16; i++) {
            sscanf(&plaintext_hex[i*2], "%02x", &plaintext[i]);
        }

        // Encrypt
        unsigned char ciphertext[16], iv_enc[AES_BLOCK_SIZE] = {0};
        EVP_CIPHER_CTX *ctx_enc = EVP_CIPHER_CTX_new();
        EVP_EncryptInit_ex(ctx_enc, EVP_aes_256_cbc(), NULL, key, iv_enc);
        int outlen1;
        EVP_EncryptUpdate(ctx_enc, ciphertext, &outlen1, plaintext, sizeof(plaintext));
        EVP_EncryptFinal_ex(ctx_enc, ciphertext + outlen1, &outlen1);

        // Print ciphertext in hexadecimal format
        for(int i = 0; i < 16; i++) {
            printf("%02X", ciphertext[i]);
        }
        printf("\n");

        return 0;
    }

This example uses the `EVP_EncryptInit_ex()`, `EVP_EncryptUpdate()`, and `EVP_EncryptFinal_ex()` functions to encrypt a 128-bit plaintext block with a 256-bit key using AES. The key and plaintext block are hex-decoded and converted to binary before encryption.

Here is the equivalent C++ code using Botan:

    #include <botan/auto_rng.h>
    #include <botan/cipher_mode.h>
    #include <botan/hex.h>
    #include <botan/rng.h>

    #include <iostream>

    int main() {
       Botan::AutoSeeded_RNG rng;

       const std::string plaintext(
          "Your great-grandfather gave this watch to your granddad for good "
          "luck. Unfortunately, Dane's luck wasn't as good as his old man's.");
       const Botan::secure_vector<uint8_t> key = Botan::hex_decode_locked("2B7E151628AED2A6ABF7158809CF4F3C");

       const auto enc = Botan::Cipher_Mode::create_or_throw("AES-128/CBC/PKCS7", Botan::Cipher_Dir::Encryption);
       enc->set_key(key);

       // generate fresh nonce (IV)
       const auto iv = rng.random_vec<std::vector<uint8_t>>(enc->default_nonce_length());

       // Copy input data to a buffer that will be encrypted
       Botan::secure_vector<uint8_t> pt(plaintext.data(), plaintext.data() + plaintext.length());

       enc->start(iv);
       enc->finish(pt);

       std::cout << enc->name() << " with iv " << Botan::hex_encode(iv) << " " << Botan::hex_encode(pt) << '\n';
       return 0;
    }

This example uses the `CipherMode` interface to encrypt a 128-bit plaintext block with a 256-bit key using AES in CBC mode with PKCS#7 padding. The `set_key()` function is used to set the key and the `start()` and `finish()` functions are used to encrypt the plaintext block.

To learn more about the `BlockCipher` and `CipherMode` interfaces, including a list of all available block ciphers and cipher modes, see the Block Ciphers and Cipher Modes handbook sections.

## Asymmetric Encryption

Consider the following application code to encrypt some data with RSA using OpenSSL.

    #include <openssl/evp.h>
    #include <openssl/pem.h>
    #include <openssl/rsa.h>
    #include <openssl/err.h>
    #include <string.h>
    #include <stdio.h>

    int main() {
        // Load public key
        FILE* pubKeyFile = fopen("public.pem", "r");
        if(pubKeyFile == NULL) {
            fprintf(stderr, "Error opening public key file.\n");
            return 1;
        }
        EVP_PKEY* pubKey = PEM_read_PUBKEY(pubKeyFile, NULL, NULL, NULL);
        fclose(pubKeyFile);

        // Load private key
        FILE* privKeyFile = fopen("private.pem", "r");
        if(privKeyFile == NULL) {
            fprintf(stderr, "Error opening private key file.\n");
            return 1;
        }
        EVP_PKEY* privKey = PEM_read_PrivateKey(privKeyFile, NULL, NULL, NULL);
        fclose(privKeyFile);

        // String to encrypt
        unsigned char* plaintext = "Your great-grandfather gave this watch to your granddad for good luck. Unfortunately, Dane's luck wasn't as good as his old man's.";
        size_t plaintext_len = strlen(plaintext);

        // Encrypt
        EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(pubKey, NULL);
        EVP_PKEY_encrypt_init(ctx);
        EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING);
        EVP_PKEY_CTX_set_rsa_oaep_md(ctx, EVP_sha256());
        size_t encrypted_len;
        EVP_PKEY_encrypt(ctx, NULL, &encrypted_len, plaintext, plaintext_len);
        unsigned char* encrypted = (unsigned char*)malloc(encrypted_len);
        EVP_PKEY_encrypt(ctx, encrypted, &encrypted_len, plaintext, plaintext_len);

        // Decrypt
        EVP_PKEY_CTX *ctx2 = EVP_PKEY_CTX_new(privKey, NULL);
        EVP_PKEY_decrypt_init(ctx2);
        EVP_PKEY_CTX_set_rsa_padding(ctx2, RSA_PKCS1_OAEP_PADDING);
        EVP_PKEY_CTX_set_rsa_oaep_md(ctx2, EVP_sha256());
        size_t decrypted_len;
        EVP_PKEY_decrypt(ctx2, NULL, &decrypted_len, encrypted, encrypted_len);
        unsigned char* decrypted = (unsigned char*)malloc(decrypted_len + 1);
        EVP_PKEY_decrypt(ctx2, decrypted, &decrypted_len, encrypted, encrypted_len);
        decrypted[decrypted_len] = '\0';

        // Print encrypted and decrypted strings
        for(size_t i = 0; i < encrypted_len; i++) {
            printf("%02X", encrypted[i]);
        }
        printf("\n");
        printf("%s\n", decrypted);

        // Clean up
        EVP_PKEY_free(pubKey);
        EVP_PKEY_free(privKey);
        EVP_PKEY_CTX_free(ctx);
        EVP_PKEY_CTX_free(ctx2);
        free(encrypted);
        free(decrypted);

        return 0;
    }

This example uses OpenSSL’S EVP interface, specifically `EVP_PKEY_encrypt()` and `EVP_PKEY_decrypt()` functions to encrypt and decrypt a string using RSA. The public and private keys are loaded from files. The `EVP_PKEY_CTX_set_rsa_padding()` and `EVP_PKEY_CTX_set_rsa_oaep_md()` functions are used to set the padding scheme and the hash function for RSA-OAEP.

Here is the equivalent C++ code using Botan:

    #include <botan/auto_rng.h>
    #include <botan/hex.h>
    #include <botan/pk_keys.h>
    #include <botan/pkcs8.h>
    #include <botan/pubkey.h>
    #include <botan/rng.h>

    #include <iostream>

    int main(int argc, char* argv[]) {
       if(argc != 2) {
          return 1;
       }
       const std::string_view plaintext(
          "Your great-grandfather gave this watch to your granddad for good luck. "
          "Unfortunately, Dane's luck wasn't as good as his old man's.");
       const Botan::secure_vector<uint8_t> pt(plaintext.data(), plaintext.data() + plaintext.length());
       Botan::AutoSeeded_RNG rng;

       // load keypair
       Botan::DataSource_Stream in(argv[1]);
       auto kp = Botan::PKCS8::load_key(in);

       // encrypt with pk
       const Botan::PK_Encryptor_EME enc(*kp, rng, "OAEP(SHA-256)");
       const auto ct = enc.encrypt(pt, rng);

       // decrypt with sk
       const Botan::PK_Decryptor_EME dec(*kp, rng, "OAEP(SHA-256)");
       const auto pt2 = dec.decrypt(ct);

       std::cout << "\nenc: " << Botan::hex_encode(ct) << "\ndec: " << Botan::hex_encode(pt2);

       return 0;
    }

This example uses the `PK_Encryptor_EME` and `PK_Decryptor_EME` classes to encrypt and decrypt. a message using RSA. The public and private keys are loaded from files. The padding scheme and hash function are passed as a string parameter.

## Asymmetric Signatures

Consider the following application code to sign some data with ECDSA using OpenSSL.

    #include <openssl/ec.h>
    #include <openssl/obj_mac.h>
    #include <openssl/err.h>
    #include <openssl/ecdsa.h>
    #include <openssl/pem.h>
    #include <openssl/sha.h>
    #include <iostream>

    int main() {
        EC_KEY *ec_key = EC_KEY_new_by_curve_name(NID_secp521r1);

        if(ec_key == NULL) {
            fprintf(stderr, "Error creating EC_KEY structure.\n");
            return 1;
        }

        if(!EC_KEY_generate_key(ec_key)) {
            fprintf(stderr, "Error generating key.\n");
            ERR_print_errors_fp(stderr);
            EC_KEY_free(ec_key);
            return 1;
        }

        // String to sign
        std::string plaintext = "This is a tasty burger!";

        // Hash the plaintext
        unsigned char hash[SHA256_DIGEST_LENGTH];
        SHA256((unsigned char*)plaintext.c_str(), plaintext.size(), hash);

        // Sign the hash
        ECDSA_SIG* sig = ECDSA_do_sign(hash, SHA256_DIGEST_LENGTH, ec_key);
        if(sig == NULL) {
            std::cerr << "Error signing: " << ERR_error_string(ERR_get_error(), NULL) << "\n";
            return 1;
        }

        // Print the signature
        const BIGNUM* r;
        const BIGNUM* s;
        ECDSA_SIG_get0(sig, &r, &s);
        char* r_hex = BN_bn2hex(r);
        char* s_hex = BN_bn2hex(s);
        std::cout << "Signature: (" << r_hex << ", " << s_hex << ")\n";

        // Clean up
        EC_KEY_free(ec_key);
        ECDSA_SIG_free(sig);
        OPENSSL_free(r_hex);
        OPENSSL_free(s_hex);
        return 0;
    }

This snippet uses OpenSSL’s ECDSA interface, specifically `ECDSA_do_sign()`, to sign a string message using ECDSA. The private key is loaded from a file. The `SHA256()` function is used to hash the plaintext before signing.

Here is the equivalent C++ code using Botan:

    #include <botan/auto_rng.h>
    #include <botan/ec_group.h>
    #include <botan/ecdsa.h>
    #include <botan/hex.h>
    #include <botan/pubkey.h>

    #include <iostream>

    int main() {
       Botan::AutoSeeded_RNG rng;
       // Generate ECDSA keypair
       const auto group = Botan::EC_Group::from_name("secp521r1");
       const Botan::ECDSA_PrivateKey key(rng, group);

       const std::string message("This is a tasty burger!");

       // sign data
       Botan::PK_Signer signer(key, rng, "SHA-256");
       signer.update(message);
       std::vector<uint8_t> signature = signer.signature(rng);
       std::cout << "Signature:\n" << Botan::hex_encode(signature);

       // now verify the signature
       Botan::PK_Verifier verifier(key, "SHA-256");
       verifier.update(message);
       std::cout << "\nis " << (verifier.check_signature(signature) ? "valid" : "invalid");
       return 0;
    }

This example uses the `PK_Signer` and `PK_Verifier` classes to sign and verify a message using ECDSA. The private key is similarly loaded from a file. The hash function is passed as a string parameter. `PK_Verifier::check_signature()` is used to verify the signature.