Click here to Skip to main content
15,792,217 members
Articles / Security / Blockchain

OpenSSL Tour for Win32 Developer

Rate me:
Please Sign up or sign in to vote.
4.87/5 (20 votes)
6 Aug 2015Apache14 min read 40.3K   3K   52   6
This article will aim to give you a brief and to point tutorial about DES modes , hash Functions , AES , RSA algorithms and example of their usage , using OpenSSL.
Image 1

Table of contents



OpenSSL is an open source toolkit that implements security protocols such as SSL and a lot of encryption algorithms like RSA, AES and several hash functions like SHA1 and MD5. this library gains great respect among developers due to its open source nature and C-Style interface which open bridge to many other languages. one of the weak point of this library is lack of introductory material for newbies developer and poor documentation on this article will aim to give you a brief and to point tutorial about some of algorithm and sample usage.

OpenSSL doesn’t come prebuilt from its origin but you can download the source from and compile it yourself or you can download prebuilt binary for win32 environment from the following link and this is what this article example is based on. This article will assume you installed OpenSSL in the following path C:\OpenSSL-Win32 and you added C:\OpenSSL-Win32\include to compiler header search path and you did the same thing with library C:\OpenSSL-Win32\lib\VC\static (this article will link to static version of library for sake of simplicity you should use dynamic library in production environment).

Why OpenSSL

  • It Is popular and its part of many large software like Apache, Oracle, PHP, web browsers, and most operating system.
  • Fast, lightweight and easy to use interface.
  • Has been around for a while and during its lifetime it survive a lot of attacks like Timing attack, predictable private key attack and most these vulnerabilities have been fixed over time.
  • Open source nature will open entire horizon to read the source and better understand how algorithm work and drive your way through library.
  • Its free under apache-style license for commercial product under minimal simple conditions. 

Entrance to Cryptography

Cryptography is the science of hiding data. the process of hiding data is called encryption (also called encipher) and the result data is named cipher and the reverse operation is decryption (decipher), these two processes depend on a Key to lock or unlock data. the key has two basic properties: length which measure in bit like 256-bit key, and weather a key is symmetric or not. Symmetric key is used for both encryption and decryption, asymmetric key algorithm’s use two keys one for encryption (public key) and the other for decryption (private key). Some algorithms are one-way encryption algorithm which produce only cipher the cipher is called digest, and a lot of hash functions fall into that category.


Categories of OpenSSL

Hash Algorithms

Hash function is one-way encryption algorithm which produced constant length data called digest, no matter how large the input data is the digest length is constant. these algorithm guarantees if a single bit of data changed it will produce complete different digest depending on this data. OpenSSL implement wide verity of hash functions, this article will only explain the most popular one MD5 and SHA-1. 

You might wonder why anyone need one-way encryption algorithm and here are some scenarios where it’s used:

  • Most modern file system attach file-hash within its catalog.
  • Download manager and torrent client guarantee that the file is downloaded successfully by comparing two hashes together.
  • Development environment detect which source file have changed by calculating the current hash and compare it to hash embedded in object file *.o and consequently decide whether to recompile the file or not .
  • In general file hash guarantee that the file isn’t tempered or edited during its transmission from origin to destination.
  • Software company generate hashes for their executable which is signed with their SSL certificate to guarantee authenticity of the product.
  • Cloud-based anti-virus like calculate file hash locally and get the result of already scanned file if available, otherwise it uploads the file, scan it, and store file hash and result on server for later retrieval. 

The MD5 message-digest algorithm is a widely used cryptographic hash function producing a 128-bit (16-byte) hash value, typically expressed in text format as a 32-digit hexadecimal number. the following snippet show you how to use the MD5 algorithm on simple text array:

First include the header file and link to library:

#include "openssl/md5.h"
#pragma comment(lib,"libeay32MD.lib")

Then use MD5 function which accept three parameters: the input data as string, length of that string, and buffer of 16-byte length that will receive the result. the third parameter will be null if error happened. to ensure you get the expected result from algorithm. compare you result with online service  :

