Skip to content

Instantly share code, notes, and snippets.

@superwills
Last active November 2, 2022 09:45
Show Gist options
  • Star 21 You must be signed in to star a gist
  • Fork 8 You must be signed in to fork a gist
  • Save superwills/5415344 to your computer and use it in GitHub Desktop.
Save superwills/5415344 to your computer and use it in GitHub Desktop.
Base64 encoding and RSA encryption sample
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <openssl/rsa.h>
#include <openssl/engine.h>
#include <openssl/pem.h>
// I'm not using BIO for base64 encoding/decoding. It is difficult to use.
// Using superwills' Nibble And A Half instead
// https://github.com/superwills/NibbleAndAHalf/blob/master/NibbleAndAHalf/base64.h
#include "base64.h"
// The PADDING parameter means RSA will pad your data for you
// if it is not exactly the right size
//#define PADDING RSA_PKCS1_OAEP_PADDING
#define PADDING RSA_PKCS1_PADDING
//#define PADDING RSA_NO_PADDING
RSA* loadPUBLICKeyFromString( const char* publicKeyStr )
{
// A BIO is an I/O abstraction (Byte I/O?)
// BIO_new_mem_buf: Create a read-only bio buf with data
// in string passed. -1 means string is null terminated,
// so BIO_new_mem_buf can find the dataLen itself.
// Since BIO_new_mem_buf will be READ ONLY, it's fine that publicKeyStr is const.
BIO* bio = BIO_new_mem_buf( (void*)publicKeyStr, -1 ) ; // -1: assume string is null terminated
BIO_set_flags( bio, BIO_FLAGS_BASE64_NO_NL ) ; // NO NL
// Load the RSA key from the BIO
RSA* rsaPubKey = PEM_read_bio_RSA_PUBKEY( bio, NULL, NULL, NULL ) ;
if( !rsaPubKey )
printf( "ERROR: Could not load PUBLIC KEY! PEM_read_bio_RSA_PUBKEY FAILED: %s\n", ERR_error_string( ERR_get_error(), NULL ) ) ;
BIO_free( bio ) ;
return rsaPubKey ;
}
RSA* loadPRIVATEKeyFromString( const char* privateKeyStr )
{
BIO *bio = BIO_new_mem_buf( (void*)privateKeyStr, -1 );
//BIO_set_flags( bio, BIO_FLAGS_BASE64_NO_NL ) ; // NO NL
RSA* rsaPrivKey = PEM_read_bio_RSAPrivateKey( bio, NULL, NULL, NULL ) ;
if ( !rsaPrivKey )
printf("ERROR: Could not load PRIVATE KEY! PEM_read_bio_RSAPrivateKey FAILED: %s\n", ERR_error_string(ERR_get_error(), NULL));
BIO_free( bio ) ;
return rsaPrivKey ;
}
unsigned char* rsaEncrypt( RSA *pubKey, const unsigned char* str, int dataSize, int *resultLen )
{
int rsaLen = RSA_size( pubKey ) ;
unsigned char* ed = (unsigned char*)malloc( rsaLen ) ;
// RSA_public_encrypt() returns the size of the encrypted data
// (i.e., RSA_size(rsa)). RSA_private_decrypt()
// returns the size of the recovered plaintext.
*resultLen = RSA_public_encrypt( dataSize, (const unsigned char*)str, ed, pubKey, PADDING ) ;
if( *resultLen == -1 )
printf("ERROR: RSA_public_encrypt: %s\n", ERR_error_string(ERR_get_error(), NULL));
return ed ;
}
unsigned char* rsaDecrypt( RSA *privKey, const unsigned char* encryptedData, int *resultLen )
{
int rsaLen = RSA_size( privKey ) ; // That's how many bytes the decrypted data would be
unsigned char *decryptedBin = (unsigned char*)malloc( rsaLen ) ;
*resultLen = RSA_private_decrypt( RSA_size(privKey), encryptedData, decryptedBin, privKey, PADDING ) ;
if( *resultLen == -1 )
printf( "ERROR: RSA_private_decrypt: %s\n", ERR_error_string(ERR_get_error(), NULL) ) ;
return decryptedBin ;
}
unsigned char* makeAlphaString( int dataSize )
{
unsigned char* s = (unsigned char*) malloc( dataSize ) ;
int i;
for( i = 0 ; i < dataSize ; i++ )
s[i] = 65 + i ;
s[i-1]=0;//NULL TERMINATOR ;)
return s ;
}
// You may need to encrypt several blocks of binary data (each has a maximum size
// limited by pubKey). You shoudn't try to encrypt more than
// RSA_LEN( pubKey ) bytes into some packet.
// returns base64( rsa encrypt( <<binary data>> ) )
// base64OfRsaEncrypted()
// base64StringOfRSAEncrypted
// rsaEncryptThenBase64
char* rsaEncryptThenBase64( RSA *pubKey, unsigned char* binaryData, int binaryDataLen, int *outLen )
{
int encryptedDataLen ;
// RSA encryption with public key
unsigned char* encrypted = rsaEncrypt( pubKey, binaryData, binaryDataLen, &encryptedDataLen ) ;
// To base 64
int asciiBase64EncLen ;
char* asciiBase64Enc = base64( encrypted, encryptedDataLen, &asciiBase64EncLen ) ;
// Destroy the encrypted data (we are using the base64 version of it)
free( encrypted ) ;
// Return the base64 version of the encrypted data
return asciiBase64Enc ;
}
// rsaDecryptOfUnbase64()
// rsaDecryptBase64String()
// unbase64ThenRSADecrypt()
// rsaDecryptThisBase64()
unsigned char* rsaDecryptThisBase64( RSA *privKey, char* base64String, int *outLen )
{
int encBinLen ;
unsigned char* encBin = unbase64( base64String, (int)strlen( base64String ), &encBinLen ) ;
// rsaDecrypt assumes length of encBin based on privKey
unsigned char *decryptedBin = rsaDecrypt( privKey, encBin, outLen ) ;
free( encBin ) ;
return decryptedBin ;
}
int main( int argc, const char* argv[] )
{
ERR_load_crypto_strings();
puts( "We are going to: rsa_decrypt( unbase64( base64( rsa_encrypt( <<binary data>> ) ) ) )" );
// public key
// http://srdevspot.blogspot.ca/2011/08/openssl-error0906d064pem.html
//1. The file must contain:
//-----BEGIN CERTIFICATE-----
//on a separate line (i.e. it must be terminated with a newline).
//2. Each line of "gibberish" must be 64 characters wide.
//3. The file must end with:
//-----END CERTIFICATE-----
// YOUR PUBLIC KEY MUST CONTAIN NEWLINES. If it doesn't (ie if you generated it with
// something like
// ssh-keygen -t rsa -C "you@example.com"
// ) THEN YOU MUST INSERT NEWLINES EVERY 64 CHRS (just line it up with how I have it here
// or with how the ssh-keygen private key is formatted by default)
const char *b64_pKey = "-----BEGIN PUBLIC KEY-----\n"
"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCp2w+8HUdECo8V5yuKYrWJmUbL\n"
"tD6nSyVifN543axXvNSFzQfWNOGVkMsCo6W4hpl5eHv1p9Hqdcf/ZYQDWCK726u6\n"
"hsZA81AblAOOXKaUaxvFC+ZKRJf+MtUGnv0v7CrGoblm1mMC/OQI1JfSsYi68Epn\n"
"aOLepTZw+GLTnusQgwIDAQAB\n"
"-----END PUBLIC KEY-----\n";
// private key
const char *b64priv_key = "-----BEGIN RSA PRIVATE KEY-----\n"
"MIICXAIBAAKBgQCp2w+8HUdECo8V5yuKYrWJmUbLtD6nSyVifN543axXvNSFzQfW\n"
"NOGVkMsCo6W4hpl5eHv1p9Hqdcf/ZYQDWCK726u6hsZA81AblAOOXKaUaxvFC+ZK\n"
"RJf+MtUGnv0v7CrGoblm1mMC/OQI1JfSsYi68EpnaOLepTZw+GLTnusQgwIDAQAB\n"
"AoGBAKDuq3PikblH/9YS11AgwjwC++7ZcltzeZJdGTSPY1El2n6Dip9ML0hUjeSM\n"
"ROIWtac/nsNcJCnvOnUjK/c3NIAaGJcfRPiH/S0Ga6ROiDfFj2UXAmk/v4wRRUzr\n"
"5lsA0jgEt5qcq2Xr/JPQVGB4wUgL/yQK0dDhW0EdrJ707e3BAkEA1aIHbmcVfCP8\n"
"Y/uWuK0lvWxrIWfR5MlHhI8tD9lvkot2kyXiV+jB6/gktwk1QaFsy7dCXn7w03+k\n"
"xrjEGGN+kQJBAMuKf55lDtU9K2Js3YSStTZAXP+Hz7XpoLxmbWFyGvBx806WjgAD\n"
"624irwS+0tBxkERbRcisfb2cXmAx8earT9MCQDZuVCpjBWxd1t66qYpgQ29iAmG+\n"
"jBIY3qn9uOOC6RSTiCCx1FvFqDMxRFmGdRVFxeyZwsVE3qNksF0Zko0MPKECQCEe\n"
"oDV97DP2iCCz5je0R5hUUM2jo8DOC0GcyR+aGZgWcqjPBrwp5x08t43mHxeb4wW8\n"
"dFZ6+trnntO4TMxkA9ECQB+yCPgO1zisJWYuD46KISoesYhwHe5C1BQElQgi9bio\n"
"U39fFo88w1pok23a2CZBEXguSvCvexeB68OggdDXvy0=\n"
"-----END RSA PRIVATE KEY-----\n";
// String to encrypt, INCLUDING NULL TERMINATOR:
int dataSize=37 ; // 128 for NO PADDING, __ANY SIZE UNDER 128 B__ for RSA_PKCS1_PADDING
unsigned char *str = makeAlphaString( dataSize ) ;
printf( "\nThe original data is:\n%s\n\n", (char*)str ) ;
// LOAD PUBLIC KEY
RSA *pubKey = loadPUBLICKeyFromString( b64_pKey ) ;
int asciiB64ELen ;
char* asciiB64E = rsaEncryptThenBase64( pubKey, str, dataSize, &asciiB64ELen ) ;
RSA_free( pubKey ) ; // free the public key when you are done all your encryption
printf( "Sending base64_encoded ( rsa_encrypted ( <<binary data>> ) ):\n%s\n", asciiB64E ) ;
puts( "<<---------------- SENDING DATA ACROSS INTERWEBS ---------------->>" ) ;
char* rxOverHTTP = asciiB64E ; // Simulate Internet connection by a pointer reference
printf( "\nRECEIVED some base64 string:\n%s\n", rxOverHTTP ) ;
puts( "\n * * * What could it be?" ) ;
// Now decrypt this very string with the private key
RSA *privKey = loadPRIVATEKeyFromString( b64priv_key ) ;
// Now we got the data at the server. Time to decrypt it.
int rBinLen ;
unsigned char* rBin = rsaDecryptThisBase64( privKey, rxOverHTTP, &rBinLen ) ;
printf("Decrypted %d bytes, the recovered data is:\n%.*s\n\n", rBinLen, rBinLen, rBin ) ; // rBin is not necessarily NULL
// terminated, so we only print rBinLen chrs
RSA_free(privKey) ;
bool allEq = true ;
for( int i = 0 ; i < dataSize ; i++ )
allEq &= (str[i] == rBin[i]) ;
if( allEq ) puts( "DATA TRANSFERRED INTACT!" ) ;
else puts( "ERROR, recovered binary does not match sent binary" ) ;
free( str ) ;
free( asciiB64E ) ; // rxOverHTTP
free( rBin ) ;
ERR_free_strings();
}
Copy link

ghost commented Nov 7, 2015

I have following error.
ERROR: Could not load PRIVATE KEY! PEM_read_bio_RSAPrivateKey FAILED: error: 0D0680A8:asn1 encoding routines:ASN1_CHECK_TLEN:wrong tag

@negativelol
Copy link

everything works fine. thanks!

@cocoyen1995
Copy link

Thanks for sharing the code! 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment