Skip to content

Instantly share code, notes, and snippets.

@issaharnoam
Forked from irbull/OpenSSLExample.cpp
Last active June 14, 2022 04:09
Show Gist options
  • Save issaharnoam/518d15e568dec030d318af1b4177b0a0 to your computer and use it in GitHub Desktop.
Save issaharnoam/518d15e568dec030d318af1b4177b0a0 to your computer and use it in GitHub Desktop.
Code signing and verification with OpenSSL
#include <iostream>
#include <openssl/aes.h>
#include <openssl/evp.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/bio.h>
#include <openssl/err.h>
#include <unistd.h>
#include <assert.h>
std::string privateKey = "-----BEGIN RSA PRIVATE KEY-----\n"\
"MIIEowIBAAKCAQEAy8Dbv8prpJ/0kKhlGeJYozo2t60EG8L0561g13R29LvMR5hy\n"\
"vGZlGJpmn65+A4xHXInJYiPuKzrKUnApeLZ+vw1HocOAZtWK0z3r26uA8kQYOKX9\n"\
"Qt/DbCdvsF9wF8gRK0ptx9M6R13NvBxvVQApfc9jB9nTzphOgM4JiEYvlV8FLhg9\n"\
"yZovMYd6Wwf3aoXK891VQxTr/kQYoq1Yp+68i6T4nNq7NWC+UNVjQHxNQMQMzU6l\n"\
"WCX8zyg3yH88OAQkUXIXKfQ+NkvYQ1cxaMoVPpY72+eVthKzpMeyHkBn7ciumk5q\n"\
"gLTEJAfWZpe4f4eFZj/Rc8Y8Jj2IS5kVPjUywQIDAQABAoIBADhg1u1Mv1hAAlX8\n"\
"omz1Gn2f4AAW2aos2cM5UDCNw1SYmj+9SRIkaxjRsE/C4o9sw1oxrg1/z6kajV0e\n"\
"N/t008FdlVKHXAIYWF93JMoVvIpMmT8jft6AN/y3NMpivgt2inmmEJZYNioFJKZG\n"\
"X+/vKYvsVISZm2fw8NfnKvAQK55yu+GRWBZGOeS9K+LbYvOwcrjKhHz66m4bedKd\n"\
"gVAix6NE5iwmjNXktSQlJMCjbtdNXg/xo1/G4kG2p/MO1HLcKfe1N5FgBiXj3Qjl\n"\
"vgvjJZkh1as2KTgaPOBqZaP03738VnYg23ISyvfT/teArVGtxrmFP7939EvJFKpF\n"\
"1wTxuDkCgYEA7t0DR37zt+dEJy+5vm7zSmN97VenwQJFWMiulkHGa0yU3lLasxxu\n"\
"m0oUtndIjenIvSx6t3Y+agK2F3EPbb0AZ5wZ1p1IXs4vktgeQwSSBdqcM8LZFDvZ\n"\
"uPboQnJoRdIkd62XnP5ekIEIBAfOp8v2wFpSfE7nNH2u4CpAXNSF9HsCgYEA2l8D\n"\
"JrDE5m9Kkn+J4l+AdGfeBL1igPF3DnuPoV67BpgiaAgI4h25UJzXiDKKoa706S0D\n"\
"4XB74zOLX11MaGPMIdhlG+SgeQfNoC5lE4ZWXNyESJH1SVgRGT9nBC2vtL6bxCVV\n"\
"WBkTeC5D6c/QXcai6yw6OYyNNdp0uznKURe1xvMCgYBVYYcEjWqMuAvyferFGV+5\n"\
"nWqr5gM+yJMFM2bEqupD/HHSLoeiMm2O8KIKvwSeRYzNohKTdZ7FwgZYxr8fGMoG\n"\
"PxQ1VK9DxCvZL4tRpVaU5Rmknud9hg9DQG6xIbgIDR+f79sb8QjYWmcFGc1SyWOA\n"\
"SkjlykZ2yt4xnqi3BfiD9QKBgGqLgRYXmXp1QoVIBRaWUi55nzHg1XbkWZqPXvz1\n"\
"I3uMLv1jLjJlHk3euKqTPmC05HoApKwSHeA0/gOBmg404xyAYJTDcCidTg6hlF96\n"\
"ZBja3xApZuxqM62F6dV4FQqzFX0WWhWp5n301N33r0qR6FumMKJzmVJ1TA8tmzEF\n"\
"yINRAoGBAJqioYs8rK6eXzA8ywYLjqTLu/yQSLBn/4ta36K8DyCoLNlNxSuox+A5\n"\
"w6z2vEfRVQDq4Hm4vBzjdi3QfYLNkTiTqLcvgWZ+eX44ogXtdTDO7c+GeMKWz4XX\n"\
"uJSUVL5+CVjKLjZEJ6Qc2WZLl94xSwL71E41H4YciVnSCQxVc4Jw\n"\
"-----END RSA PRIVATE KEY-----\n\0";
std::string publicKey = "-----BEGIN PUBLIC KEY-----\n"\
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy8Dbv8prpJ/0kKhlGeJY\n"\
"ozo2t60EG8L0561g13R29LvMR5hyvGZlGJpmn65+A4xHXInJYiPuKzrKUnApeLZ+\n"\
"vw1HocOAZtWK0z3r26uA8kQYOKX9Qt/DbCdvsF9wF8gRK0ptx9M6R13NvBxvVQAp\n"\
"fc9jB9nTzphOgM4JiEYvlV8FLhg9yZovMYd6Wwf3aoXK891VQxTr/kQYoq1Yp+68\n"\
"i6T4nNq7NWC+UNVjQHxNQMQMzU6lWCX8zyg3yH88OAQkUXIXKfQ+NkvYQ1cxaMoV\n"\
"PpY72+eVthKzpMeyHkBn7ciumk5qgLTEJAfWZpe4f4eFZj/Rc8Y8Jj2IS5kVPjUy\n"\
"wQIDAQAB\n"\
"-----END PUBLIC KEY-----\n";
RSA* createPrivateRSA(std::string key) {
RSA* rsa = NULL;
const char* c_string = key.c_str();
BIO* keybio = BIO_new_mem_buf((void*)c_string, -1);
if (keybio == NULL) {
return 0;
}
rsa = PEM_read_bio_RSAPrivateKey(keybio, &rsa, NULL, NULL);
return rsa;
}
RSA* createPublicRSA(std::string key) {
RSA* rsa = NULL;
BIO* keybio;
const char* c_string = key.c_str();
keybio = BIO_new_mem_buf((void*)c_string, -1);
if (keybio == NULL) {
return 0;
}
rsa = PEM_read_bio_RSA_PUBKEY(keybio, &rsa, NULL, NULL);
return rsa;
}
bool RSASign(RSA* rsa,
const unsigned char* Msg,
size_t MsgLen,
unsigned char** EncMsg,
size_t* MsgLenEnc) {
EVP_MD_CTX* m_RSASignCtx = EVP_MD_CTX_create();
EVP_PKEY* priKey = EVP_PKEY_new();
EVP_PKEY_assign_RSA(priKey, rsa);
if (EVP_DigestSignInit(m_RSASignCtx, NULL, EVP_sha256(), NULL, priKey) <= 0) {
return false;
}
if (EVP_DigestSignUpdate(m_RSASignCtx, Msg, MsgLen) <= 0) {
return false;
}
if (EVP_DigestSignFinal(m_RSASignCtx, NULL, MsgLenEnc) <= 0) {
return false;
}
*EncMsg = (unsigned char*)malloc(*MsgLenEnc);
if (EVP_DigestSignFinal(m_RSASignCtx, *EncMsg, MsgLenEnc) <= 0) {
return false;
}
EVP_MD_CTX_cleanup(m_RSASignCtx);
return true;
}
bool RSAVerifySignature(RSA* rsa,
unsigned char* MsgHash,
size_t MsgHashLen,
const char* Msg,
size_t MsgLen,
bool* Authentic) {
*Authentic = false;
EVP_PKEY* pubKey = EVP_PKEY_new();
EVP_PKEY_assign_RSA(pubKey, rsa);
EVP_MD_CTX* m_RSAVerifyCtx = EVP_MD_CTX_create();
if (EVP_DigestVerifyInit(m_RSAVerifyCtx, NULL, EVP_sha256(), NULL, pubKey) <= 0) {
return false;
}
if (EVP_DigestVerifyUpdate(m_RSAVerifyCtx, Msg, MsgLen) <= 0) {
return false;
}
int AuthStatus = EVP_DigestVerifyFinal(m_RSAVerifyCtx, MsgHash, MsgHashLen);
if (AuthStatus == 1) {
*Authentic = true;
EVP_MD_CTX_cleanup(m_RSAVerifyCtx);
return true;
}
else if (AuthStatus == 0) {
*Authentic = false;
EVP_MD_CTX_cleanup(m_RSAVerifyCtx);
return true;
}
else {
*Authentic = false;
EVP_MD_CTX_cleanup(m_RSAVerifyCtx);
return false;
}
}
void Base64Encode(const unsigned char* buffer,
size_t length,
char** base64Text) {
BIO* bio, * b64;
BUF_MEM* bufferPtr;
b64 = BIO_new(BIO_f_base64());
bio = BIO_new(BIO_s_mem());
bio = BIO_push(b64, bio);
BIO_write(bio, buffer, static_cast<int>(length));
BIO_flush(bio);
BIO_get_mem_ptr(bio, &bufferPtr);
BIO_set_close(bio, BIO_NOCLOSE);
BIO_free_all(bio);
*base64Text = (*bufferPtr).data;
}
size_t calcDecodeLength(const char* b64input) {
size_t len = strlen(b64input), padding = 0;
if (b64input[len - 1] == '=' && b64input[len - 2] == '=') //last two chars are =
padding = 2;
else if (b64input[len - 1] == '=') //last char is =
padding = 1;
return (len * 3) / 4 - padding;
}
void Base64Decode(const char* b64message, unsigned char** buffer, size_t* length) {
BIO* bio, * b64;
int decodeLen = static_cast<int>(calcDecodeLength(b64message));
*buffer = (unsigned char*)malloc(decodeLen + 1);
(*buffer)[decodeLen] = '\0';
bio = BIO_new_mem_buf(b64message, -1);
b64 = BIO_new(BIO_f_base64());
bio = BIO_push(b64, bio);
*length = BIO_read(bio, *buffer, static_cast<int>(strlen(b64message)));
BIO_free_all(bio);
}
char* signMessage(std::string privateKey, std::string plainText) {
RSA* privateRSA = createPrivateRSA(privateKey);
unsigned char* encMessage;
char* base64Text;
size_t encMessageLength;
RSASign(privateRSA, (unsigned char*)plainText.c_str(), plainText.length(), &encMessage, &encMessageLength);
Base64Encode(encMessage, encMessageLength, &base64Text);
free(encMessage);
return base64Text;
}
bool verifySignature(std::string publicKey, std::string plainText, char* signatureBase64) {
RSA* publicRSA = createPublicRSA(publicKey);
unsigned char* encMessage;
size_t encMessageLength;
bool authentic;
Base64Decode(signatureBase64, &encMessage, &encMessageLength);
bool result = RSAVerifySignature(publicRSA, encMessage, encMessageLength, plainText.c_str(), plainText.length(), &authentic);
return result & authentic;
}
bool RSASignatureFromFile(const std::string& fileName, const std::string& rsaPrivateKey, std::string& signature) {
signature.clear();
if (rsaPrivateKey.empty() || fileName.empty())
return false;
static const size_t bufSize = getpagesize();
RSA* privateRSA = NULL;
BIO* keybio = NULL;
EVP_MD_CTX* rSASignCtx = NULL;
EVP_PKEY* priKey = NULL;
FILE* file = NULL;
bool rv = false;
unsigned char* ucBuffer = NULL;
char* signatureRaw = NULL;
do {
file = fopen(fileName.c_str(), "rb");
if (file == NULL)
break;
size_t bytesRead = 0;
ucBuffer = new unsigned char[bufSize];
keybio = BIO_new_mem_buf(reinterpret_cast<const void*>(rsaPrivateKey.c_str()), static_cast<int>(rsaPrivateKey.size()));
if (keybio == NULL)
break;
privateRSA = PEM_read_bio_RSAPrivateKey(keybio, &privateRSA, NULL, NULL);
if (privateRSA == NULL)
break;
rSASignCtx = EVP_MD_CTX_create();
if (rSASignCtx == NULL)
break;
priKey = EVP_PKEY_new();
if (priKey == NULL)
break;
EVP_PKEY_assign_RSA(priKey, privateRSA);
if (EVP_DigestSignInit(rSASignCtx, NULL, EVP_sha256(), NULL, priKey) <= 0)
break;
bool isBroken = false;
//now, loop on file and push chunks to sig update...
while (!feof(file))
{
bytesRead = fread(ucBuffer, 1, bufSize, file);
if (bytesRead > 0)
{
if (EVP_DigestSignUpdate(rSASignCtx, ucBuffer, bytesRead) <= 0) {
isBroken = true;
break;
}
}
if (bytesRead != bufSize)
break;
}
if (isBroken)
break;
size_t encLen = 0;
if (EVP_DigestSignFinal(rSASignCtx, NULL, &encLen) <= 0 || encLen == 0)
break;
signature.resize(encLen, '\0');
unsigned char* encPtr = reinterpret_cast<unsigned char*>(&*signature.begin());
if (EVP_DigestSignFinal(rSASignCtx, encPtr, &encLen) <= 0)
break;
std::string signatureBin;
signature.swap(signatureBin);
Base64Encode(encPtr, encLen, &signatureRaw); //TODO: method leaks!
signature = signatureRaw;
rv = true;
} while (false);
if (signatureRaw!=NULL)
free(signatureRaw);
signatureRaw = NULL;
if (ucBuffer != NULL)
delete[] ucBuffer;
ucBuffer = NULL;
if (priKey != NULL)
EVP_PKEY_free(priKey);
priKey = NULL;
if (rSASignCtx != NULL)
EVP_MD_CTX_cleanup(rSASignCtx);
rSASignCtx = NULL;
if (keybio != NULL)
BIO_free(keybio);
keybio = NULL;
if (file != NULL) {
fclose(file);
}
file = NULL;
return rv;
}
bool VerifyRSASignatureOnFile(const std::string& fileName, const std::string& rsaPublicKey, const std::string& aSignature) {
if (rsaPublicKey.empty() || fileName.empty() || aSignature.empty())
return false;
unsigned char* signature = NULL;
size_t signatureLen = 0;
Base64Decode(aSignature.c_str(), &signature, &signatureLen);
static const size_t bufSize = getpagesize();
RSA* publicRSA = NULL;
BIO* keybio = NULL;
EVP_MD_CTX* rSAVerifyCtx = NULL;
EVP_PKEY* pubKey = NULL;
FILE* file = NULL;
bool rv = false;
unsigned char* ucBuffer = NULL;
do {
file = fopen(fileName.c_str(), "rb");
if (file == NULL)
break;
size_t bytesRead = 0;
ucBuffer = new unsigned char[bufSize];
keybio = BIO_new_mem_buf(reinterpret_cast<const void*>(rsaPublicKey.c_str()), static_cast<int>(rsaPublicKey.size()));
if (keybio == NULL)
break;
publicRSA = PEM_read_bio_RSA_PUBKEY(keybio, &publicRSA, NULL, NULL);
if (publicRSA == NULL)
break;
rSAVerifyCtx = EVP_MD_CTX_create();
if (rSAVerifyCtx == NULL)
break;
pubKey = EVP_PKEY_new();
if (pubKey == NULL)
break;
EVP_PKEY_assign_RSA(pubKey, publicRSA);
if (EVP_DigestVerifyInit(rSAVerifyCtx, NULL, EVP_sha256(), NULL, pubKey) <= 0)
break;
//now, loop on file and push chunks to verify update...
bool isBroken = false;
while (!feof(file))
{
bytesRead = fread(ucBuffer, 1, bufSize, file);
if (bytesRead > 0)
{
if (EVP_DigestVerifyUpdate(rSAVerifyCtx, ucBuffer, bytesRead) <= 0) {
isBroken = true;
break;
}
}
if (bytesRead != bufSize)
break;
}
if (isBroken)
break;
int authStatus = EVP_DigestVerifyFinal(rSAVerifyCtx, signature, signatureLen);
if (authStatus != 1)
break;
rv = true;
} while (false);
if (ucBuffer != NULL)
delete[] ucBuffer;
ucBuffer = NULL;
if (pubKey != NULL)
EVP_PKEY_free(pubKey);
pubKey = NULL;
if (rSAVerifyCtx != NULL)
EVP_MD_CTX_cleanup(rSAVerifyCtx);
rSAVerifyCtx = NULL;
if (keybio != NULL)
BIO_free(keybio);
keybio = NULL;
if (file != NULL) {
fclose(file);
}
file = NULL;
if (signature != NULL)
free(signature);
signature = NULL;
return rv;
}
#define USE_FILE 1
int main() {
#if USE_FILE
const std::string data(1024 * 1024, '1'); //use some big data to demonstrate chunking...
std::string filePath = "/tmp/ExampleSignatureFile.txt"; //SUPPOSINGLY running on Linux, otherwise rename to some temporal folder....
FILE* pFile = NULL;
pFile = fopen(filePath.c_str(), "wb");
fwrite(data.c_str(), sizeof(char), data.size(), pFile);
fclose(pFile);
std::string signature;
if(!RSASignatureFromFile(filePath, privateKey, signature))
std::cout << "Failed to sign" << std::endl;
bool authentic = VerifyRSASignatureOnFile(filePath, publicKey, signature);
if (authentic) {
std::cout << "Authentic" << std::endl;
}
else {
std::cout << "Not Authentic" << std::endl;
}
#else
std::string plainText = "My secret message.\n";
char* signature = signMessage(privateKey, plainText);
bool authentic = verifySignature(publicKey, "My secret message.\n", signature);
if (authentic) {
std::cout << "Authentic" << std::endl;
}
else {
std::cout << "Not Authentic" << std::endl;
}
#endif
}
@issaharnoam
Copy link
Author

Hi,

I forked https://gist.github.com/irbull OpenSSLExample.cpp code and added option to work with file which required looping EVP_DigestVerifyUpdate and EVP_DigestSignUpdate on file chunks. code is tested on linux only. using /tmp hardcode path to write/read the file.
I kept my code without memory leak except usage of base64 encode decode methods which originally comes from base branch.
Please be careful in your code to keep care about that.

You are welcome to use

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