-
-
Save issaharnoam/518d15e568dec030d318af1b4177b0a0 to your computer and use it in GitHub Desktop.
Code signing and verification with OpenSSL
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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