PEM_read_bio_PUBKEY returns nullptr - c++

I'll include my full code below as well as a sample run. In short, this is failing:
EVP_PKEY * pk = PEM_read_bio_PUBKEY(bioReadPublic, nullptr, nullptr, nullptr);
If I cut & paste a public key from examples found on the internet, it works. So I think the error has to do with how I'm producing the keys. But I can use the private key just fine (or at least I think I can).
When I cout them, both the public and private keys look okay.
Does anyone see what I'm doing wrong?
The code here, and a sample run to follow.
#include <iostream>
#include <iostream>
#include <openssl/bio.h>
#include <openssl/pem.h>
using std::cout;
using std::cerr;
using std::endl;
using std::string;
int main()
{
::RSA * rsa = nullptr;
::BIGNUM * bne = nullptr;
::BIO * bioPrivate = nullptr;
::BIO * bioPublic = nullptr;
::BIO * bioReadPublic = nullptr;
::BIO * bioReadPrivate = nullptr;
::RSA * privateRSA = nullptr;
::RSA * publicRSA = nullptr;
int bits = 2048;
try {
// This code produces the public/private keys.
rsa = RSA_new();
bne = BN_new();
BN_set_word(bne, RSA_F4);
RSA_generate_key_ex(rsa, bits, bne, NULL);
bioPrivate = BIO_new( BIO_s_mem() );
bioPublic = BIO_new( BIO_s_mem() );
PEM_write_bio_RSAPrivateKey( bioPrivate, rsa, nullptr, nullptr, 0, nullptr, nullptr );
PEM_write_bio_RSAPublicKey( bioPublic, rsa );
size_t privateLen = BIO_pending( bioPrivate );
size_t publicLen = BIO_pending( bioPublic );
// Oversized.
char privateBuffer[10000];
char publicBuffer[10000];
BIO_read(bioPrivate, privateBuffer, privateLen);
BIO_read(bioPublic, publicBuffer, publicLen);
privateBuffer[privateLen] = 0;
publicBuffer[publicLen] = 0;
string privateKey = privateBuffer;
string publicKey = publicBuffer;
cout << "Keys:\n"
<< privateKey << endl << endl
<< publicKey << endl << endl;
// And this is where I try to use them.
cout << "Try PEM_read\n";
bioReadPrivate = BIO_new_mem_buf( (void *)privateBuffer, privateLen );
bioReadPublic = BIO_new_mem_buf( (void *)publicBuffer, publicLen );
cout << "The private...\n";
privateRSA = PEM_read_bio_RSAPrivateKey(bioReadPrivate, nullptr, nullptr, nullptr);
if (privateRSA == nullptr) {
cout << "Failed to read private key\n";
}
cout << "The public...\n";
publicRSA = PEM_read_bio_RSAPublicKey(bioReadPublic, nullptr, nullptr, nullptr);
if (publicRSA == nullptr) {
cout << "Failed to read public key\n";
}
EVP_PKEY * pk = PEM_read_bio_PUBKEY(bioReadPublic, nullptr, nullptr, nullptr);
if (pk == nullptr) {
cout << "PEM_read_bio_PUBKEY failed\n";
}
}
catch (const std::exception &e) {
cerr << "Exception: " << e.what() << endl;
}
cout << "Done.\n";
if (privateRSA != nullptr) {
RSA_free(privateRSA);
}
if (publicRSA != nullptr) {
RSA_free(publicRSA);
}
if (bioReadPrivate != nullptr) {
BIO_free(bioReadPrivate);
}
if (bioReadPublic != nullptr) {
BIO_free(bioReadPublic);
}
if (bioPublic != nullptr) {
BIO_free(bioPublic);
}
if (bioPrivate != nullptr) {
BIO_free(bioPrivate);
}
if (bne != nullptr) {
BN_free(bne);
}
if (rsa != nullptr) {
RSA_free(rsa);
}
return 0;
}
Running it produces this output:
Keys:
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEArqd1UmR+VYPByNJujunS5FhlkSgsuxiSTjWJCdNoUkdIcKxJ
6ZvNV6uQAkJdL3RgHBRmhrb2CBTkkYdJoB0HyY58AgTH25eJpW7aRmHMh6exzrSv
47MkKVuHVfLNw/RjA3cTDMMgg5SHg4bS8O9lXa9A3YIAUtTb3f4xhuYX8ZG64SzB
lhCruCRKubb1N7eSuqLpq8DJqMj842PEwEU5HfQhxWHU4KAK43Wkglq6BjkKYULR
0/cUm8hMK5WE63LJ0mSsU4sD9UdzpbPD1WO+KBPLX6C0O/z6fGmEIwEyjNmXomUE
tqOBhW+1tEvF9T/l5UF/a+RyQs4uPjqsuiBfrQIDAQABAoIBAH6FqSQK0Mo6LIPC
jXzSPohU0R8ar2zKHBK0DZYkpAoWwY99//1PdK3yBtzcHo1mDMnKrYsht5GbMQmL
q/ZlkWKd6Y+8zttxJ7HEYDNkXbpX7SMFGfYlrCiZuay1vAIcZC36cL5qDH/FdXX+
kYkgo5n+f0r4Biv7L1vd7RsATJAeAla8wz+SQ5TVyONMQ4KYvQFk4GrIOtkdvVjW
B+EMxNPRFdLBpGL0cAwwEMS71H30R+NqOogRWpok/gE4LTe3tohoNMr9UvRrQucG
xLLUMLlQFe+t2iys1oRLRbIsPbp8+Jb0LkPnEuz+rG86nyg5UnTyI2KSkW5GO2sP
1l+xUDECgYEA55JVMSOeQWFBfh1MySbOsbceGT/0ZPmrOEj0n2Sab59iJv/9B/W0
SlZ+nmZzDq7Zxkefaaq2+1uaqHzPstqJUPAuCvJV4dJ1dMX3gAhVxMiIyPR4KywZ
Wq22r7hWOggwQ8nt7YLhbXX0NG9MepHUpbYniGBH04y/Ff4kV/yMkKcCgYEAwRQM
5KZa2GNjcqRH+G+/DyjSml8cyv/HyA/bp5hsRUZJdNnuHK0Dl07IED31d3r023Mz
vBgwqZ0DNnfNvqebrtkeQE2MBgXBU5r8lZ/Xc1uzC5ClYxvjAMvR/GW9XpatqvZa
+tw/DeQ08htwc/yvKAlJwnp3AQRmDQyVwG+hI4sCgYAc7MBLZQGNdRJzKEHBFmHW
/OJ7kOt7+VuTqjkz96L6slPWPz9h0ST17BvguTdB7EEzbRrSXgqBFw4YYY6n7ngc
bgOyRlbH4DvfnCKwEVp1O/8p6q9f5SA9nVkmVcYfdYphzUU015ZEz+jy67wHy1Ne
Z4REmpIeMIRlwNrVSqxcHQKBgF3DMOew6Zj/2wGHSfQZwDzKZEvXOJJUWF6NZOwk
lx+lHHMEih+e3YmTFpDcpeHZ8iLH9S24yZj0yOSglWeq2W21vn0Xq5IZJWNGdrbq
oqRAudg57DcPCdQPJvBdL/NJVRka/d+pKW3Djvqr3JZW9XCJ4inxcu0ph616mRaU
nzo1AoGAbSMgdhMRq1x78LhI4zHklyonA55T5eO3Tk+tyXF513e9LmVTgVg6sCsj
pU+4/gMHa3oDn+LU1LsxuJU+xn/zHVFTjIYaw8+dLzHv7mVz3DpdeAVud3BRuHNu
kj7X7ZsRRCtGv8762B5YOdbAlXStc6i2JHLyWI1n7s324hm1f3c=
-----END RSA PRIVATE KEY-----
-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEArqd1UmR+VYPByNJujunS5FhlkSgsuxiSTjWJCdNoUkdIcKxJ6ZvN
V6uQAkJdL3RgHBRmhrb2CBTkkYdJoB0HyY58AgTH25eJpW7aRmHMh6exzrSv47Mk
KVuHVfLNw/RjA3cTDMMgg5SHg4bS8O9lXa9A3YIAUtTb3f4xhuYX8ZG64SzBlhCr
uCRKubb1N7eSuqLpq8DJqMj842PEwEU5HfQhxWHU4KAK43Wkglq6BjkKYULR0/cU
m8hMK5WE63LJ0mSsU4sD9UdzpbPD1WO+KBPLX6C0O/z6fGmEIwEyjNmXomUEtqOB
hW+1tEvF9T/l5UF/a+RyQs4uPjqsuiBfrQIDAQAB
-----END RSA PUBLIC KEY-----
Try PEM_read
The private...
The public...
PEM_read_bio_PUBKEY failed
Done.

