#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;
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
Botan::Path_Validation_Restrictions restrictions;
// Optional: Specify usage type, compared against the key usage in end_certs[0]
Botan::Usage_Type usage = Botan::Usage_Type::UNSPECIFIED;
// Optional: Specify hostname, if not empty, compared against the DNS name in end_certs[0]
std::string hostname;
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()), buf.size());
size_t readcount = std::cin.gcount();
// update hash computations with read data
hash1->update(buf.data(), readcount);
hash2->update(buf.data(), readcount);
hash3->update(buf.data(), 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 std::vector<uint8_t> key = Botan::hex_decode("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)
Botan::secure_vector<uint8_t> iv = rng.random_vec(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;
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.");
std::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
Botan::PK_Encryptor_EME enc(*kp, rng, "OAEP(SHA-256)");
std::vector<uint8_t> ct = enc.encrypt(pt, rng);
// decrypt with sk
Botan::PK_Decryptor_EME dec(*kp, rng, "OAEP(SHA-256)");
Botan::secure_vector<uint8_t> 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");
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 similary
loaded from a file.
The hash function is passed as a string parameter.
PK_Verifier::check_signature()
is used to
verify the signature.