I've downloaded mails with Poco/Net/POP3ClientSession, I wanted to convert e-mail subject into human readable, so I tried to use neagoegab's solution from here:
https://stackoverflow.com/a/8104496/1350091
unfortunately it doesn't work:
#include <Poco/Net/POP3ClientSession.h>
#include <Poco/Net/MailMessage.h>
#include <iostream>
#include <string>
using namespace std;
using namespace Poco::Net;
#include <iconv.h>
const size_t BUF_SIZE=1024;
class IConv {
iconv_t ic_;
public:
IConv(const char* to, const char* from)
: ic_(iconv_open(to,from)) { }
~IConv() { iconv_close(ic_); }
bool convert(char* input, char* output, size_t& out_size) {
size_t inbufsize = strlen(input)+1;
return iconv(ic_, &input, &inbufsize, &output, &out_size);
}
};
int main()
{
POP3ClientSession session("poczta.o2.pl");
session.login("my mail", "my password");
POP3ClientSession::MessageInfoVec messages;
session.listMessages(messages);
cout << "id: " << messages[0].id << " size: " << messages[0].size << endl;
MailMessage message;
session.retrieveMessage(messages[0].id, message);
const string subject = message.getSubject();
cout << "Original subject: " << subject << endl;
IConv iconv_("UTF8","ISO-8859-2");
char from[BUF_SIZE];// "=?ISO-8859-2?Q?Re: M=F3j sen o JP II?=";
subject.copy(from, sizeof(from));
char to[BUF_SIZE] = "bye";
size_t outsize = BUF_SIZE;//you will need it
iconv_.convert(from, to, outsize);
cout << "converted: " << to << endl;
}
The output is:
id: 1 size: 2792
Original subject: =?ISO-8859-2?Q?Re: M=F3j sen o JP II?=
converted: =?ISO-8859-2?Q?Re: M=F3j sen o JP II?=
The interesting thing is that when I try to convert the subject with POCO it fails:
cout << "Encoded with POCO: " << MailMessage::encodeWord("Re: Mój sen o JP II", "ISO-8859-2") << endl; // output: Encoded with POCO: =?ISO-8859-2?q?Re=3A_M=C3=B3j_sen_o_JP_II?=
But the subject I want to receive is:
"Re: Mój sen o JP II"
The only succesfull way I found to convert the subject is:
https://docs.python.org/2/library/email.header.html#email.header.decode_header
So my question is -how to convert e-mail's subject in C++ into some format like UTF-8?
The relevant RFC to your situation is RFC 2047. That RFC specifies how non-ASCII data should be encoded in mail messages. The basic gist is that all bytes besides printable ASCII characters are escaped as an '=' character followed by two hexadecimal digits. Since "ó" is represented by the byte 0xF3 in ISO-8859-2, and 0xF3 is not a printable ASCII character, it is encoded as "=F3". You'll need to decode all of the encoded characters in your message.
I found out how to solve the problem (I'm not sure that it is 100% correct solution), but it looks like it is enough to use:
Poco::UTF8Encoding::convert to convert from characterCode to utf8:
#include <Poco/Net/POP3ClientSession.h>
#include <Poco/Net/MessageHeader.h>
#include <Poco/Net/MailMessage.h>
#include <Poco/UTF8Encoding.h>
#include <iostream>
#include <string>
using namespace std;
using namespace Poco::Net;
class EncoderLatin2
{
public:
EncoderLatin2(const string& encodedSubject)
{
/// encoded-word = "=?" charset "?" encoding "?" encoded-text "?="
int charsetBeginPosition = strlen("=?");
int charsetEndPosition = encodedSubject.find("?", charsetBeginPosition);
charset = encodedSubject.substr(charsetBeginPosition, charsetEndPosition-charsetBeginPosition);
int encodingPosition = charsetEndPosition + strlen("?");
encoding = encodedSubject[encodingPosition];
if ("ISO-8859-2" != charset)
throw std::invalid_argument("Invalid encoding!");
const int lenghtOfEncodedText = encodedSubject.length() - encodingPosition-strlen("?=")-2;
extractedEncodedSubjectToConvert = encodedSubject.substr(encodingPosition+2, lenghtOfEncodedText);
}
string convert()
{
size_t positionOfAssignment = -1;
while (true)
{
positionOfAssignment = extractedEncodedSubjectToConvert.find('=', positionOfAssignment+1);
if (string::npos != positionOfAssignment)
{
const string& charHexCode = extractedEncodedSubjectToConvert.substr(positionOfAssignment + 1, 2);
replaceAllSubstringsWithUnicode(extractedEncodedSubjectToConvert, charHexCode);
}
else
break;
}
return extractedEncodedSubjectToConvert;
}
void replaceAllSubstringsWithUnicode(string& s, const string& charHexCode)
{
const int charCode = stoi(charHexCode, nullptr, 16);
char buffer[10] = {};
encodingConverter.convert(charCode, (unsigned char*)buffer, sizeof(buffer));
replaceAll(s, '=' + charHexCode, buffer);
}
void replaceAll(string& s, const string& replaceFrom, const string& replaceTo)
{
size_t needlePosition = -1;
while (true)
{
needlePosition = s.find(replaceFrom, needlePosition + 1);
if (string::npos == needlePosition)
break;
s.replace(needlePosition, replaceFrom.length(), replaceTo);
}
}
private:
string charset;
char encoding;
Poco::UTF8Encoding encodingConverter;
string extractedEncodedSubjectToConvert;
};
int main()
{
POP3ClientSession session("poczta.o2.pl");
session.login("my mail", "my password");
POP3ClientSession::MessageInfoVec messages;
session.listMessages(messages);
MessageHeader header;
MailMessage message;
auto currentMessage = messages[0];
session.retrieveHeader(currentMessage.id, header);
session.retrieveMessage(currentMessage.id, message);
const string subject = message.getSubject();
EncoderLatin2 encoder(subject);
cout << "Original subject: " << subject << endl;
cout << "Encoded: " << encoder.convert() << endl;
}
I found another solution, better than before.
Some e-mails subjects has different encodings, I noticed:
Latin2, encoded like: =?ISO-8859-2?Q?...?=
UTF-8 Base64 like:
=?utf-8?B?Wm9iYWN6Y2llIGNvIGRsYSBXYXMgcHJ6eWdvdG93YWxpxZtteSAvIHN0eWN6ZcWEIHcgTGFzZXJwYXJrdQ==?=
UTF-8 quoted printable like:
=?utf-8?Q?...?=
No encoding (if only ASCII characters) like: ...
So with POCO (Base64Decoder, Latin2Encoding, UTF8Encoding, QuotedPrintableDecoder) I managed to convert all the cases:
#include <iostream>
#include <string>
#include <sstream>
#include <Poco/Net/POP3ClientSession.h>
#include <Poco/Net/MessageHeader.h>
#include <Poco/Net/MailMessage.h>
#include <Poco/Base64Decoder.h>
#include <Poco/Latin2Encoding.h>
#include <Poco/UTF8Encoding.h>
#include <Poco/Net/QuotedPrintableDecoder.h>
using namespace std;
class Encoder
{
public:
Encoder(const string& encodedText)
{
isStringEncoded = isEncoded(encodedText);
if (!isStringEncoded)
{
extractedEncodedSubjectToConvert = encodedText;
return;
}
splitEncodedText(encodedText);
}
string convert()
{
if (isStringEncoded)
{
if (Poco::Latin2Encoding().isA(charset))
return decodeFromLatin2();
if (Poco::UTF8Encoding().isA(charset))
return decodeFromUtf8();
}
return extractedEncodedSubjectToConvert;
}
private:
void splitEncodedText(const string& encodedText)
{
/// encoded-word = "=?" charset "?" encoding "?" encoded-text "?="
const int charsetBeginPosition = strlen(sequenceBeginEncodedText);
const int charsetEndPosition = encodedText.find("?", charsetBeginPosition);
charset = encodedText.substr(charsetBeginPosition, charsetEndPosition-charsetBeginPosition);
const int encodingPosition = charsetEndPosition + strlen("?");
encoding = encodedText[encodingPosition];
const int lenghtOfEncodedText = encodedText.length() - encodingPosition-strlen(sequenceBeginEncodedText)-strlen(sequenceEndEncodedText);
extractedEncodedSubjectToConvert = encodedText.substr(encodingPosition+2, lenghtOfEncodedText);
}
bool isEncoded(const string& encodedSubject)
{
if (encodedSubject.size() < 4)
return false;
if (0 != encodedSubject.find(sequenceBeginEncodedText))
return false;
const unsigned positionOfLastTwoCharacters = encodedSubject.size() - strlen(sequenceEndEncodedText);
return positionOfLastTwoCharacters == encodedSubject.rfind(sequenceEndEncodedText);
}
string decodeFromLatin2()
{
size_t positionOfAssignment = -1;
while (true)
{
positionOfAssignment = extractedEncodedSubjectToConvert.find('=', positionOfAssignment+1);
if (string::npos != positionOfAssignment)
{
const string& charHexCode = extractedEncodedSubjectToConvert.substr(positionOfAssignment + 1, 2);
replaceAllSubstringsWithUnicode(extractedEncodedSubjectToConvert, charHexCode);
}
else
break;
}
return extractedEncodedSubjectToConvert;
}
void replaceAllSubstringsWithUnicode(string& s, const string& charHexCode)
{
static Poco::UTF8Encoding encodingConverter;
const int charCode = stoi(charHexCode, nullptr, 16);
char buffer[10] = {};
encodingConverter.convert(charCode, (unsigned char*)buffer, sizeof(buffer));
replaceAll(s, '=' + charHexCode, buffer);
}
void replaceAll(string& s, const string& replaceFrom, const string& replaceTo)
{
size_t needlePosition = -1;
while (true)
{
needlePosition = s.find(replaceFrom, needlePosition + 1);
if (string::npos == needlePosition)
break;
s.replace(needlePosition, replaceFrom.length(), replaceTo);
}
}
string decodeFromUtf8()
{
if('B' == toupper(encoding))
{
return decodeFromBase64();
}
else // if Q:
{
return decodeFromQuatedPrintable();
}
}
string decodeFromBase64()
{
istringstream is(extractedEncodedSubjectToConvert);
Poco::Base64Decoder e64(is);
extractedEncodedSubjectToConvert.clear();
string buffer;
while(getline(e64, buffer))
extractedEncodedSubjectToConvert += buffer;
return extractedEncodedSubjectToConvert;
}
string decodeFromQuatedPrintable()
{
replaceAll(extractedEncodedSubjectToConvert, "_", " ");
istringstream is(extractedEncodedSubjectToConvert);
Poco::Net::QuotedPrintableDecoder qp(is);
extractedEncodedSubjectToConvert.clear();
string buffer;
while(getline(qp, buffer))
extractedEncodedSubjectToConvert += buffer;
return extractedEncodedSubjectToConvert;
}
private:
string charset;
char encoding;
string extractedEncodedSubjectToConvert;
bool isStringEncoded;
static constexpr const char* sequenceBeginEncodedText = "=?";
static constexpr const char* sequenceEndEncodedText = "?=";
};
int main()
{
Poco::Net::POP3ClientSession session("poczta.o2.pl");
session.login("my mail", "my password");
Poco::Net::POP3ClientSession::MessageInfoVec messages;
session.listMessages(messages);
Poco::Net::MessageHeader header;
Poco::Net::MailMessage message;
auto currentMessage = messages[0];
session.retrieveHeader(currentMessage.id, header);
session.retrieveMessage(currentMessage.id, message);
const string subject = message.getSubject();
Encoder encoder(subject);
cout << "Original subject: " << subject << endl;
cout << "Encoded: " << encoder.convert() << endl;
}
Related
I am receiving data following a certain format over a TCP server by serializing them.
the class of the object:
class Command {
private:
char letter;
int x;
int y;
std::string button;
public:
Command(char _letter, int _x, int _y, std::string _button) {
letter = _letter;
x = _x;
y = _y;
button = _button;
}
Command(std::string serializedCmd)
{
size_t delimPos = 0;
std::vector<std::string> parts;
while ((delimPos = serializedCmd.find(SERIALIZE_DELIM)) != std::string::npos)
{
parts.push_back(serializedCmd.substr(0, delimPos));
serializedCmd.erase(0, delimPos + 1);
}
if (parts.empty()) {
this->letter = '$';
this->x = -1;
this->y = -1;
this->button = "nan";
return;
}
this->letter = (char)atoi(parts.at(0).data());
this->x = atoi(parts.at(1).data());
this->y = atoi(parts.at(2).data());
this->button = parts.at(3);
}
Command() {}
~Command() {}
std::string serialize()
{
return std::to_string(letter) + SERIALIZE_DELIM + std::to_string(x) + SERIALIZE_DELIM + std::to_string(y) + SERIALIZE_DELIM + button + SERIALIZE_DELIM;
}
char getLetter() { return letter; }
int getX() { return x; }
int getY() { return y; }
std::string getButton() { return button; }
bool isEmpty() {
return((this->letter == '$') && (this->x == -1) && (this->y == -1) && (this->button == "nan"));
}
void printCommand() {
std::cout << "letter: " << letter << std::endl;
std::cout << "x : " << x << std::endl;
std::cout << "y : " << y << std::endl;
std::cout << "button: " << button << std::endl;
std::cout << "================" << std::endl;
}
};
The data after being DeSerialized at the clients end follows this format:
||{key}|{x}|{y}|{button}||
Example: ||$|20|40|nan||
The problem is that when using recv to get the data, it seems that I'm picking up some noise around the command.
Example:
Sending:
||$|301|386|nan||
Reciving:
(¿ⁿ8T√|301|386|╠╠↕▼
The command is there although it's crowded with noise for some reason.
The code I'm using to receive the data:
char buf[4096];
Command c;
std::string commandTemp = "";
while (true) {
memset(buf, '\0', 4096);
const int size = recv(sock, buf, sizeof(buf), 0);
std::string s(buf,size);
std::cout << s << std::endl;
buf[size] = 0;
commandTemp = buf;
if (!commandTemp.empty()) {
try {
c = Command(commandTemp);
exe(c); //executes command (unrelated)
}
catch (const std::exception& err) {
std::cout << "Couldn't execute!!!!!!!!" << std::endl;
}
}
else {
std::cout << "Error empty command!\n";
}
}
If I am missing any information I will happily provide it.
Can someone maybe tell what the problem is?
You have to loop on the recv till you get the entire message
This may not be the immediate cause of you problem but you will hit it eventually.
TCP is a stream protocol, not a message protocol. All that TCP guarantees is that the bytes you send are received once and in order. But you might send one 100 byte message and receive 20 5 byte messages. You will say "but it works now", true if on the same machine or the messages are small but not true with larger message over a real netwrok so you must do this
char buf[4096];
Command c;
std::string commandTemp = "";
while (true) {
memset(buf, '\0', 4096);
int offset = 0;
int len = ??;
while(len > 0){
const int size = recv(sock, buf + offset, sizeof(buf) - offset, 0);
if (size == 0)
break; // record that we got incomplete message
offset += size;
len -= size;
}
....
Note that you need to know the length in advance too. So either send fixed length message or prepend a fixed size length to each message and read that first
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
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.
everyone! I am using rapidxml to parse my setting file. When I do everything in a single function, it works well. But when I try to add some functions to do the work, it always give std::exception. Does anyone know the reason?
A simple code example is
#include <stdexcept>
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include "rapidxml.hpp"
struct MyConfig{
bool LES_FLAG, ITP_FLAG;
int calcCase, dataInterval, maxIter;
double va, lesCoef, eigenLength, Re;
double xLow, xUp, yLow, yUp, maxTime;
}config;
void readConfig(const char* fileName);
void parseFlag(rapidxml::xml_node<> *node, char *flagName, bool &flag, int namesize=0);
void parseFloat(rapidxml::xml_node<> *node, char *name, double &variable, int namesize=0);
void parseInt(rapidxml::xml_node<> *node, char *name, int &variable, int namesize=0);
void parseString(rapidxml::xml_node<> *node, char *name, std::string &str, int namesize=0);
int main(int argn, char** argv){
try{
readConfig("test.xml");
}
catch (std::exception e)
{
std::cout << e.what() << std::endl;
}
}
void readConfig(const char* fileName){
std::ifstream myfile(fileName);
if (!myfile.is_open())
throw std::runtime_error("Can not open the configuration file!\n");
std::stringstream buffer;
buffer << myfile.rdbuf();
myfile.close();
try{
rapidxml::xml_document<> doc;
doc.parse<0>(&buffer.str()[0]);
rapidxml::xml_node<> *rNode = doc.first_node("Config_2DVPM");
rapidxml::xml_node<> *cnode = rNode->first_node("flags",5,false);
parseFlag(cnode, "LES_FLAG", config.LES_FLAG);
parseFlag(cnode, "ITP_FLAG", config.ITP_FLAG);
cnode = rNode->first_node("float",5,false);
cnode = rNode->first_node("float",5,false);
parseFloat(cnode, "va", config.va, 2);
parseFloat(cnode, "lesCoef", config.lesCoef, 7);
parseFloat(cnode, "eigenLength", config.eigenLength, 11);
parseFloat(cnode, "Re", config.Re, 2);
parseFloat(cnode, "xLow", config.xLow, 4);
parseFloat(cnode, "xUp", config.xUp, 3);
parseFloat(cnode, "yLow", config.yLow, 4);
parseFloat(cnode, "yUp", config.yUp, 3);
parseFloat(cnode, "maxTime", config.maxTime, 7);
cnode = rNode->first_node("int",3,false);
parseInt(cnode, "calcCase", config.calcCase);
parseInt(cnode, "dataInterval", config.dataInterval);
parseInt(cnode, "maxIter", config.maxIter);
}
catch (rapidxml::parse_error& e)
{
std::string ss = std::string("xml parse error: ") + e.what();
throw std::runtime_error(ss);
}
}
void parseFlag(rapidxml::xml_node<> *node, char* name, bool &flag, int namesize)
{
char *str = node->first_attribute(name, namesize, false)->value();
if (str == NULL)
{
std::string ss = std::string("Undefined attribute in flags: ") + name;
throw std::runtime_error(ss);
}
if (strcmp(str, "1") == 0 || strcmp(str, "true") == 0 || strcmp(str, "True") == 0)
flag = true;
else flag = false;
}
void parseFloat(rapidxml::xml_node<> *node, char* name,
double &variable, int namesize)
{
char* str = node->first_attribute(name, namesize, false)->value();
if (*str == '\0')
{
std::string ss = std::string("Undefined attribute in float: ") + name;
throw std::runtime_error(ss);
}
std::stringstream ss(str);
ss >> variable;
}
void parseInt(rapidxml::xml_node<> *node, char* name,
int &variable, int namesize)
{
char *str = node->first_attribute(name, namesize, false)->value();
if (*str == '\0')
{
std::string ss = std::string("Undefined attribute in int: ") + name;
throw std::runtime_error(ss);
}
std::stringstream ss(str);
ss >> variable;
}
void Calculation::parseString(rapidxml::xml_node<> *node, char* name,
std::string &res, int namesize)
{
char *str = node->first_attribute(name, namesize, false)->value();
if (*str == '\0')
{
std::string ss = std::string("Undefined attribute in int: ") + name;
throw std::runtime_error(ss);
}
res = str;
}
The example file text.xml is
<?xml version="1.0" encoding="UTF-8"?>
<Config_2DVPM version="1.0">
<flags LES_FLAG="true" ITP_FLAG="false"/>
<float va="1.0" lesCoef="0.15" eigenLength="1.0" Re="500" maxTime="15"
xLow="-1" xUp="5" yLow="-1" yUp="1"/>
<int calcCase="0" dataInterval="200" maxIter="100000"/>
<string test="haha"/>
</Config_2DVPM>
The code always gives an output std::exception. After some check, the problem always happens when I parse the attribute "eingenLength".
I need to get an access token (for a service account) for the google's OAuth authentication service. I tried several things an studied a lot of on the web but don't succeed.
Basically i followed https://developers.google.com/accounts/docs/OAuth2ServiceAccount
What i have done (VS2013):
int _tmain(int argc, _TCHAR* argv[])
{
Json::Value jwt_header;
Json::Value jwt_claim_set;
std::string jwt_b64;
std::time_t t = std::time(NULL);
Json::FastWriter jfw;
Json::StyledWriter jsw;
/* Create jwt header */
jwt_header["alg"] = "RS256";
jwt_header["typ"] = "JWT";
std::cout << jsw.write(jwt_header);
/* Create jwt claim set */
jwt_claim_set["iss"] = "myid#developer.gserviceaccount.com"; /* service account email address */
jwt_claim_set["scope"] = "https://www.googleapis.com/auth/plus.me" /* scope of requested access token */;
jwt_claim_set["aud"] = "https://accounts.google.com/o/oauth2/token"; /* intended target of the assertion for an access token */
jwt_claim_set["iad"] = std::to_string(t); /* issued time */
jwt_claim_set["exp"] = std::to_string(t+3600); /* expire time*/
std::cout << jsw.write(jwt_claim_set);
/* create http POST request body */
/* for header */
std::string json_buffer;
std::string json_buffer1;
json_buffer = jfw.write(jwt_header);
json_buffer = json_buffer.substr(0, json_buffer.size() - 1);
json_buffer = base64_encode(reinterpret_cast<const unsigned char*>(json_buffer.c_str()), json_buffer.length(), true); /* urlsafeBasic64 encode*/
json_buffer1.clear();
std::remove_copy(json_buffer.begin(), json_buffer.end(), std::back_inserter(json_buffer1), '=');
jwt_b64 = json_buffer1;
jwt_b64 += ".";
/* for claim set */
json_buffer = jfw.write(jwt_claim_set);
json_buffer = json_buffer.substr(0, json_buffer.size() - 1);
json_buffer = base64_encode(reinterpret_cast<const unsigned char*>(json_buffer.c_str()), json_buffer.length(), true); /* urlsafeBasic64 encode*/
json_buffer1.clear();
std::remove_copy(json_buffer.begin(), json_buffer.end(), std::back_inserter(json_buffer1), '=');
jwt_b64 += json_buffer1;
/* for signature */
std::string jwt_signature = jws_sign(jwt_b64, "key.p12");
if (!jwt_signature.empty())
{
jwt_b64 += ".";
json_buffer1.clear();
std::remove_copy(jwt_signature.begin(), jwt_signature.end(), std::back_inserter(json_buffer1), '=');
jwt_b64 += json_buffer1;
write2file("jwt.bat", jwt_b64); /* for test purpose calling with curl */
}
else
std::cout << "Error creating signature";
return 0;
}
int write2file(std::string filename, std::string data)
{
std::ofstream f(filename);
f << "%curl% -d \"grant_type=urn%%3Aietf%%3Aparams%%3Aoauth%%3Agrant-type%%3Ajwt-bearer&assertion=";
f << data;
f << "\" https://accounts.google.com/o/oauth2/token";
f.close();
return 0;
}
std::string jws_sign(std::string data, std::string pkcs12_path) {
SHA256_CTX mctx;
unsigned char hash[SHA256_DIGEST_LENGTH];
size_t hlen = SHA256_DIGEST_LENGTH;
const char *buf = data.c_str();
int n = strlen((const char*) buf);
SHA256_Init(&mctx);
SHA256_Update(&mctx, buf, n);
SHA256_Final(hash, &mctx);
std::string signature_b64;
unsigned char *sig = NULL;
size_t slen = 0;
EVP_PKEY_CTX *kctx;
EVP_PKEY *key = getPkey(pkcs12_path);
kctx = EVP_PKEY_CTX_new(key, NULL);
if (!kctx) goto err;
if (EVP_PKEY_sign_init(kctx) <= 0) goto err;
if (EVP_PKEY_CTX_set_rsa_padding(kctx, RSA_PKCS1_PADDING) <= 0) goto err;
if (EVP_PKEY_CTX_set_signature_md(kctx, EVP_sha256()) <= 0) goto err;
/* Determine buffer length */
if (EVP_PKEY_sign(kctx, NULL, &slen, hash, hlen) <= 0) goto err;
sig = (unsigned char *) OPENSSL_malloc(slen);
if (!sig) goto err;
if (EVP_PKEY_sign(kctx, sig, &slen, hash, hlen) <= 0) goto err;
signature_b64 = base64_encode(sig, (unsigned int)slen, true);
return signature_b64;
err:
/* Clean up */
EVP_cleanup();
signature_b64.clear();
return signature_b64;
}
All i receive back is
{
"error" : "invalid_grant"
}
So if someone can point me into the right direction would be great.
It would also help, if someone can point me to get the thing working by manually generating the jwt request out of openssl commands.
I'm working with VS2013
I found my mistake - was simply a typo :(
jwt_claim_set["iad"] = std::to_string(t); /* issued time */
needs to be
jwt_claim_set["iat"] = std::to_string(t); /* issued time */
The code works and generate valid token requests.
I've made a class for authentication on C++, will leave it here, may be someone may need it.
// YOU SHOULD GO TO Credentials SECTION FOR YOUR PROJECT AT https://console.developers.google.com/
// MAKE Service Account AND GET AUTHENTICATION JSON FROM IT,
// PLACE IT TO BUILD FOLDER AND CALL IT google_service_account.json
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
#include <iomanip>
// SSL INCLUDES
#include <openssl/aes.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/bio.h>
// https://github.com/nlohmann/json
#include <nlohmann/json.hpp>
using json = nlohmann::json;
class TGoogleAuthCpp {
json serviceAccountJSON;
bool serviceAccountExists;
void readServiceAccountJson();
RSA* createPrivateRSA(std::string key);
bool RSASign( RSA* rsa,
const unsigned char* Msg,
size_t MsgLen,
unsigned char** EncMsg,
size_t* MsgLenEnc);
std::string signMessage(std::string privateKey, std::string plainText);
std::string url_encode(const std::string &value);
std::string base64_encode(const std::string &in);
public:
TGoogleAuthCpp();
int createRequest();
};
TGoogleAuthCpp::TGoogleAuthCpp() {
serviceAccountExists = false;
readServiceAccountJson();
}
RSA* TGoogleAuthCpp::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;
}
bool TGoogleAuthCpp::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;
}
std::string TGoogleAuthCpp::signMessage(std::string privateKey, std::string plainText) {
RSA* privateRSA = createPrivateRSA(privateKey);
unsigned char* encMessage;
size_t encMessageLength;
RSASign(privateRSA, (unsigned char*) plainText.c_str(), plainText.length(), &encMessage, &encMessageLength);
std::string str1((char *)(encMessage), encMessageLength);
free(encMessage);
return base64_encode(str1);
}
void TGoogleAuthCpp::readServiceAccountJson() {
std::string fname = "google_service_account.json";
std::string line;
std::ifstream myfile (fname);
if (myfile.good()) {
std::stringstream ss;
if (myfile.is_open()) {
while (getline(myfile, line)) {
ss << line << '\n';
}
myfile.close();
serviceAccountJSON = json::parse(ss.str());
serviceAccountExists = true;
}
}
}
std::string TGoogleAuthCpp::base64_encode(const std::string &in) {
std::string out;
std::string base64_encode_b = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";//=
int val=0, valb=-6;
for (unsigned char c : in) {
val = (val<<8) + c;
valb += 8;
while (valb>=0) {
out.push_back(base64_encode_b[(val>>valb)&0x3F]);
valb-=6;
}
}
if (valb>-6) out.push_back(base64_encode_b[((val<<8)>>(valb+8))&0x3F]);
while (out.size()%4) out.push_back('=');
return out;
}
std::string TGoogleAuthCpp::url_encode(const std::string &value) {
std::ostringstream escaped;
escaped.fill('0');
escaped << std::hex;
for (std::string::const_iterator i = value.begin(), n = value.end(); i != n; ++i) {
std::string::value_type c = (*i);
// Keep alphanumeric and other accepted characters intact
if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') {
escaped << c;
continue;
}
// Any other characters are percent-encoded
escaped << std::uppercase;
escaped << '%' << std::setw(2) << int((unsigned char) c);
escaped << std::nouppercase;
}
return escaped.str();
}
int TGoogleAuthCpp::createRequest() {
if (!serviceAccountExists) return 0;
json jwt_header;
json jwt_claim_set;
std::time_t t = std::time(NULL);
// Create jwt header
jwt_header["alg"] = "RS256";
jwt_header["typ"] = "JWT";
// Create jwt claim set
jwt_claim_set["iss"] = serviceAccountJSON["client_email"]; /* service account email address */
jwt_claim_set["scope"] = "https://www.googleapis.com/auth/androidpublisher" /* scope of requested access token */;
jwt_claim_set["aud"] = serviceAccountJSON["token_uri"]; /* intended target of the assertion for an access token */
jwt_claim_set["iat"] = t; /* issued time */
jwt_claim_set["exp"] = t+3600; /* expire time*/
// web token
std::stringstream jwt_ss;
// header
jwt_ss << base64_encode(jwt_header.dump());
jwt_ss << ".";
// claim set
jwt_ss << base64_encode(jwt_claim_set.dump());
// signature
std::string signed_msg = signMessage(serviceAccountJSON["private_key"], jwt_ss.str());
jwt_ss << "." << signed_msg;
std::stringstream post_body_ss;
post_body_ss << "curl -d '";
post_body_ss << "grant_type=" << url_encode("urn:ietf:params:oauth:grant-type:jwt-bearer");
post_body_ss << "&assertion=" << url_encode(jwt_ss.str());
post_body_ss << "' https://oauth2.googleapis.com/token";
std::string post_body = post_body_ss.str();
std::cout << post_body << std::endl;
return 1;
}
int main() {
TGoogleAuthCpp auth;
int res = auth.createRequest();
}