Okay, this came down to one line of code.
The underlying JWT library I'm using does this:
EVP_PKEY * pk = PEM_read_bio_PUBKEY(bioReadPublic, nullptr, nullptr, nullptr);
Which means my public key needs to be compatible with doing that. So instead of using PEM_write_bio_RSAPublicKey, I have to use PEM_write_bio_RSA_PUBKEY like this:
PEM_write_bio_RSA_PUBKEY( bioPublic, rsa );
The resulting data is very slightly different. The public key looks like this:
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoMeN556vV68LEAQ1KE22
n6fHTCsVV1CwKMo8L6WvCMD13xNs72MwA+FNNOZUTg2+2HvaH7ZnwNgLPeY/O+Rc
vTBLI90qGxAubGYnvp+76hNROzXQfLFlTQdGiu2vF0oiCU00EDKM4xzL8iOf7H8A
aw9f6S0Y9UiaZAMJ0CLXwv6yWLvxHgNGiJ1Z3VGrUnW16K9EGyoNMYARPiJqSMaG
Grw26SUBzA485zzZfiIgnruriuiBQPp9dL5vvGAvRFODCcj45Jqpo25hTSADQTw3
kUUQ5WRRy2Din1VAEWJflCUMAf+VowODoglZP5YOSkf8lsBlVpekroWNFVaQehs3
MwIDAQAB
-----END PUBLIC KEY-----
If you go back to my original post, you'll see the format is just slightly different. BEGIN RSA PUBLIC KEY vs BEGIN PUBLIC KEY. I don't know if there are more differences, because of course every time I run it, it generates new keys, and I haven't looked further.

Related

OpenSSL RSA Encryption/Decryption with EVP Methods