char inbuffer [] = "Truth comes out of error more easily than out of confusion." ;
unsigned char outbuffer[MD5_DIGEST_LENGTH];
char digest[33] = {0} ;
MD5((unsigned char*)&inbuffer, strlen(inbuffer), (unsigned char*)&outbuffer);
for(int i = 0; i < MD5_DIGEST_LENGTH; i++)
    sprintf(&digest[i*2], "%02x", (unsigned int)outbuffer[i]); // convert hex to char

cout <<  digest <<   endl ;

SHA stand for Secure Hash Algorithm, it produces 160-bit (20-byte or 40-hex) hash digest. this algorithm releases its first version SHA-0 in 1993 and forth version SHA-3 released in late 2012.the following snippet show you how to use the SHA-1 hash function on sample text array:

First include the header file and link to library:

#include "openssl/sha.h"
#pragma comment(lib,"libeay32MD.lib")

 Then use SHA1 function which accept same parameter as MD5 function:

char inbuffer[] = "Be nice to people on your way up because you'll need them on your way down.";
unsigned char outbuffer[SHA_DIGEST_LENGTH];

SHA1((const unsigned char*) inbuffer, strlen(inbuffer) , outbuffer);

char digest[41] = "\0";
for(int i = 0 ; i < SHA_DIGEST_LENGTH ; i++)
      sprintf(&digest[i*2],"%02x",outbuffer[i]); // convert hex to char

cout << digest << endl ;

the code accompanied with this article:

  • shows more than one way to calculate hash also has more example about other versions of same algorithm and demonstrate how to calculate hash for entire input file with full documentation in code describing each step (the example here is short to save article space).
  • Demonstrate usage of high level interface EVP_. Openssl come with unified high level interface to call all its algorithm function through it. all functions of this interface start with EVP_ prefix and defined in <openssl/evp.h> header.
  • Demonstrate usage of other hash function like MDC-2 and Whirlpool.

OpenSSL come with openssl.exe (found in Bin folder) which allow you to generate hash directly from command line. use the following command line to calculate hash of input file:

openssl dgst -sha1 -out filename  [inputfile1...]
openssl dgst -md5 -out filename  [inputfile1...]

note that it can calculate hash for different files concurrently and if out-filename parameter is missing the output will be displayed on screen by default .and if the openssl.exe is in your environment search path it can be called directly from code using system() function .

DES Algorithm

Data Encryption Standard or DES is algorithm that use 56-bit symmetric key for data encryption. the algorithm is developed by IBM in early 70’s and is now considered to be insecure for many applications. This is mainly due to the 56-bit key size being too small. There are also some analytical results which demonstrate theoretical weaknesses in the cipher, although they are infeasible to mount in practice. The algorithm is believed to be practically secure in the form of Triple DES or 3DES (released in 1999), although there are theoretical attacks. In recent years, the cipher has been superseded by the AES algorithm. Triple des operate on 3*56-bit keys or 168-bit as whole. it uses first key to encrypt, a second key to decrypt and third key to encrypt again. both DES and triple DES operate on different mode to produce the outcome cipher and here are the most famous modes.

DES CBC(cipher-block-chaining) Mode 

In this mode the plain text is divided into blocks of length 64-bits start by XOR-ing plain text with pre-initialized data(64-bit) called initialization vector (IV) and then encrypt data producing 64-bit cipher-block. this cipher-block become IV for next plain-text block and so on. at the end if the plain text size is less than 64-bit it will be padded with zeros(null) and finalize the encryption .in decryption the XOR-ing with IV happen after decryption and the previous cipher-block become IV for the next cipher block.

Image 2

Image 3

The following example demonstrate how to encrypt and decrypt text array using DES-CBC mode:

char inbuffer[64] = "If you see a turtle on a fence post, he has had some help." ;
int inbufferSize = strlen(inbuffer);
unsigned char outbuffer[64] ;

