-
-
Save irbull/08339ddcd5686f509e9826964b17bb59 to your computer and use it in GitHub Desktop.
#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 <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, 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 = 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, 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; | |
} | |
int main() { | |
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; | |
} | |
} |
I am getting the below error while compiling:
In function ‘void Base64Decode(const char*, unsigned char**, size_t*)’:
testverify2.cpp:166:39: error: invalid conversion from ‘const void*’ to ‘void*’ [-fpermissive]
bio = BIO_new_mem_buf(b64message, -1);
^
In file included from /usr/include/openssl/evp.h:75:0,
from testverify2.cpp:3:
/usr/include/openssl/bio.h:668:6: error: initializing argument 1 of ‘BIO* BIO_new_mem_buf(void*, int)’ [-fpermissive]
BIO *BIO_new_mem_buf(void *buf, int len);
Please help me to resolve.
This is great! Thanks for taking the time to put this together
I am getting the same error as @joysher . I fixed it by changing
void Base64Decode(const char* b64message, unsigned char** buffer, size_t* length) {
to
void Base64Decode(char* b64message, unsigned char** buffer, size_t* length) {
@irbull Thank you for sharing this example. After two days of trying to get other code to work, this was up and running in half an hour.. What is the license for using this code please?
@irbull +1, thanks for this very useful code, would like to know how you license this code.
hello. if i sign text line in php and then verify it if c++ (using this example) I get Not Authentic. Can any one help, what is problem?
EVP_MD_CTX_cleanup()
has removed. I replaced it with EVP_MD_CTX_free()
Ref : https://www.openssl.org/news/cl110.txt
Great thanks for the example, probably the best example on the internet.
I've problem with compile. Debian 9, gcc version 8.3.0, libssl-dev installed
$ g++ -lssl -lcrypto -std=c++11 verifyrsa.cpp
verifyrsa.cpp: In function ‘bool RSASign(RSA*, const unsigned char*, size_t, unsigned char**, size_t*)’:
verifyrsa.cpp:94:3: error: ‘EVP_MD_CTX_cleanup’ was not declared in this scope
EVP_MD_CTX_cleanup(m_RSASignCtx);
^~~~~~~~~~~~~~~~~~
verifyrsa.cpp:94:3: note: suggested alternative: ‘EVP_MD_CTX_create’
EVP_MD_CTX_cleanup(m_RSASignCtx);
^~~~~~~~~~~~~~~~~~
EVP_MD_CTX_create
verifyrsa.cpp: In function ‘bool RSAVerifySignature(RSA*, unsigned char*, size_t, const char*, size_t, bool*)’:
verifyrsa.cpp:118:5: error: ‘EVP_MD_CTX_cleanup’ was not declared in this scope
EVP_MD_CTX_cleanup(m_RSAVerifyCtx);
^~~~~~~~~~~~~~~~~~
verifyrsa.cpp:118:5: note: suggested alternative: ‘EVP_MD_CTX_create’
EVP_MD_CTX_cleanup(m_RSAVerifyCtx);
^~~~~~~~~~~~~~~~~~
EVP_MD_CTX_create
verifyrsa.cpp:122:5: error: ‘EVP_MD_CTX_cleanup’ was not declared in this scope
EVP_MD_CTX_cleanup(m_RSAVerifyCtx);
^~~~~~~~~~~~~~~~~~
verifyrsa.cpp:122:5: note: suggested alternative: ‘EVP_MD_CTX_create’
EVP_MD_CTX_cleanup(m_RSAVerifyCtx);
^~~~~~~~~~~~~~~~~~
EVP_MD_CTX_create
verifyrsa.cpp:126:5: error: ‘EVP_MD_CTX_cleanup’ was not declared in this scope
EVP_MD_CTX_cleanup(m_RSAVerifyCtx);
^~~~~~~~~~~~~~~~~~
verifyrsa.cpp:126:5: note: suggested alternative: ‘EVP_MD_CTX_create’
EVP_MD_CTX_cleanup(m_RSAVerifyCtx);
^~~~~~~~~~~~~~~~~~
EVP_MD_CTX_create
This is beacause of changes in openSSL v1.1
https://wiki.openssl.org/index.php/OpenSSL_1.1.0_Changes
I tried this code within VS 2019 on Windows,there are some issues:
#1 the funtionalities which are Base64Encode , Base64Decode doesn't work ,you have to replace them with something else
#2 you have to replace all the EVP_MD_CTX_cleanup with EVP_MD_CTX_free as well
beside the issue above anything else works fine so far
Hi,
Your code is exactly what I as looking for. Thanks for that.
But I'm facing some problems to find some symbols.
I'm using Ubuntu subsytem on Windows 10. As it came with an old version of openssl
I updated it:
wget https://www.openssl.org/source/openssl-1.1.1a.tar.gz
tar -zxf openssl-1.1.1a.tar.gz && cd openssl-1.1.1a
./config
make
make test # Verify possible erros
sudo mv /usr/bin/openssl ~/tmp # backup current version
sudo make install
sudo ln -s /usr/local/bin/openssl /usr/bin/openssl # create symbolic link
sudo ldconfig # update de symlinks and rebuild library caches
Follow @abcdef123ghi comment, I replaced all EVP_MD_CTX_cleanup
with EVP_MD_CTX_free
.
I noticed that strlen
and padding
functions produced errors as well, that I solved including the #include <string.h>
But, there was another error, it's not possible find EVP_MD_CTX_new
and EVP_MD_CTX_free
:
$ g++ signVerify.cpp -o signVerify -I/usr/include/openssl/ -L/usr/bin/openssl -lssl -lcry
pto
/tmp/ccqw7GSo.o: In function `RSASign(rsa_st*, unsigned char const*, unsigned long, unsigned char**, unsigned long*)':
signVerify.cpp:(.text+0x139): undefined reference to `EVP_MD_CTX_new'
signVerify.cpp:(.text+0x231): undefined reference to `EVP_MD_CTX_free'
/tmp/ccqw7GSo.o: In function `RSAVerifySignature(rsa_st*, unsigned char*, unsigned long, char const*, unsigned long, bool*)':
signVerify.cpp:(.text+0x282): undefined reference to `EVP_MD_CTX_new'
signVerify.cpp:(.text+0x31b): undefined reference to `EVP_MD_CTX_free'
signVerify.cpp:(.text+0x33b): undefined reference to `EVP_MD_CTX_free'
signVerify.cpp:(.text+0x355): undefined reference to `EVP_MD_CTX_free'
collect2: error: ld returned 1 exit status
Any suggestion? How can I find the exactly library where this functions must be?
Thanks
Hey guys,
I'v just found the error, and just to register here:
It was necessary remove libssl-dev
sudo apt-get remove libssl-dev
Maybe a dumb question , but I do not see a free after malloc. Did I miss something?
I want to say that this code is helpful and does work. But be careful as there are memory leaks here (I had to check all openssl functions used here and find the function used to free the allocated memory and then verify that there were no memory leaks). Apart from openssl functions the malloc is never freed, so keep that in mind
I am getting access violation exception at EVP_DigestVerifyFinal api. Not able to understand why as pointer its data and memory pointing is correct
can please anybody suggest anything
I don't know what am I doing wrong, but
bool authentic = verifySignature(publicKey, "My secret message.\n", signature);
always returns false
I have replaced all EVP_MD_CTX_cleanup()
with EVP_MD_CTX_free()
but this might not be the case.
my EVP_DigestVerifyFinal()
returns 0
ERR_get_error()
and ERR_error_string()
called after it returns "err 67108983 = error:04000077:rsa routines::wrong signature length"
Update: I got rid of all base64 encoding-decoding in my code, and signature-text verification worked like a charm!
BIO_read(bio, *buffer, strlen(b64message));
was returning 0, so EVP_DigestVerifyFinal() returned errors.
add
(*base64Text)[(*bufferPtr).length] = '\0';
after line 146
will fix bugs about base64 encoding
I don't know what am I doing wrong, but
bool authentic = verifySignature(publicKey, "My secret message.\n", signature);
always returns false
I have replaced allEVP_MD_CTX_cleanup()
withEVP_MD_CTX_free()
but this might not be the case.
myEVP_DigestVerifyFinal()
returns 0
ERR_get_error()
andERR_error_string()
called after it returns "err 67108983 = error:04000077:rsa routines::wrong signature length"Update: I got rid of all base64 encoding-decoding in my code, and signature-text verification worked like a charm!
BIO_read(bio, *buffer, strlen(b64message));
was returning 0, so EVP_DigestVerifyFinal() returned errors.
this encoder forget to add '\0' at the end of the char* string
see my comment for fixing
Why don't you just use return value of function RSAVerifySignature(...) to indicate verification is successful or failure?
Hi, Thank you for sharing this, great work.
When I run Valgrind it is reporting some memory leaks. I think it comes from *b64 which is created in the heap but never freed. I don't think it is freed internally by libcrypto (on Linux).
I'll investigate more and let you know.
Hi, Thank you for sharing this, great work. When I run Valgrind it is reporting some memory leaks. I think it comes from *b64 which is created in the heap but never freed. I don't think it is freed internally by libcrypto (on Linux). I'll investigate more and let you know.
I fixed this memory leaks. This is my fixed codes.
`#include
#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 <assert.h>
#include <string.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);
BIO_free(keybio);
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);
BIO_free(keybio);
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) {
EVP_PKEY_free(priKey);
return false;
}
if (EVP_DigestSignUpdate(m_RSASignCtx, Msg, MsgLen) <= 0) {
EVP_PKEY_free(priKey);
return false;
}
if (EVP_DigestSignFinal(m_RSASignCtx, NULL, MsgLenEnc) <=0) {
EVP_PKEY_free(priKey);
return false;
}
EncMsg = (unsigned char)malloc(*MsgLenEnc);
if (EVP_DigestSignFinal(m_RSASignCtx, *EncMsg, MsgLenEnc) <= 0) {
EVP_PKEY_free(priKey);
return false;
}
EVP_PKEY_free(priKey);
EVP_MD_CTX_free(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) {
EVP_PKEY_free(pubKey);
return false;
}
if (EVP_DigestVerifyUpdate(m_RSAVerifyCtx, Msg, MsgLen) <= 0) {
EVP_PKEY_free(pubKey);
return false;
}
int AuthStatus = EVP_DigestVerifyFinal(m_RSAVerifyCtx, MsgHash, MsgHashLen);
if (AuthStatus==1) {
*Authentic = true;
EVP_PKEY_free(pubKey);
EVP_MD_CTX_free(m_RSAVerifyCtx);
return true;
} else if(AuthStatus==0){
*Authentic = false;
EVP_PKEY_free(pubKey);
EVP_MD_CTX_free(m_RSAVerifyCtx);
return true;
} else{
*Authentic = false;
EVP_PKEY_free(pubKey);
EVP_MD_CTX_free(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, length);
BIO_flush(bio);
BIO_get_mem_ptr(bio, &bufferPtr);
BIO_set_close(bio, BIO_NOCLOSE);
BIO_free_all(bio);
*base64Text=(*bufferPtr).data;
(*base64Text)[(*bufferPtr).length] = '\0';
}
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 = 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, 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);
free(encMessage);
return result & authentic;
}
int main() {
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;
}
}
`
there are still memory leaks. one of them can be solved by adding free(signature);
as a last line in the main()
and then there is 1 memory leak remaining which i haven't found yet:
==28980== HEAP SUMMARY:
==28980== in use at exit: 32 bytes in 1 blocks
==28980== total heap usage: 11,284 allocs, 11,283 frees, 1,362,954 bytes allocated
==28980==
==28980== 32 bytes in 1 blocks are definitely lost in loss record 1 of 1
==28980== at 0x4848899: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==28980== by 0x4A2B41D: CRYPTO_zalloc (in /usr/lib/x86_64-linux-gnu/libcrypto.so.3)
==28980== by 0x496AD13: BUF_MEM_new (in /usr/lib/x86_64-linux-gnu/libcrypto.so.3)
==28980== by 0x496AD6C: BUF_MEM_new_ex (in /usr/lib/x86_64-linux-gnu/libcrypto.so.3)
==28980== by 0x4958E99: ??? (in /usr/lib/x86_64-linux-gnu/libcrypto.so.3)
==28980== by 0x494FA13: BIO_new_ex (in /usr/lib/x86_64-linux-gnu/libcrypto.so.3)
==28980== by 0x10AC23: Base64Encode(unsigned char const*, unsigned long, char**) (openssl_example_new.cc:165)
==28980== by 0x10AEF5: signMessage(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >) (openssl_example_new.cc:211)
==28980== by 0x10B0DA: main (openssl_example_new.cc:232)
==28980==
==28980== LEAK SUMMARY:
==28980== definitely lost: 32 bytes in 1 blocks
==28980== indirectly lost: 0 bytes in 0 blocks
==28980== possibly lost: 0 bytes in 0 blocks
==28980== still reachable: 0 bytes in 0 blocks
==28980== suppressed: 0 bytes in 0 blocks
==28980==
==28980== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
btw. how could we replace 3 deprecated functions?
/home/xyz/abc/cc/openssl/openssl_example_new.cc:59:37: warning: ‘RSA* PEM_read_bio_RSAPrivateKey(BIO*, RSA**, int (*)(char*, int, int, void*), void*)’ is deprecated: Since OpenSSL 3.0 [-Wdeprecated-declarations]
59 | rsa = PEM_read_bio_RSAPrivateKey(keybio, &rsa, nullptr, nullptr);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /home/xyz/abc/cc/openssl/openssl_example_new.cc:7:
/usr/include/openssl/pem.h:447:1: note: declared here
447 | DECLARE_PEM_rw_cb_attr(OSSL_DEPRECATEDIN_3_0, RSAPrivateKey, RSA)
| ^~~~~~~~~~~~~~~~~~~~~~
/home/xyz/abc/cc/openssl/openssl_example_new.cc: In function ‘RSA* createPublicRSA(std::string)’:
/home/xzy/abc/cc/openssl/openssl_example_new.cc:87:34: warning: ‘RSA* PEM_read_bio_RSA_PUBKEY(BIO*, RSA**, int (*)(char*, int, int, void*), void*)’ is deprecated: Since OpenSSL 3.0 [-Wdeprecated-declarations]
87 | rsa = PEM_read_bio_RSA_PUBKEY(keybio, &rsa,nullptr, nullptr);
| ~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/openssl/pem.h:449:1: note: declared here
449 | DECLARE_PEM_rw_attr(OSSL_DEPRECATEDIN_3_0, RSA_PUBKEY, RSA)
| ^~~~~~~~~~~~~~~~~~~
[100%] Linking CXX executable openssl_example_new
[100%] Built target openssl_example_new
Authentic
i've just found last memory leaks. you can fix it by adding free(bufferPtr);
as a last line in the Base64Encode()
but that's not much nice code at all...
It really helped me. i think the part of base64 code can`t peer to current standard. use some other popular base64 open source library can make it more compatible.
It really helped me, i'm using Objective-C
for openssl>=1.1.0 use EVP_MD_CTX_free
instead of EVP_MD_CTX_cleanup
. It works for me on my linux.
#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 <assert.h>
#include <cstring>
/**
g++ b.cpp -o main -lcryptopp -lssl -lcrypto 2>&1 && ./main
*/
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_free(m_RSASignCtx);
EVP_MD_CTX_free(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_free(m_RSAVerifyCtx);
return true;
} else if(AuthStatus==0){
*Authentic = false;
EVP_MD_CTX_free(m_RSAVerifyCtx);
return true;
} else{
*Authentic = false;
EVP_MD_CTX_free(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, 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 = 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, 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;
}
int main() {
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;
}
}
Thank you so much for this sample code! Made my life easier. Had a hard time resolving private key sign and public key verify codes in other sources. And this just works.