I´ve created a rsa class with Openssl EVP (OpenSSL Ref) Methods.
rsa.h
#ifndef RSA_RSA_H
#define RSA_RSA_H
#include <string>
#include <stdexcept>
#include <sstream>
#include <iostream>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include "utils.h"
#define PADDING RSA_PKCS1_OAEP_PADDING
#define LOGGING
namespace tools
{
class CryptoRSA
{
private:
int _keysize;
char *_rsaPrivateKeyStr, *_privateKeyStr, *_publicKeyStr;
unsigned char* _encryptedKey, *_iv;
EVP_PKEY *_pkey;
EVP_CIPHER_CTX *rsaEncryptContext, *rsaDecryptContext;
static std::string getOpenSSLError();
bool generateRSAKey();
bool loadKeyFromFile(const std::string &filepath);
bool loadKeyFromStr(const std::string &str);
public:
explicit CryptoRSA(int keysize);
explicit CryptoRSA(const std::string &key);
virtual ~CryptoRSA();
inline unsigned char* getRSAIV() const { return _iv; }
inline unsigned char* getRSAEncryptedKey() const { return _encryptedKey; }
inline EVP_PKEY *getEvpPkey() { return _pkey; }
inline int getEvpPkeySize(EVP_PKEY *key) { return EVP_PKEY_size(key); }
char *getRSAPrivateKeyStr();
char *getPrivateKeyStr();
char *getPublicKeyStr();
int encryptEVP(EVP_PKEY *key, const unsigned char *message, size_t messageLength, unsigned char **encryptedMessage, unsigned char **encryptedKey, size_t *encryptedKeyLength, unsigned char **iv);
int decryptEVP(EVP_PKEY *key, unsigned char *encryptedMessage, size_t encryptedMessageLength, unsigned char *encryptedKey, size_t encryptedKeyLength, unsigned char *iv, unsigned char **decryptedMessage);
};
} // namespace tools
#endif //RSA_RSA_H
and rsa.cpp
#include "rsa.h"
namespace tools
{
CryptoRSA::CryptoRSA(int keysize) : _keysize(keysize), _pkey(nullptr)
{
// Initalize contexts
rsaEncryptContext = EVP_CIPHER_CTX_new();
rsaDecryptContext = EVP_CIPHER_CTX_new();
// Generate the RSA Key
if ( !generateRSAKey() )
{
#ifdef LOGGING
std::cerr << "Error at generating the RSA key!" << std::endl;
#endif
throw std::runtime_error("Error at generating the RSA key!");
}
}
CryptoRSA::CryptoRSA(const std::string &key) : _keysize(-1), _pkey(nullptr)
{
// Initalize contexts
rsaEncryptContext = EVP_CIPHER_CTX_new();
rsaDecryptContext = EVP_CIPHER_CTX_new();
// Load pkey from file or string
if ( !loadKeyFromFile(key) )
{
#ifdef LOGGING
std::cerr << "Could not load key from file/string!" << std::endl;
#endif
}
}
CryptoRSA::~CryptoRSA()
{
EVP_PKEY_free(_pkey);
EVP_CIPHER_CTX_free(rsaEncryptContext);
EVP_CIPHER_CTX_free(rsaDecryptContext);
}
std::string CryptoRSA::getOpenSSLError()
{
char *buf;
BIO *bio = BIO_new(BIO_s_mem());
ERR_print_errors(bio);
size_t len = static_cast<size_t >(BIO_get_mem_data(bio, &buf));
std::string err(buf, len);
BIO_free(bio);
return err;
}
bool CryptoRSA::generateRSAKey()
{
EVP_PKEY_CTX *context = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
if(EVP_PKEY_keygen_init(context) <= 0)
{
return false;
}
if(EVP_PKEY_CTX_set_rsa_keygen_bits(context, _keysize) <= 0)
{
return false;
}
if(EVP_PKEY_keygen(context, &_pkey) <= 0)
{
return false;
}
EVP_PKEY_CTX_free(context);
return true;
}
bool CryptoRSA::loadKeyFromFile(const std::string &filepath)
{
if ( existsFile(filepath) )
{
if ( loadKeyFromStr(readBinFile(filepath)) )
{
#ifdef LOGGING
std::cerr << "Loaded successfully rsa key from file " << filepath << std::endl;
#endif
return true;
}
} else
{
if ( loadKeyFromStr(filepath) )
{
#ifdef LOGGING
std::cerr << "Loaded successfully rsa key from string!" << std::endl;
#endif
return true;
}
}
return false;
}
bool CryptoRSA::loadKeyFromStr(const std::string &str)
{
std::string fLine;
std::istringstream f(str);
std::getline(f, fLine);
BIO *bioPrivate = BIO_new(BIO_s_mem());
BIO_write(bioPrivate, str.c_str(), static_cast<int>(str.length()));
if (fLine == "-----BEGIN RSA PRIVATE KEY-----")
{
_pkey = PEM_read_bio_PrivateKey(bioPrivate, nullptr, nullptr, nullptr);
} else if (fLine == "-----BEGIN PUBLIC KEY-----")
{
_pkey = PEM_read_bio_PUBKEY(bioPrivate, nullptr, nullptr, nullptr);
} else
{
#ifdef LOGGING
std::cerr << "Unsupported file provided with file header: " << fLine << std::endl;
#endif
BIO_free(bioPrivate);
return false;
}
BIO_free(bioPrivate);
return true;
}
char* CryptoRSA::getRSAPrivateKeyStr()
{
RSA *rsaPrivateKey = EVP_PKEY_get0_RSA(_pkey);
BIO *bioPrivate = BIO_new(BIO_s_mem());
PEM_write_bio_RSAPrivateKey(bioPrivate, rsaPrivateKey, nullptr, nullptr, 0, nullptr, nullptr);
BIO_flush(bioPrivate);
BIO_get_mem_data(bioPrivate, &_rsaPrivateKeyStr);
return _rsaPrivateKeyStr;
}
char* CryptoRSA::getPrivateKeyStr()
{
BIO *bioPrivate = BIO_new(BIO_s_mem());
PEM_write_bio_PrivateKey(bioPrivate, _pkey, NULL, NULL, 0, 0, NULL);
BIO_flush(bioPrivate);
BIO_get_mem_data(bioPrivate, &_privateKeyStr);
return _privateKeyStr;
}
char* CryptoRSA::getPublicKeyStr()
{
BIO *bioPublic = BIO_new(BIO_s_mem());
PEM_write_bio_PUBKEY(bioPublic, _pkey);
BIO_flush(bioPublic);
BIO_get_mem_data(bioPublic, &_publicKeyStr);
return _publicKeyStr;
}
int CryptoRSA::encryptEVP(EVP_PKEY *key, const unsigned char *message, size_t messageLength, unsigned char **encryptedMessage,
unsigned char **encryptedKey, size_t *encryptedKeyLength, unsigned char **iv)
{
// Allocate memory for everything
size_t encryptedMessageLength = 0;
size_t blockLength = 0;
*encryptedKey = (unsigned char*)malloc(EVP_PKEY_size(key));
*iv = (unsigned char*)malloc(EVP_MAX_IV_LENGTH);
*encryptedMessage = (unsigned char*)malloc(messageLength + EVP_MAX_IV_LENGTH);
if(!EVP_SealInit(rsaEncryptContext, EVP_aes_256_cbc(), encryptedKey, (int*)encryptedKeyLength, *iv, &key, 1))
{
#ifdef LOGGING
std::cerr << "Error during EVP_SealInit in RSA encrypt: " << getOpenSSLError() << std::endl;
#endif
return -1;
}
if(!EVP_SealUpdate(rsaEncryptContext, *encryptedMessage + encryptedMessageLength, (int*)&blockLength, (const unsigned char*)message, (int)messageLength))
{
#ifdef LOGGING
std::cerr << "Error during EVP_SealUpdate in RSA encrypt: " << getOpenSSLError() << std::endl;
#endif
return -1;
}
encryptedMessageLength += blockLength;
if(!EVP_SealFinal(rsaEncryptContext, *encryptedMessage + encryptedMessageLength, (int*)&blockLength))
{
#ifdef LOGGING
std::cerr << "Error during EVP_SealFinal in RSA encrypt: " << getOpenSSLError() <<std::endl;
#endif
return -1;
}
encryptedMessageLength += blockLength;
return (int)encryptedMessageLength;
}
int CryptoRSA::decryptEVP(EVP_PKEY *key, unsigned char *encryptedMessage, size_t encryptedMessageLength, unsigned char *encryptedKey,
size_t encryptedKeyLength, unsigned char *iv, unsigned char **decryptedMessage)
{
// Allocate memory for everything
size_t decryptedMessageLength = 0;
size_t blockLength = 0;
*decryptedMessage = (unsigned char*)malloc(encryptedMessageLength + EVP_MAX_IV_LENGTH);
// Decrypt it!
if(!EVP_OpenInit(rsaDecryptContext, EVP_aes_256_cbc(), encryptedKey, getEvpPkeySize(key), iv, key))
{
#ifdef LOGGING
std::cerr << "Error during EVP_OpenInit in RSA decrypt: " << getOpenSSLError() << std::endl;
#endif
return -1;
}
if(!EVP_OpenUpdate(rsaDecryptContext, (unsigned char*)*decryptedMessage + decryptedMessageLength, (int*)&blockLength, encryptedMessage, (int)encryptedMessageLength))
{
#ifdef LOGGING
std::cerr << "Error during EVP_OpenUpdate in RSA decrypt: " << getOpenSSLError() << std::endl;
#endif
return -1;
}
decryptedMessageLength += blockLength;
if(!EVP_OpenFinal(rsaDecryptContext, (unsigned char*)*decryptedMessage + decryptedMessageLength, (int*)&blockLength))
{
#ifdef LOGGING
std::cerr << "Error during EVP_OpenFinal in RSA decrypt: " << getOpenSSLError() << std::endl;
#endif
return -1;
}
decryptedMessageLength += blockLength;
return (int)decryptedMessageLength;
}
} // namespace tools
main.cpp
#include <iostream>
#include <memory>
#include <cstring>
#include "rsa.h"
bool writeBinFile(const std::string &filepath, const char* content, long contentLength, bool append=false)
{
std::fstream out;
if (append)
{
out.open(filepath, std::ios::out | std::ios::app | std::ios::binary);
} else
{
out.open(filepath, std::ios::out | std::ios::binary);
}
if (out.is_open())
{
out.write(content, contentLength);
return true;
} else
{
return false;
}
}
void encrypt_decrypt_with_evp(std::string msg_to_encrypt)
{
std::unique_ptr<tools::CryptoRSA> cryptoRSA = std::unique_ptr<tools::CryptoRSA>(new tools::CryptoRSA(2048));
unsigned char *encryptedMessage = nullptr;
char *decryptedMessage = nullptr;
unsigned char *encryptedKey;
unsigned char *iv;
size_t encryptedKeyLength;
std::string priv = cryptoRSA->getRSAPrivateKeyStr();
std::string pub = cryptoRSA->getPublicKeyStr();
// Save RSA KeyPair
writeBinFile("RSAPrivateKey.pem", priv.c_str(), priv.length());
writeBinFile("PublicKey.pem", pub.c_str(), pub.length());
// Start RSA Encryption
int encryptedMessageLength = cryptoRSA->encryptEVP(cryptoRSA->getEvpPkey(), (const unsigned char*)msg_to_encrypt.c_str(), msg_to_encrypt.size()+1,
&encryptedMessage, &encryptedKey, &encryptedKeyLength, &iv);
if(encryptedMessageLength == -1)
{
std::cerr << "Encryption failed" << std::endl;
exit(1);
}
std::cout << "Encrypted message: " << encryptedMessage << std::endl;
writeBinFile("enc_msg.bin", reinterpret_cast<const char *>(encryptedMessage), encryptedMessageLength);
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Start RSA Decryption
int decryptedMessageLength = cryptoRSA->decryptEVP(cryptoRSA->getEvpPkey(), encryptedMessage, (size_t)encryptedMessageLength,
encryptedKey, encryptedKeyLength, iv, (unsigned char**)&decryptedMessage);
if(decryptedMessageLength == -1)
{
std::cerr << "Decryption failed" << std::endl;
exit(1);
}
std::cout << "Decrypted message: " << decryptedMessage << std::endl;
}
int main(int argc, char* argv[])
{
encrypt_decrypt_with_evp("abcdef");
return 0;
}
The RSA Encryption and Decryption with the implemented class works completely fine.
What i would like to achieve is the following:
Encrypt a message like abcdef with my implemented solution and save the content in a file enc_msg.bin
Using the openssl command line interface (for instance: openssl rsautl) to decrypt the message
Or:
Encrypt the message abcdef with the openssl command line interface
Decrypt the created file with my implemented solution
As you can see the EVP_SealInit uses the EVP_aes_256_cbc with an encryptedKey and an IV. But i didn´t find a possibility to provide those parameters to the openssl rsautl cli.
Does anyone know how to decrypt messages (with Openssl cli) which were created with the EVP methods (EVP_SealInit, EVP_SealUpdate, EVP_SealFinal)?
Every help is highly appreciated!
With the help of #Topaco i found the solution. It is a Hybrid Encryption
I saved also the EncryptedKey and the IV to rsa_ek.bin and rsa_iv.txt file.
Decrypt the rsa_ek.bin with the openssl rsautl cli
openssl rsautl -decrypt -inkey RSAPrivateKey.pem -in rsa_ek.bin -out rsa_ek.txt
Decrypt the enc_msg.bin file with the AES-256-CBC Cipher
openssl enc -aes-256-cbc -d -in enc_msg.bin -K $(xxd -p -c 256 rsa_ek.txt) -iv $(xxd -p -c 256 rsa_iv.txt) -out enc_msg.dec.txt

OpenSSL RSA_sign C++ - Different sign than the command line