DES_cblock key = {0x44 ,0x6f ,0x4a ,0x6a ,0x71 ,0x71 ,0x73 ,0x74 };
DES_cblock iv =  {0x6c ,0x6c ,0x4b ,0x4b ,0x71 ,0x54 ,0x31 ,0x75};
DES_key_schedule schedule;
DES_set_key_checked(&key, &schedule);

DES_ncbc_encrypt((const unsigned char*)inbuffer,outbuffer,inbufferSize,&schedule,&iv,DES_ENCRYPT);
cout << inbuffer  << endl ;

//display cipher encoded in base64
cout << base64_encode(outbuffer,64) << endl ;

//re-initialize iv since it's changed from last encrypt process.
DES_cblock iv2 =  {0x6c ,0x6c ,0x4b ,0x4b ,0x71 ,0x54 ,0x31 ,0x75};

//decrypt : notice last parameter and the first two parameter have been reversed
DES_ncbc_encrypt((const unsigned char*)outbuffer,(unsigned char*)inbuffer,64 ,&schedule,&iv2,DES_DECRYPT);

cout << inbuffer  << endl ;

first we define two variables key and iv of type DES_cblock which is typedef for unsigned char [8] .the key in DES algorithm must have odd-parity (the count of 1’s in binary represenation is odd ) and this is the job of DES_set_odd_parity function. the key then is converted to architecture dependent form using DES_set_key_checked function and store the result in struct of type DES_key_schedule which is used later in encryprion or decryption .

DES_ncbc_encrypt  function is used for both encryption and decryption depending on the last parameter whether it is one or zero . the first paramter is the input data to encrypt or cipher data to decrypt.the second paramter is buffer that will receive the outcome result.the third parameter is input buffer length in byte . the forth paramter is pointer to schedule object we created eralier . the fifth paramter is initialization vector iv and final paramter is integer deciding whether to encrypt or decrypt .

CBC mode exist in many other forms (with minimal changes) like Propagating Cipher Block Chaining (PCBC) , Cipher Feedback (CFB) , Output Feedback (OFB) , RSA CBC mode (XCBC) .a brief description of those mode with an example of Triple DES-CBC and EVP_ interface is acompanied with source code of this article .

DES ECB (Electronic CodeBook) Mode    

This is the simplest form of DES. In this mode each block of data is encrypted or decrypted independent from each other and without need of initialization vector. this give you the freedom to encrypt or decrypt certain block of data or change the block order after encryption or decryption as you wish.

Image 4

The following example demonstrate how to encrypt and decrypt text array using DES-ECB mode:

DES_cblock key;

//the random key generator must be seeded for key to get generated properly
DES_cblock seed = {0x58 ,0x48 ,0x54 ,0x4f ,0x36 ,0x65 ,0x69 ,0x47};
RAND_seed(seed, DES_KEY_SZ);
DES_random_key(&key);// let the library pick a secure key for us

DES_key_schedule schedule;
DES_set_key_checked(&key, &schedule);

string text  = "The best education in the world is that got by struggling to get a living.";
DES_cblock outbuffer , inbuffer = {0};

string cipher , back ;
for(int i = 0 , size = text.length() ; i < size ; i+=8)
      //encrypt 8-byte block atime
      cipher.append((const char*)outbuffer,8); // save encrypted data to restore it later

      //IMPORTANT : zero the memory of the buffer ,
      //otherwise you'll get dirty read if the
      //last loop iteration read is less than 8 byte


//print encrypted data encode in base64
cout << base64_encode((const unsigned char *)cipher.c_str(),cipher.length()) << endl ;

for(int i = 0 , size = cipher.length() ; i < size ; i+=8)
      //decrypt 8-byte block atime , notice the reverse of two parameter
      back.append((const char*)inbuffer,8);

cout << back  << endl ;

the code use same steps as DES_CBC code except the key is generated automatically using DES_random_key function . for this function to work properly the random number genrator must be seeded with a call to RAND_seed function . DES_ecb_encrypt function operate on two buffer of length 64-bit each as first two argument , a pointer to schedule object and integer flag to encrypt or decrypt .

AES Algorithm