I am trying to develop a Digital Signature tool using OpenSSL's libs but the created sign from my code is different than the one I get from the command line.
I checked and validated that the SHA256 digest is correct.
This is an example of the command I use to validate the signatures:
openssl rsautl -sign -inkey privateKey.pem -in hash.txt > signature
Based on my code below, is there something obvious that I am doing wrong?
#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
#include <iomanip>
#include <string.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/sha.h>
#include <openssl/rsa.h>
using namespace std;
RSA * generateKeys()
{
RSA *rsa = RSA_new();
BIGNUM *bignum = NULL;
bignum = BN_new();
BN_set_word(bignum, RSA_F4);
FILE *file;
bool exists;
if ((file = fopen("privateKey.pem", "r")))
{
exists = true;
fclose(file);
}
if (!exists)
{
FILE *pkey = fopen("privateKey.pem", "wb");
RSA_generate_key_ex(rsa, 2048, bignum, NULL);
PEM_write_RSAPrivateKey(pkey, rsa, NULL, NULL, 0, NULL, NULL);
fclose(pkey);
}else{
FILE *pkey = fopen("privateKey.pem", "rb");
PEM_read_RSAPrivateKey(
pkey,
&rsa,
NULL,
NULL);
fclose(pkey);
}
return rsa;
}
// Sign document
void sign(const char *filepath)
{
FILE *file = fopen(filepath, "r");
// SHA256 digest
unsigned char hash[SHA256_DIGEST_LENGTH];
SHA256_CTX sha256;
SHA256_Init(&sha256);
char *buffer[1024];
int bytesRead = 0;
while ((bytesRead = fread(buffer, 1, 1024, file)))
{
SHA256_Update(&sha256, buffer, bytesRead);
}
fclose(file);
SHA256_Final(hash, &sha256);
// Retrieve keys (create keys if it does not exist)
RSA *rsa = generateKeys();
// Sign
const int size = RSA_size(rsa);
unsigned char *sign = new unsigned char[size];
unsigned int outlen = 0;
RSA_sign(NID_sha256, hash, SHA256_DIGEST_LENGTH, sign, &outlen, rsa);
FILE *signedDoc = fopen("signedDocument.signed","wb");
fputs((const char *) sign,signedDoc);
fclose(signedDoc);
}
// Validate document
// int validate(FILE *file){
// return 0;
// }
int main(int argc, char *argv[])
{
// Input validation
if (argc == 1)
{
cout << "Usage: sign -sv document" << endl
<< "-s sign document" << endl
<< "-v validate document signature" << endl;
return 0;
}
if (argc > 3 || (string(argv[1]) != "-s" && string(argv[1]) != "-v"))
{
cout << "Error: Wrong arguments given." << endl
<< "sign for help" << endl;
return -1;
}
FILE *document = fopen(argv[2], "r");
if (document == NULL)
{
cout << "File open error.." << endl;
return -1;
}
// Sign document
if ((string(argv[1]) == "-s"))
{
sign(argv[2]);
}
// // Validate signature
// if((string(argv[1]) == "-v")){
// validate(document);
// }
}
And these are the flags I use to compile it:
-Wall -Werror -Wextra -pedantic -lcrypto -std=c++11 -g
Thanks in advance.
I have implemented the RSA_verify function and based on the sign my code does, RSA_verify works and it returns that it is valid. I have also added the code to save the public key as well.
My best guess is that my code doesn't represent exactly the command.

C++, Google, ServiceAccount, OAUTH2_JWT -> SHA256withRSA -> "error_description": "Invalid JWT Signature."

Got "Invalid JWT Signature". It's seems like only signing is wrong. But I just don't know what to do. Please help!
Curl works 100%
Base64 seems like works fine. Header and Claim decodes is OK.
It has some info here (REST) if it will help.
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) { std::cout << "BAD! " << std::endl; return false; }
if (EVP_DigestSignUpdate(m_RSASignCtx, Msg, MsgLen) <= 0) { std::cout << "BAD! " << std::endl; return false; }
if (EVP_DigestSignFinal(m_RSASignCtx, NULL, MsgLenEnc) <=0) { std::cout << "BAD! " << std::endl; return false; }
*EncMsg = (unsigned char*)malloc(*MsgLenEnc);
if (EVP_DigestSignFinal(m_RSASignCtx, *EncMsg, MsgLenEnc) <= 0) { std::cout << "BAD! " << std::endl; return false; }
EVP_MD_CTX_free(m_RSASignCtx);
return true;
}
// JWT HEADER
std::string jwtHeader = base64_encode( "{\"alg\":\"RS256\",\"typ\":\"JWT\"}" );
// JWT CLAIM
std::string jwtClaim = base64_encode( ... );
// JWT SIGNATURE
std::string JWS = jwtHeader + "." + jwtClaim;
const char* privateKey = "-----BEGIN PRIVATE KEY-----\n...";
// HASH
SHA256_CTX sha_ctx = { 0 };
unsigned char digest[SHA256_DIGEST_LENGTH];
SHA256_Init(&sha_ctx);
SHA256_Update(&sha_ctx, JWS.c_str(), JWS.size());
SHA256_Final(digest, &sha_ctx);
// SIGN
FILE *file = nullptr;
file = fopen("file.key", "r");
if (!file) { std::cout << "BAD FILE" << std::endl; }
RSA* rsa = PEM_read_RSAPrivateKey(file, NULL, NULL, NULL);
fclose(file);
if(!rsa)
std::cout << "BAD KEY" << std::endl;
// there is no luck with or without sha256 before signing
if (RSASign( rsa, (const unsigned char*) JWS.c_str(), JWS.length(), &sig, (size_t*)&slen))
std::cout << "SIGNED size: " << slen << std::endl;
// TOKEN REQUEST
std::string sign = base64_encode(sig, slen);
std::string requestStr = "grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=" + JWS + "." + sign;
headers = curl_slist_append(headers, "Content-Type: application/x-www-form-urlencoded");
curl_easy_setopt(curl_handle, CURLOPT_URL, "https://oauth2.googleapis.com/token");
curl_easy_setopt( ... CURLOPT_POST, CURLOPT_HTTPHEADER, CURLOPT_POSTFIELDS ... );
UPDATE:
Ok. Now I know that I need SHA256withRSA. But problem is still there.
Thank You Botje! Thank You very much for trying to help me.
But I have no time anymore for learning openssl. So I found a great one:
https://github.com/Thalhammer/jwt-cpp
from here
jwt.io
It doesn't support the 'scope' field for the jwt-claim, so I just added a method into a class into a header: 'set_scope(...);'.
Ten, 10... Nooo, You just can't feel my pain beacuse of openssl_from_devs_with_crooked_hands_with_no_human_docs.
10 strings of a code and it's done!
It has simple sources, so You can find out how to work with ssl from there.
If You want to use OpenSSL lib and don't know how it works - think twice, may be it's the worst choice of Your life. Good luck!
Just for future people looking this up and struggle how to compute a RS256 (SHA256WithRSA)-Hash in OpenSSL, since I spent about 5 hours researching the error. The code from Botje is quite close, but is missing the Finalize method (and the destructors).
#include <OpenSSL/obj_mac.h>
#include <OpenSSL/evp.h>
#include <OpenSSL/pem.h>
#include <OpenSSL/bio.h>
bool SHA256WithRSA(const std::string RSAKey, const std::string Data)
{
struct bio_st* pem_BIO = BIO_new(BIO_s_mem());
if (BIO_write(pem_BIO, RSAKey.data(), RSAKey.size()) != RSAKey.size())
{
BIO_free_all(pem_BIO);
return false;
}
struct evp_pkey_st* signing_key = PEM_read_bio_PrivateKey(pem_BIO, nullptr, nullptr, nullptr);
if(signing_key == nullptr)
{
BIO_free_all(pem_BIO);
EVP_PKEY_free(signing_key);
return false;
}
std::string ResultBuffer(EVP_PKEY_size(signing_key), '\0');
unsigned int len = 0;
struct evp_md_ctx_st * MD_ctx = EVP_MD_CTX_new();
if (!EVP_SignInit(MD_ctx, EVP_get_digestbyname(SN_sha256WithRSAEncryption)) ||
!EVP_SignUpdate(MD_ctx, Data.data(), Data.size()) ||
EVP_SignFinal(MD_ctx, (unsigned char*)ResultBuffer.data(), &len, signing_key) == 0)
{
BIO_free_all(pem_BIO);
EVP_PKEY_free(signing_key);
EVP_MD_CTX_free(MD_ctx);
return false;
}
ResultBuffer.resize(len);
/* Do something with the ResultBuffer */
BIO_free_all(pem_BIO);
EVP_PKEY_free(signing_key);
EVP_MD_CTX_free(MD_ctx);
return true;
}
No need to mark this answer as accepted, yours is by far the easier solution. I'm posting this for future readers to find.
For what it's worth, I was able to reproduce the raw hash in the RS256 test case with the following:
void die() {
for (int err = ERR_get_error(); err != 0; err = ERR_get_error()) {
fprintf(stderr, "%s\n", ERR_error_string(err, nullptr));
}
exit(1);
}
int main() {
/* md is a SHA-256 digest in this example. */
BIO *pem_BIO = BIO_new_mem_buf(RSA_pem, sizeof(RSA_pem));
EVP_PKEY *signing_key = PEM_read_bio_PrivateKey(pem_BIO, nullptr, nullptr, nullptr);
const unsigned char header_payload[] = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXUyJ9.eyJpc3MiOiJhdXRoMCJ9";
EVP_MD_CTX * MD_ctx = EVP_MD_CTX_new();
bool success = true;
success = EVP_DigestSignInit(MD_ctx, nullptr, EVP_get_digestbyname(SN_sha256WithRSAEncryption), nullptr, signing_key);
if (!success) die();
unsigned char sigbuf[512];
size_t siglen = sizeof(sigbuf);
success = EVP_DigestSign(MD_ctx, sigbuf, &siglen, header_payload, sizeof(header_payload)-1);
if (!success) die();
fwrite(sigbuf, 1, siglen, stdout);
}
The end result still has to be base64-encoded (with the url-safe alphabet), though.

Incorrect signature with RSA and SHA256

I have the following string:ET8d1voUkzNcqud7M8W0WQcd3l2Ih1ZtiMxStPeubKg=
I want to apply SHA256 to it and obtain its hash.
Then I want to sign that hash with RSA and a private key.
This is the signature I'm suppose to be obtaining (without line breaks):
Zg8ftxJqRyoreMQtMKAZNrjHHcD/rOSkU29Ty8zV9aItwHBDO0WpzaaPrqnX6/vdayUAndDVvSBoOc9g0WBkFHQHtB/auLlq+ABeBP4jxy
d7ypPxBbJFecfZiBDaGCq4jAxUnhQ2HjT5R23DHOOcf/i50TrWXr2G5k8enqa754TUn1JiDOJJT2JkfnKmgM7EPpCjHV/eCJsOQFXNaxht
h7zHz5hZ4aOfy6EGGveOggzIjKSLeo0pIE8jBc1wy9V8vZkhPTpkeLguCxnwvpMIV1X7zF3m5OoM0PbC5yXgPUYPrz0JNSlvCKR9q5CsFm
rnit5vBfi5el1ZmevP2MgyEA==
This is the signature I get:
AR1X19H5wyb9IEi9WHrhatkR1jtTc7TovX23tdx8yID5CEWz+DF5kBNCXZxttJ8v
ctsbOL0rrQ0b4Gqa2ld00+nfzZJNg1osWwKb+sj6yNLy1XLqxFvfn1wrZ9Y8yOwS
oqJ0tTCpYbbbMo1mSVO4YuD18GbZJEDBUhBYY5D8H0MoHCSWLXsAjThAImyxw4ch
Hr2d1Sli4n6OA+CckGOQ15uLk6JiO6rNzNWfbbSb8a9trJ7bAdVPiKoln1X9tnWF
s3HK6fnfro9jlRQQ/Z0bCpF+FQrWrLouvLk+mjuTeiC+86HeeqzqlReAtpqtcO5/
9lWmrxXV2qZIqfuPYMovcA==
The code I'm using:
#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 <string.h>
#include <cstring>
std::string privateKey = "-----BEGIN RSA PRIVATE KEY-----\n"\
"MIIEogIBAAKCAQEAjdKXiqYHzi++YmEb9X6qvqFWLCz1VEfxom2JhinPSJxxcuZW\n"\
"Bejk2I5yCL5pDnUaG2xpQlMTkV/7S7JfGGvYJumKO4R5zg0QSA7qdxiEhcwf/ekf\n"\
"SvzM2EDnLHDCKAQwEWsnJy78uxZTLzu/65VZ7EgEcWUTvCs/GZJLI9s6XmKY2SMm\n"\
"v9+vfqBqkJNXE0ZB6OfSbyeE325P94iMn+B/yJ4vZwXvXGFqNDJyqG+ww7f77HYu\n"\
"bQPJjLQPedy2qTcgmSAwkUEJVBjYA6mPf/BeZlL1YJHHM7CIBnb3/bzED0n944wo\n"\
"io+4+rnMZdfhcCVpm74DZomlEf9KuJtq5u/JRQIDAQABAoIBAG2AzvWE4L346z02\n"\
"0cmptdhe5hRR2lLrAc1yWh83JQ9hi881vfHuMtRql+3cZ218SV4nRNarIo6612NJ\n"\
"JFfM3SaeZ9cwoIPSXmHk8nBmg9xzEbiRSVIzA09uPZB4t9EB+sNYQvDkPMuPn0b3\n"\
"EWaq+LWRnayYaLZ/hccOx+m1mcnJnIs27+EPnufrUKVniCguburQoU3VEXSFCzBk\n"\
"23rhSu20vUOikLuuU4gcvWfnfUoOwdhb2iBhjgMbsjTTmg3+GQuPtblCSTKjk11F\n"\
"YX2MJHEDFfwVzSurmbqAZC9rjr7PbflC8GMmPfa1LCb7IG5s9AIM4v9Fea0SyZP+\n"\
"/pM9mzkCgYEAyjLPU7ieqly9+mgeb2fmWh7pYgO49KuFIqqHnP+LaXXYK/TvCJ8X\n"\
"zJ3PxBgwVMOT94nXSDNjzNLzp0hl8OWWBH0tN0fiq1OEyySM3Mlji6o8KpGcU1k4\n"\
"jFkXMK1rVQcW8ckLmzMrQF3SphQi4UiEpLX1Zba4YQ4fNHK8NHHHaEMCgYEAs48m\n"\
"Oe4iEZcVDnag+Mp0Zjgu4mYJeeeGtVUZFCJOeyLDsQVmnt5mJIgGwrxg4o3qljut\n"\
"aAUXzf8aYZ0fURAsLcwnQg03THFKeIt94Rw/72n2UWT+AZTU3GQtuwf+osZHUfS3\n"\
"XTLaQE+A1JBC4XLJ99j/95sxt6xZy5YyfhfY09cCgYAbqyhDxJexqE823NiNViJn\n"\
"YqN9DhVZJb9qJvu3uCBTphSWr0WmYF7ZWR79LnIupzSwQuR6tM2LUbKVyYpplIEa\n"\
"zCZL0kJqP1uEkNPVwpkkm37wNEy3+xWJ3wcVWiW91OKG44P7EN1ySWRx5X+AZHQC\n"\
"NgQGjyJb5ZrPioPGiWtIEQKBgE/0B/N3o9ftTET6cccWbootDkNlaAbOH1+TGu2q\n"\
"MQQHgNfMLdvD7/uITmpb81AuHSz0Ocy9p9HkK90XV6CC8QkbhMeWlu8E60It6slY\n"\
"COgUaMfpjmkp2nagbPSBJNNaMtu9egCX6jMEs7ry2bUFpgUkrSWWB1df+UP8B1O6\n"\
"TqRVAoGAVQoCUPVm6C6h6V5dgPvsJMxJ8EjOCgwkXNucAHWcpBV3/LlxLiCGRuEL\n"\
"B+epYxqwKLpSQBhldasKmmKB0M6MFTwxXwxCmCi80+DBdP5A7GIK52ZGth63i22t\n"\
"FI8MeDIzA5HqAI24P7ltozoEYAB7GIdJQXq9oT/DRagTwQUzQ8E=\n"\
"-----END RSA PRIVATE KEY-----\n";
/*std::string privateKey ="-----BEGIN PRIVATE KEY-----\n"\
"MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCN0peKpgfOL75i\n"\
"YRv1fqq+oVYsLPVUR/GibYmGKc9InHFy5lYF6OTYjnIIvmkOdRobbGlCUxORX/tL\n"\
"sl8Ya9gm6Yo7hHnODRBIDup3GISFzB/96R9K/MzYQOcscMIoBDARaycnLvy7FlMv\n"\
"O7/rlVnsSARxZRO8Kz8Zkksj2zpeYpjZIya/369+oGqQk1cTRkHo59JvJ4Tfbk/3\n"\
"iIyf4H/Ini9nBe9cYWo0MnKob7DDt/vsdi5tA8mMtA953LapNyCZIDCRQQlUGNgD\n"\
"qY9/8F5mUvVgkcczsIgGdvf9vMQPSf3jjCiKj7j6ucxl1+FwJWmbvgNmiaUR/0q4\n"\
"m2rm78lFAgMBAAECggEAbYDO9YTgvfjrPTbRyam12F7mFFHaUusBzXJaHzclD2GL\n"\
"zzW98e4y1GqX7dxnbXxJXidE1qsijrrXY0kkV8zdJp5n1zCgg9JeYeTycGaD3HMR\n"\
"uJFJUjMDT249kHi30QH6w1hC8OQ8y4+fRvcRZqr4tZGdrJhotn+Fxw7H6bWZycmc\n"\
"izbv4Q+e5+tQpWeIKC5u6tChTdURdIULMGTbeuFK7bS9Q6KQu65TiBy9Z+d9Sg7B\n"\
"2FvaIGGOAxuyNNOaDf4ZC4+1uUJJMqOTXUVhfYwkcQMV/BXNK6uZuoBkL2uOvs9t\n"\
"+ULwYyY99rUsJvsgbmz0Agzi/0V5rRLJk/7+kz2bOQKBgQDKMs9TuJ6qXL36aB5v\n"\
"Z+ZaHuliA7j0q4Uiqoec/4tpddgr9O8InxfMnc/EGDBUw5P3iddIM2PM0vOnSGXw\n"\
"5ZYEfS03R+KrU4TLJIzcyWOLqjwqkZxTWTiMWRcwrWtVBxbxyQubMytAXdKmFCLh\n"\
"SISktfVltrhhDh80crw0ccdoQwKBgQCzjyY57iIRlxUOdqD4ynRmOC7iZgl554a1\n"\
"VRkUIk57IsOxBWae3mYkiAbCvGDijeqWO61oBRfN/xphnR9RECwtzCdCDTdMcUp4\n"\
"i33hHD/vafZRZP4BlNTcZC27B/6ixkdR9LddMtpAT4DUkELhcsn32P/3mzG3rFnL\n"\
"ljJ+F9jT1wKBgBurKEPEl7GoTzbc2I1WImdio30OFVklv2om+7e4IFOmFJavRaZg\n"\
"XtlZHv0uci6nNLBC5Hq0zYtRspXJimmUgRrMJkvSQmo/W4SQ09XCmSSbfvA0TLf7\n"\
"FYnfBxVaJb3U4objg/sQ3XJJZHHlf4BkdAI2BAaPIlvlms+Kg8aJa0gRAoGAT/QH\n"\
"83ej1+1MRPpxxxZvKi0OQ2VoBs4fX5Ma7aoxBAeA18wt28Pv+4hOalvzUC4dLPQ5\n"\
"zL2n0eQr3RdXoILxCRuEx5aW7wTrQi3qyVgI6BRox+mOaSnadqBs9IEk01oy2716\n"\
"AJfqMwSzuvLZtQWmBSStJZYHV1/5Q/wHU7pOpFUCgYBVCgJQ9WboLqHpXl2A++wk\n"\
"zEnwSM4KDCRc25wAdZykFXf8uXEuIIZG4QsH56ljGrAoulJAGGV1qwqaYoHQzowV\n"\
"PDFfDEKYKLzT4MF0/kDsYgrnZka2HreLba0Ujwx4MjMDkeoAjbg/uW2jOgRgAHsY\n"\
"h0lBer2hP8NFqBPBBTNDwQ==\n"\
"-----END PRIVATE KEY-----\n";
*/
std::string publicKey ="-----BEGIN PUBLIC KEY-----\n"\
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjdKXiqYHzi++YmEb9X6q\n"\
"vqFWLCz1VEfxom2JhinPSJxxcuZWBejk2I5yCL5pDnUaG2xpQlMTkV/7S7JfGGvY\n"\
"JumKO4R5zg0QSA7qdxiEhcwf/ekfSvzM2EDnLHDCKAQwEWsnJy78uxZTLzu/65VZ\n"\
"7EgEcWUTvCs/GZJLI9s6XmKY2SMmv9+vfqBqkJNXE0ZB6OfSbyeE325P94iMn+B/\n"\
"yJ4vZwXvXGFqNDJyqG+ww7f77HYubQPJjLQPedy2qTcgmSAwkUEJVBjYA6mPf/Be\n"\
"ZlL1YJHHM7CIBnb3/bzED0n944woio+4+rnMZdfhcCVpm74DZomlEf9KuJtq5u/J\n"\
"RQIDAQAB\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);
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);
}
unsigned char * strToHex(std::string plainText){
unsigned char * hexArray;
hexArray = (unsigned char*)malloc(plainText.length());
for(int i = 0; i < plainText.size();i++){
hexArray[i] = std::strtol(&plainText[i],NULL,64);
}
return hexArray;
}
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.data(), 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 = "ET8d1voUkzNcqud7M8W0WQcd3l2Ih1ZtiMxStPeubKg=";
char* signature = signMessage(privateKey, plainText);
bool authentic = verifySignature(publicKey, "ET8d1voUkzNcqud7M8W0WQcd3l2Ih1ZtiMxStPeubKg=", signature);
if ( authentic ) {
std::cout << "Authentic" << std::endl;
std::cout << signature << std::endl;
} else {
std::cout << "Not Authentic" << std::endl;
}
}
What I'm actually trying to do is to 'translate' a C# code used by a third-party, I REQUIRE to do the same operations, I tried looking for the docs of the libreries used here but I couldn't find much, the code I'm trying to replicate is the following:
using System;
using System.Security.Cryptography;
using System.Text;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
namespace KeyPairLoad
{
class Program
{
static void Main(string[] args)
{
string cadena = ComputeSha256Hash("ET8d1voUkzNcqud7M8W0WQcd3l2Ih1ZtiMxStPeubKg=");
string pathPrivateKey = #"C:\Users\xxx\Downloads\Fieles de pruebas\FOO1\some.key";
string password = "12345678a";
byte[] privBytes = System.IO.File.ReadAllBytes(pathPrivateKey);
AsymmetricKeyParameter llavePrivada = PrivateKeyFactory.DecryptKey(password.ToCharArray(), privBytes);
var rsaPriv = DotNetUtilities.ToRSA(llavePrivada as RsaPrivateCrtKeyParameters);
var csp = new CspParameters();
csp.KeyContainerName = "KeyContainer";
var rsaPrivate = new RSACryptoServiceProvider(csp);
string fin = Convert.ToBase64String(rsaPrivate.SignHash(Convert.FromBase64String(cadena), "SHA256"));
}
static string ComputeSha256Hash(string rawData)
{
using (SHA256 sha256Hash = SHA256.Create())
{
byte[] bytes = sha256Hash.ComputeHash(Encoding.UTF8.GetBytes(rawData));
return Convert.ToBase64String(bytes);
}
}
}
}
This third-party gave me the signature I'm supposed to be obtaining. I've tried many things but nothing works, I get different signatures but not the one I'm looking for.
Am I missing something in my translation? Am I doing something wrong?
Thanks for your patience and time. Let me know if u require more info.
Regards!
This code gets the desired signature:
Zg8ftxJqRyoreMQtMKAZNrjHHcD/rOSkU29Ty8zV9aItwHBDO0WpzaaPrqnX6/vdayUAndDVvSBoOc9g0WBkFHQHtB/auLlq+ABeBP4jxy
d7ypPxBbJFecfZiBDaGCq4jAxUnhQ2HjT5R23DHOOcf/i50TrWXr2G5k8enqa754TUn1JiDOJJT2JkfnKmgM7EPpCjHV/eCJsOQFXNaxht
h7zHz5hZ4aOfy6EGGveOggzIjKSLeo0pIE8jBc1wy9V8vZkhPTpkeLguCxnwvpMIV1X7zF3m5OoM0PbC5yXgPUYPrz0JNSlvCKR9q5CsFm
rnit5vBfi5el1ZmevP2MgyEA==
that I originally stated. It writes it in a file called result.dat which I have to encode in base 64 resulting int he above signature.
#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 <string>
#include <cstring>
#include <memory>
#include <vector>
#include <fstream>
#include "base64.h"
std::string privateKey = "-----BEGIN RSA PRIVATE KEY-----\n"
"MIIEogIBAAKCAQEAjdKXiqYHzi++YmEb9X6qvqFWLCz1VEfxom2JhinPSJxxcuZW\n"
"Bejk2I5yCL5pDnUaG2xpQlMTkV/7S7JfGGvYJumKO4R5zg0QSA7qdxiEhcwf/ekf\n"
"SvzM2EDnLHDCKAQwEWsnJy78uxZTLzu/65VZ7EgEcWUTvCs/GZJLI9s6XmKY2SMm\n"
"v9+vfqBqkJNXE0ZB6OfSbyeE325P94iMn+B/yJ4vZwXvXGFqNDJyqG+ww7f77HYu\n"
"bQPJjLQPedy2qTcgmSAwkUEJVBjYA6mPf/BeZlL1YJHHM7CIBnb3/bzED0n944wo\n"
"io+4+rnMZdfhcCVpm74DZomlEf9KuJtq5u/JRQIDAQABAoIBAG2AzvWE4L346z02\n"
"0cmptdhe5hRR2lLrAc1yWh83JQ9hi881vfHuMtRql+3cZ218SV4nRNarIo6612NJ\n"
"JFfM3SaeZ9cwoIPSXmHk8nBmg9xzEbiRSVIzA09uPZB4t9EB+sNYQvDkPMuPn0b3\n"
"EWaq+LWRnayYaLZ/hccOx+m1mcnJnIs27+EPnufrUKVniCguburQoU3VEXSFCzBk\n"
"23rhSu20vUOikLuuU4gcvWfnfUoOwdhb2iBhjgMbsjTTmg3+GQuPtblCSTKjk11F\n"
"YX2MJHEDFfwVzSurmbqAZC9rjr7PbflC8GMmPfa1LCb7IG5s9AIM4v9Fea0SyZP+\n"
"/pM9mzkCgYEAyjLPU7ieqly9+mgeb2fmWh7pYgO49KuFIqqHnP+LaXXYK/TvCJ8X\n"
"zJ3PxBgwVMOT94nXSDNjzNLzp0hl8OWWBH0tN0fiq1OEyySM3Mlji6o8KpGcU1k4\n"
"jFkXMK1rVQcW8ckLmzMrQF3SphQi4UiEpLX1Zba4YQ4fNHK8NHHHaEMCgYEAs48m\n"
"Oe4iEZcVDnag+Mp0Zjgu4mYJeeeGtVUZFCJOeyLDsQVmnt5mJIgGwrxg4o3qljut\n"
"aAUXzf8aYZ0fURAsLcwnQg03THFKeIt94Rw/72n2UWT+AZTU3GQtuwf+osZHUfS3\n"
"XTLaQE+A1JBC4XLJ99j/95sxt6xZy5YyfhfY09cCgYAbqyhDxJexqE823NiNViJn\n"
"YqN9DhVZJb9qJvu3uCBTphSWr0WmYF7ZWR79LnIupzSwQuR6tM2LUbKVyYpplIEa\n"
"zCZL0kJqP1uEkNPVwpkkm37wNEy3+xWJ3wcVWiW91OKG44P7EN1ySWRx5X+AZHQC\n"
"NgQGjyJb5ZrPioPGiWtIEQKBgE/0B/N3o9ftTET6cccWbootDkNlaAbOH1+TGu2q\n"
"MQQHgNfMLdvD7/uITmpb81AuHSz0Ocy9p9HkK90XV6CC8QkbhMeWlu8E60It6slY\n"
"COgUaMfpjmkp2nagbPSBJNNaMtu9egCX6jMEs7ry2bUFpgUkrSWWB1df+UP8B1O6\n"
"TqRVAoGAVQoCUPVm6C6h6V5dgPvsJMxJ8EjOCgwkXNucAHWcpBV3/LlxLiCGRuEL\n"
"B+epYxqwKLpSQBhldasKmmKB0M6MFTwxXwxCmCi80+DBdP5A7GIK52ZGth63i22t\n"
"FI8MeDIzA5HqAI24P7ltozoEYAB7GIdJQXq9oT/DRagTwQUzQ8E=\n"
"-----END RSA PRIVATE KEY-----\n";
// std::string privateKey ="-----BEGIN PRIVATE KEY-----\n"\
// "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCN0peKpgfOL75i\n"\
// "YRv1fqq+oVYsLPVUR/GibYmGKc9InHFy5lYF6OTYjnIIvmkOdRobbGlCUxORX/tL\n"\
// "sl8Ya9gm6Yo7hHnODRBIDup3GISFzB/96R9K/MzYQOcscMIoBDARaycnLvy7FlMv\n"\
// "O7/rlVnsSARxZRO8Kz8Zkksj2zpeYpjZIya/369+oGqQk1cTRkHo59JvJ4Tfbk/3\n"\
// "iIyf4H/Ini9nBe9cYWo0MnKob7DDt/vsdi5tA8mMtA953LapNyCZIDCRQQlUGNgD\n"\
// "qY9/8F5mUvVgkcczsIgGdvf9vMQPSf3jjCiKj7j6ucxl1+FwJWmbvgNmiaUR/0q4\n"\
// "m2rm78lFAgMBAAECggEAbYDO9YTgvfjrPTbRyam12F7mFFHaUusBzXJaHzclD2GL\n"\
// "zzW98e4y1GqX7dxnbXxJXidE1qsijrrXY0kkV8zdJp5n1zCgg9JeYeTycGaD3HMR\n"\
// "uJFJUjMDT249kHi30QH6w1hC8OQ8y4+fRvcRZqr4tZGdrJhotn+Fxw7H6bWZycmc\n"\
// "izbv4Q+e5+tQpWeIKC5u6tChTdURdIULMGTbeuFK7bS9Q6KQu65TiBy9Z+d9Sg7B\n"\
// "2FvaIGGOAxuyNNOaDf4ZC4+1uUJJMqOTXUVhfYwkcQMV/BXNK6uZuoBkL2uOvs9t\n"\
// "+ULwYyY99rUsJvsgbmz0Agzi/0V5rRLJk/7+kz2bOQKBgQDKMs9TuJ6qXL36aB5v\n"\
// "Z+ZaHuliA7j0q4Uiqoec/4tpddgr9O8InxfMnc/EGDBUw5P3iddIM2PM0vOnSGXw\n"\
// "5ZYEfS03R+KrU4TLJIzcyWOLqjwqkZxTWTiMWRcwrWtVBxbxyQubMytAXdKmFCLh\n"\
// "SISktfVltrhhDh80crw0ccdoQwKBgQCzjyY57iIRlxUOdqD4ynRmOC7iZgl554a1\n"\
// "VRkUIk57IsOxBWae3mYkiAbCvGDijeqWO61oBRfN/xphnR9RECwtzCdCDTdMcUp4\n"\
// "i33hHD/vafZRZP4BlNTcZC27B/6ixkdR9LddMtpAT4DUkELhcsn32P/3mzG3rFnL\n"\
// "ljJ+F9jT1wKBgBurKEPEl7GoTzbc2I1WImdio30OFVklv2om+7e4IFOmFJavRaZg\n"\
// "XtlZHv0uci6nNLBC5Hq0zYtRspXJimmUgRrMJkvSQmo/W4SQ09XCmSSbfvA0TLf7\n"\
// "FYnfBxVaJb3U4objg/sQ3XJJZHHlf4BkdAI2BAaPIlvlms+Kg8aJa0gRAoGAT/QH\n"\
// "83ej1+1MRPpxxxZvKi0OQ2VoBs4fX5Ma7aoxBAeA18wt28Pv+4hOalvzUC4dLPQ5\n"\
// "zL2n0eQr3RdXoILxCRuEx5aW7wTrQi3qyVgI6BRox+mOaSnadqBs9IEk01oy2716\n"\
// "AJfqMwSzuvLZtQWmBSStJZYHV1/5Q/wHU7pOpFUCgYBVCgJQ9WboLqHpXl2A++wk\n"\
// "zEnwSM4KDCRc25wAdZykFXf8uXEuIIZG4QsH56ljGrAoulJAGGV1qwqaYoHQzowV\n"\
// "PDFfDEKYKLzT4MF0/kDsYgrnZka2HreLba0Ujwx4MjMDkeoAjbg/uW2jOgRgAHsY\n"\
// "h0lBer2hP8NFqBPBBTNDwQ==\n"\
// "-----END PRIVATE KEY-----\n";
std::string publicKey ="-----BEGIN PUBLIC KEY-----\n"\
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjdKXiqYHzi++YmEb9X6q\n"\
"vqFWLCz1VEfxom2JhinPSJxxcuZWBejk2I5yCL5pDnUaG2xpQlMTkV/7S7JfGGvY\n"\
"JumKO4R5zg0QSA7qdxiEhcwf/ekfSvzM2EDnLHDCKAQwEWsnJy78uxZTLzu/65VZ\n"\
"7EgEcWUTvCs/GZJLI9s6XmKY2SMmv9+vfqBqkJNXE0ZB6OfSbyeE325P94iMn+B/\n"\
"yJ4vZwXvXGFqNDJyqG+ww7f77HYubQPJjLQPedy2qTcgmSAwkUEJVBjYA6mPf/Be\n"\
"ZlL1YJHHM7CIBnb3/bzED0n944woio+4+rnMZdfhcCVpm74DZomlEf9KuJtq5u/J\n"\
"RQIDAQAB\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;
}
void sellar(std::string hashclear, std::string privateKey_string){
EVP_PKEY_CTX *ctx;
/* md is a SHA-256 digest in this example. */
unsigned char *sig;
size_t siglen;
unsigned char clear_message[hashclear.length()];
strcpy((char*) clear_message,hashclear.c_str());
size_t mdlen;
// //1. Decodificar hashclear. || base64 -d data.dat > b64.dat -> Me debe dar una cadena de 32 bytes.
unsigned char * md = base64_decode(clear_message, strlen((char*) clear_message), &mdlen);
std::cout << "MD is " << mdlen << " bytes long.\n";
// //2. Cargar llave privada.
RSA* privateRSA = createPrivateRSA(privateKey);
EVP_PKEY* signing_key = EVP_PKEY_new();
EVP_PKEY_assign_RSA(signing_key, privateRSA);
ctx = EVP_PKEY_CTX_new(signing_key, NULL /* no engine */);
if(!ctx) {
std::cout << "Error CTX_new" << std::endl;
return;
}
if (EVP_PKEY_sign_init(ctx) <= 0){
std::cout << "Error sign_init\n";
return;
}
if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) <= 0){
std::cout << "Error set_rsa_padding\n";
return;
}
if (EVP_PKEY_CTX_set_signature_md(ctx, EVP_sha256()) <= 0){
std::cout << "Error set_signature_md\n";
return;
}
/* Determine buffer length */
if (EVP_PKEY_sign(ctx, NULL, &siglen, md, mdlen) <= 0){
std::cout << "Error PKEY_sign\n";
return;
}
sig = (unsigned char*)OPENSSL_malloc(siglen);
if (!sig){
std::cout << "Error malloc";
return;
}
if (EVP_PKEY_sign(ctx, sig, &siglen, md, mdlen) <= 0){
std::cout << "Error sign";
return;
}
std::cout << siglen << " bytes written in buffer sig\n";
/* Signature is siglen bytes written to buffer sig */
size_t cadena_sellada_len;
unsigned char * cadena_sellada = base64_encode(sig, siglen, &cadena_sellada_len);
std::ofstream myfile ("result_final.dat");
if (myfile.is_open())
{
for(int count = 0; count < cadena_sellada_len; count ++){
myfile << cadena_sellada[count] ;
}
myfile.close();
}
else std::cout << "Unable to open file";
}
int main() {
std::string plainText = "ET8d1voUkzNcqud7M8W0WQcd3l2Ih1ZtiMxStPeubKg=\n";
// unsigned char src[plainText.length()];
// strcpy((char*) src,plainText.c_str());
// std::cout << "Src has " << strlen((char*)src) << " entries.\n";
// size_t out_len;
// unsigned char * bytes = base64_decode(src, strlen((char*)src), &out_len);
// std::cout << "bytes has " << out_len << " entries.\n";
// std::cout << bytes << std::endl;
// size_t re_out_len;
// unsigned char * re_encode = base64_encode(bytes, out_len,&re_out_len);
// std::cout << re_encode << "\n";
// std::ofstream myfile ("b64.dat");
// if (myfile.is_open())
// {
// for(int count = 0; count < out_len; count ++){
// myfile << bytes[count] ;
// }
// myfile.close();
// }
// else std::cout << "Unable to open file";
sellar(plainText, privateKey);
return 0;
//strcpy()
//std::cout << "Length : " << b64dat.length() << std::endl;
//char* signature = signMessage(privateKey, plainText);
//std::cout << signature << std::endl;
// bool authentic = verifySignature(publicKey, "ET8d1voUkzNcqud7M8W0WQcd3l2Ih1ZtiMxStPeubKg=", signature);
// if ( authentic ) {
// std::cout << "Authentic" << std::endl;
// } else {
// std::cout << "Not Authentic" << std::endl;
// }
}
Thank you all for your time and patience.
I'm not going to read all the code to try and figure out what it is you're trying to do and thus where your mistake(s) are. Instead, I'll just examine what you've provided and you can go from there.
Clearly, both the signatures you've provided are valid signatures. This can be seen by manually going through the verification steps, stopping just after modular exponentiation using the encrypt exponent. The result is as expected for pkcs1 EMSA-PKCS1-v1_5 encoding. And just as clearly, we can see that different SHA-256 hashes are involved. In other words, the signatures are different because the data they are signing is different.
For the signature that starts with AR1X19... the data being signed is the UTF-8 encoding of the string ET8d1voUkzNcqud7M8W0WQcd3l2Ih1ZtiMxStPeubKg=. Note that this string a pretty obviously the base64-encoding of random looking 32 bytes. In this context one might guess that those 32-bytes are actually the sha256 hash of some other unknown quantity. And sure, enough the signature that starts with Zg8ftxJqR... is over data whose sha256 hash is the base64-decoding of ET8d....
So it looks like the first signature is correct, and the second one is the result of base64-encoding the sha256 hash during in the middle of the signing operation.