Advanced Encryption Standard (AES) also named Rijndael (is a play on the names of the two inventors Joan Daemen and Vincent Rijmen) is a symmetric block cipher that can process data blocks of size 128 bits, using cipher keys with lengths of 128 (AES-128), 192(AES-192), and 256 bits (AES-256). The Algorithm Developed in 2001 and took 5 more years for standardization process .in 2002 AES has been adopted by US government to protect classified information and now used worldwide.

The key size used for an AES cipher specifies the number of repetitions of transformation rounds (substitution and permutation of bits) that convert the input into the final cipher. The number of cycles of repetition are as follows:

Image 5

The following example demonstrate how to encrypt and decrypt array of plain text using 128-bit key length:

//key size must be 16-byte long (AES-128)
const unsigned char userKey[] = { 0x54 ,0x68 ,0x65 ,0x20 ,0x67 ,0x72 ,0x65 ,0x61,
                                  0x74 ,0x20 ,0x70 ,0x6c ,0x65 ,0x61 ,0x73 ,0x75 };

AES_KEY key ;

string input = "The great pleasure in life is doing what people say you cannot do." ;
string cipher , back ;
unsigned char inbuffer[AES_BLOCK_SIZE] = {0};
unsigned char outbuffer[AES_BLOCK_SIZE] ={0};

for ( int i = 0 , size = input.length() ; i < size ; i+=AES_BLOCK_SIZE)
      //save outbuffer  to decrypting it later
      cipher.append((const char*)outbuffer,AES_BLOCK_SIZE);
      cout << base64_encode(outbuffer,AES_BLOCK_SIZE) ;

      //IMPORTANT : to avoid dirty read if block size of last iteration is less than 16


for ( int i = 0 , size = cipher.length() ; i < size ; i+=AES_BLOCK_SIZE)
      back.append((const char*)inbuffer,AES_BLOCK_SIZE);

cout << "\n\n" << back << "\n";

AES_set_encrypt_key function expect three parameters the user key (usually expressed in hex), the length of that key depend of second parameter which is key length in bit (other possible value 192 and 256) and if the user passed array is bigger than second parameter length the remaining character is ignored ,the third parameter is architecture dependent form of the key of type AES_KEY.  After that we define two intermediate buffer inbuffer, outbuffer both of length 16-byte. AES_encrypt function operate iteratively on those buffer using the AES_KEY we craeted earlier . AES_decrypt function operate in similier fashion except the need to call AES_set_decrypt_key function before decryption which expect same paramters(and must match to work) as AES_set_encrypt_key.

the code accompanied with this article:

  • implement more demos about AES modes like CBC and EBC with various key length .
  • Show how to encrypt and decrypt using high level interafce EVP_ .

Finally you can encrypt directly from command line prompt using command:

openssl enc -aes-[256|192|128]-[cbc|ebc] -in plainText.txt -out -pass arg [-d|-e]

public key cryptography standards (PKCS)

RSA was invented by three mathematicians Ron Rivest, Adi Shamir, and Leonard Adleman in 1977.The Algorithm depend on two mathematically related keys public key (published key) for encryption and private key for decryption (kept secret).

The way the algorithm work is pure math I will refer you to another article and video demonstration of how algorithm operate internally and I’ll stick to presenting a working examples:

Where do you see RSA cryptography? 

  • Software package signing: a signature is generated by private key and got validated by public key on the other end.
  • Secure Shell Communication (SSH): where key pair used for authentication and only computer with private key can establish a connection to computer with public key.
  • Most networking protocols that end with s like HTTPS and FTPS.

Key pair generation

To generate a key pair, use the following command line:

openssl genrsa -out private.pem 4096

this will generate RSA key pair with length 4096-bit and save the result to private.pem file. any value less than 1024-bit for key length is insecure and any value greater than 4096 is impractical. the file content will look something like this:

. . .

To extract the public key from the generated key pair, use the following command line:

openssl rsa -pubout -in private.pem -out public.pem

this will store the public key in public.pem file. if the -out parameter is missing the key will be displayed on screen by default.

To view key components, use the following command line:

openssl rsa -text -in private.pem