How to perform asymmetric encryption with Botan

I'm using Botan to generate a hash, perform encryption with AES256, and now I want to perform asymmetric encryption with that. Reading this page: http://botan.randombit.net/pubkey.html.
I created a code to generate the public and private keys for RSA encryption, but I do not understand how to encrypt and decrypt data. Can someone help me?
I'm using Botan 1.8.8 2009-11-03.
void generatekey()
{
LibraryInitializer init;
std::ostringstream pub;
std::ostringstream priv;
int bits = 1024;
AutoSeeded_RNG rng;
RSA_PrivateKey key(rng, bits);
pub << X509::PEM_encode(key);
priv << PKCS8::PEM_encode(key);
qDebug() << QString(pub.str().c_str());
qDebug() << QString(priv.str().c_str());
}
After reading some tutorials i wrote this code to asymmetric encryption.
#include <QDebug>
#include <botan/botan.h>
#include <botan/rsa.h>
#include <botan/look_pk.h>
using namespace Botan;
void encryptdata()
{
try
{
QString text = "abc";
LibraryInitializer init;
AutoSeeded_RNG rng;
RSA_PrivateKey key(rng, 1024);
std::string pub = X509::PEM_encode(key);
std::string priv = PKCS8::PEM_encode(key);
DataSource_Memory key_pub(pub);
DataSource_Memory key_priv(priv);
X509_PublicKey *pub_rsa = X509::load_key(key_pub);
PKCS8_PrivateKey *priv_rsa = PKCS8::load_key(key_priv, rng);
PK_Encrypting_Key *enckey = dynamic_cast<PK_Encrypting_Key*>(pub_rsa);
PK_Decrypting_Key *deckey = dynamic_cast<PK_Decrypting_Key*>(priv_rsa);
PK_Encryptor *enc = get_pk_encryptor(*enckey, "EME1(SHA-256)");
PK_Decryptor *dec = get_pk_decryptor(*deckey, "EME1(SHA-256)");
QByteArray array = text.toLatin1();
byte msgtoencrypt[array.count()];
for (int i = 0; i < array.count(); i++)
{
msgtoencrypt[i] = array[i];
}
SecureVector<byte> ciphertext = enc->encrypt(msgtoencrypt, sizeof(msgtoencrypt), rng);
SecureVector<byte> plaintext = dec->decrypt(ciphertext, ciphertext.size());
QByteArray encrypted;
for (uint i = 0; i < ciphertext.size(); i++)
{
encrypted[i] = ciphertext[i];
}
QByteArray result;
for (uint i = 0; i < plaintext.size(); i++)
{
result[i] = plaintext[i];
}
if (array == result)
{
qDebug() << "Ok";
}
else
{
qDebug() << "Error";
}
qDebug() << QString(encrypted);
qDebug() << QString(array);
qDebug() << QString(result);
}
catch(std::exception &e)
{
qDebug() << e.what();
}
}