this will display two prime numbers p and q , the modulus n , two exponents e and d along with key data.

You can generate key pair directly in code using RSA_generate_key_ex function :

#include <openssl/pem.h>
#include <openssl/rsa.h>
#include <openssl/rand.h>
#pragma comment(lib,"libeay32MD.lib")
//The pseudo-random number generator must be seeded prior to calling RSA_generate_key_ex function
unsigned char seed[] = {0x58 ,0x48 ,0x54 ,0x4f ,0x36 ,0x65 ,0x69 ,0x47};

RSA* rsa = RSA_new();//allocate empty key

int bits = 4096;
unsigned long e = RSA_F4 ; //65537 public exponent
BIGNUM* bne = BN_new(); // allocate BINNUM structure in heap
BN_set_word(bne,e); //store that public exponent in big-number object bne

// generate RSA key with length 4096 , public exponent 65537

//allocate a memory BIO in heap
BIO* bio_private = BIO_new(BIO_s_mem());
BIO* bio_public = BIO_new(BIO_s_mem());

//extract private and public key to bio-object respectively
PEM_write_bio_RSAPrivateKey(bio_private, rsa, NULL, NULL, 0, NULL, NULL);
PEM_write_bio_RSAPublicKey(bio_public, rsa);

//BIO_pending function return number of byte read to bio buffer during previous step
int private_len = BIO_pending(bio_private);
int public_len = BIO_pending(bio_public);

//this two buffer will hold the keys as string
char* private_key = new char[private_len + 1];
char* public_key = new char[public_len + 1];

//copy extracted keys to string
BIO_read(bio_private, private_key, private_len);
BIO_read(bio_public, public_key, public_len);

//ensure that both keys ends with null terminator

cout << private_key << "\n\n\n\n" ;
cout << public_key << "\n\n\n\n" ;

//clean up memory
delete [] private_key;
delete [] public_key;

RSA in main struct responsible for holding key components, this key is allocated by RSA_new() and deallocated RSA_free() function . in general all objects that allocated via API must be manpiulated and deallocated by API . bne is an pointer of type BIGNUM that will hold public exponent (e = 65537), this object is allocated by BN_new()  , manipulated by BN_set_word() , and freed by BN_free() functions respectively. RSA_generate_key_ex() function dose the actual key generation and expect the following argument : key pointer , key length in bit , BIGNUM pointer containing public exponent e , callback function during prime number generation and it's usually null. OpenSSL define abstraction layer for input and output that use same interface to handle many IO devices (socket, terminal, memory, files), this interface is called BIO. BIO object is allocated via BIO_new() which expect BIO method as parameter. The Function BIO_s_mem() return in-memory method type. PEM_write_bio_RSAPrivateKey() and PEM_write_bio_RSAPublicKey() functions extract private and public keys to BIO objects respectively . BIO_pending() function return count of bytes read or written by last read or write operation (it’s BIO length in our case). BIO_read() copy BIO internal buffer to char string we already created and then i printed those buffers on screen.

After creating key pair, you use those functions to encrypt and decrypt:

int   RSA_public_encrypt(int flen, const unsigned char *from, unsigned char *to,
                         RSA *rsa,int padding);

int   RSA_public_decrypt(int flen, const unsigned char *from, unsigned char *to,
                         RSA *rsa,int padding);

int   RSA_private_encrypt(int flen, const unsigned char *from, unsigned char *to,
                          RSA *rsa,int padding);

int   RSA_private_decrypt(int flen, const unsigned char *from, unsigned char *to, 
                          RSA *rsa,int padding);

The first two pair encrypt the data using the public key and decrypt it using private key. the latter two use private key to encrypt and public key to decrypt data (less common). The usage of the first two function is demonstrated is the following snippet:

//test public encrypt and private decrypt on sample array string
void testRSA_PublicEncrypt()
     //note the line ending char \n is part of the key 
      char *public_key ="-----BEGIN PUBLIC KEY-----\n\
-----END PUBLIC KEY-----\n";

      char* private_key = "-----BEGIN RSA PRIVATE KEY-----\n\
-----END RSA PRIVATE KEY-----\n";

    char* msg = "The great pleasure in life is doing what people say you cannot do.";
    char* cipher,*back ;
    int cipherSize;

    // encrypting
        RSA* rsa = RSA_new() ;
        //load string key to bio object
        BIO* mem_bio = BIO_new_mem_buf(public_key, strlen(public_key));
        //convert bio to a key
        rsa = PEM_read_bio_RSA_PUBKEY(mem_bio, &rsa,NULL, NULL);
        // return key size in byte
        cipherSize = RSA_size(rsa)+1;
        cipher = new char [cipherSize];

        // Encrypting using loaded public key
        cipherSize = RSA_public_encrypt(strlen(msg), (unsigned char *)msg, 
                                       (unsigned char *)cipher, rsa, RSA_PKCS1_OAEP_PADDING);

        cout << base64_encode((const unsigned char*)cipher,cipherSize) << "\n\n\n" ;

    // decrypting
        RSA* rsa = RSA_new() ;
        BIO* mem_bio = BIO_new_mem_buf(private_key, strlen(private_key));
        rsa = PEM_read_bio_RSAPrivateKey(mem_bio, &rsa,NULL, NULL);
        back = new char [cipherSize];
        cipherSize = RSA_private_decrypt(cipherSize,(unsigned char *)cipher, 
                                         (unsigned char *)back,rsa,RSA_PKCS1_OAEP_PADDING);
        cout << back << "\n\n\n" ;

      delete[] cipher ;
      delete[] back ;

First we define the keys as string and load them to BIO object via BIO_new_mem_buf() function . BIO object is convert to RSA* key via a call to PEM_read_bio_RSA_PUBKEY() function which return the public key or null in case of failure. RSA_public_encrypt() function dose the actual encrypt  passing message length as first argument ,then message itself , buffer to hold the result, this buffer must be less than RSA_size(rsa)-41 for RSA_PKCS1_OAEP_PADDING schema , the fourth parameter is a public key to encrypt with , and finally the padding schema which is predefine constant , the function return actual data length written to out buffer. Decrypting follow same step as encrypting except the calls to PEM_read_bio_RSAPrivateKey() and RSA_private_decrypt() functions.

the code accompanied with this article:

  • show more demo on how to save generated key to file and load them back and how to construct a key from string.
  • demonstrate how to use private key to encrypt and public key to decrypt a whole file.

Final word

After this article I barley touch the surface of vast API provided by openssl. I did my best to make the examples complete, realistic, comprehensive as much as I can. Dear reader if you implemented more examples about those algorithms, I will be happy to add them to my code with full credit given to you, just leave the code in comment section. Also note that code supplied with this article have minimal error checking, don’t copy and paste the code and expect it to work everywhere.

Hope this article was a helpful effort  :) .

Reference and Credit


This article, along with any associated source code and files, is licensed under The Apache License, Version 2.0

Written By
Software Developer (Senior)
Syrian Arab Republic Syrian Arab Republic
C++ , MFC , Win32 professional Developer.

Comments and Discussions

GeneralExcellent... Pin
Musthafa (Member 379898)27-May-18 18:22
Musthafa (Member 379898)27-May-18 18:22 
Questionmy vote of 5 Pin
Michael Haephrati30-Oct-17 7:38
mvaMichael Haephrati30-Oct-17 7:38 
QuestionGreat Work.. Please Help to resolve some build issues Pin
Member 1274133327-Dec-16 3:14
Member 1274133327-Dec-16 3:14 
QuestionHow Encryption between client(desktop) and server(desktop) works Pin
Member 737491226-Aug-15 2:39
Member 737491226-Aug-15 2:39 
AnswerRe: How Encryption between client(desktop) and server(desktop) works Pin
Huzifa Terkawi26-Aug-15 8:55
Huzifa Terkawi26-Aug-15 8:55 
SuggestionNo longer secure methods Pin
feanorgem10-Aug-15 7:59
feanorgem10-Aug-15 7:59 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.