I have some openssl code that encrypts and decrypts a file.
Initially I wrote it so the encrypted file is base64 encoded. The problem is certain file sizes are reduced on decryption. i.e I lose upto 16 characters in the decrypted based on the original file
If the file is less that 16 bytes it is fine, greater than that and I start losing characters on decryption with and error on the final decrypt step.
I assumed initially it was a padding issue, but adding padding does bot fix however If I remove the base64 encoding it is fine however.
This is based on open_ssl 1.1.1s
int BIO_enc_header(BIO* file, unsigned char* Salt)
{
RAND_bytes(Salt, SALTSIZE);
int len=BIO_write(file,MAGIC, MAGIC_SIZE);
len=BIO_write(file,Salt, SALTSIZE);
return 0;
}
int BIO_dec_header(BIO* file, unsigned char* Salt)
{
int mlen = 0;
char mBuff[MAGIC_SIZE + 1] = { 0 };
mlen = BIO_read(file,mBuff, MAGIC_SIZE);
if (mlen == MAGIC_SIZE) {
/* Check Magic size */
int salt_len = BIO_read(file,Salt,SALTSIZE);
if (salt_len != SALTSIZE) {
return -1;
}
}
return 0;
}
//#define BASE64 1
int do_crypt_bio(BIO* bio_in, BIO* bio_out, int do_encrypt, unsigned char* szPassword)
{
int Result = OK;
unsigned char salt[8] = { 0 };
int inByte = 0, outByte = 0;
//int Padding;
#ifdef BASE64
BIO *b64 = BIO_new(BIO_f_base64());
if (!b64) {
return ERROR;
}
#endif
#ifdef BASE64
if (do_encrypt) {
/*out base64*/
bio_out = BIO_push(b64, bio_out);
BIO_set_flags(bio_out, BIO_FLAGS_BASE64_NO_NL);
}else {
/* read base 64*/
bio_in = BIO_push(b64, bio_in);
BIO_set_flags(bio_in, BIO_FLAGS_BASE64_NO_NL);
}
#endif
/* Allow enough space in output buffer for additional block */
unsigned char inbuf[ENC_BUFF_SIZE+ EVP_MAX_BLOCK_LENGTH], outbuf[ENC_BUFF_SIZE + EVP_MAX_BLOCK_LENGTH];
int inlen, outlen;
EVP_CIPHER_CTX* ctx=NULL;
#define AES_256_KEY_SIZE 32
unsigned char key[AES_256_KEY_SIZE] = { 0 };
unsigned char iv[AES_BLOCK_SIZE] = { 0 };
int rounds = 5;
int Size=EVP_CIPHER_block_size(EVP_aes_256_cbc());
if (do_encrypt) {
BIO_enc_header(bio_out, salt);
}else {
BIO_dec_header(bio_in, salt);
}
if (!EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha256(), salt, szPassword, strlen((char*)szPassword), rounds, key, iv)) {
#ifdef BASE64
BIO_free(b64);
#endif
return ERROR;
}
/* Don't set key or IV right away; we want to check lengths */
ctx = EVP_CIPHER_CTX_new();
if (!ctx) {
#ifdef BASE64
BIO_free(b64);
#endif
return ERROR;
}
int CipherResult=EVP_CipherInit_ex(ctx, EVP_aes_256_cbc(), NULL, NULL, NULL, do_encrypt);
CipherResult =EVP_CipherInit_ex(ctx, NULL, NULL, key, iv, do_encrypt); /* Now we can set key and IV */
//EVP_CIPHER_CTX_set_padding(ctx, 32);
int len;
for (;;) {
memset(inbuf, 0, ENC_BUFF_SIZE+EVP_MAX_BLOCK_LENGTH);
inlen = BIO_read(bio_in,inbuf, ENC_BUFF_SIZE);
//inByte += inlen;
if (inlen <= 0)break;
if (!EVP_CipherUpdate(ctx, outbuf, &outlen, inbuf, inlen)) {
/* Error */
Result = ERROR;
goto error;
}
len=BIO_write(bio_out,outbuf,outlen);
//outByte += outlen;
}
int f_len;
if (!EVP_CipherFinal_ex(ctx, outbuf, &f_len)) {
/* Error */
Result = ERROR;
ERR_print_errors_fp(stderr);
goto error;
}
len= BIO_write(bio_out, outbuf, f_len);
//outByte += f_len;
len = len;
error:
EVP_CIPHER_CTX_free(ctx);
#ifdef BASE64
BIO_free(b64);
#endif
return Result;
}
int EncryptFile(char* input, char *encrypted,char* password)
{
int Result = OK;
BIO* bio_in = BIO_new_file(input, "rb");
if (!bio_in) {
return ERROR;
}
BIO* bio_out = BIO_new_file(encrypted, "wb+");
if (!bio_out) {
BIO_free(bio_in);
return ERROR;
}
Result=do_crypt_bio(bio_in, bio_out, 1, (unsigned char*)password);
BIO_free(bio_in);
BIO_free(bio_out);
return Result;
}
int DecryptFile(char* encrypted, char* output, char* password)
{
int Result = OK;
BIO* bio_in = BIO_new_file(encrypted, "rb");
if (!bio_in) {
return ERROR;
}
BIO* bio_out = BIO_new_file(output, "wb+");
if (!bio_out) {
BIO_free(bio_in);
return ERROR;
}
Result=do_crypt_bio(bio_in, bio_out, 0, (unsigned char*)password);
BIO_free(bio_in);
BIO_free(bio_out);
return Result;
}
Related
Thank you so much for answering this question.
I use lame and I want to decode mp3 file to wav.
I succeeded in decoding mp3 files into wav files through several searches.
However, the size of the wav file is created too large and an error message appears.
Media player error message :
This file cannot be played. The file format may not be supported, the file extension may be incorrect, or the file may be corrupted.
If you know my problem, please give me some advice.
Thank you
HEADER FILE
#pragma once
#ifndef _LAME_HELPER_H_
#define _LAME_HELPER_H_
#include <windows.h>
#include "lame.h"
#define LH_STARTED WM_USER+1
#define LH_COMPUTED WM_USER+2
#define LH_DONE WM_USER+3
#define LH_ERROR WM_USER+4
#define MAX_THREAD_COUNT 5
enum encode_mode_e
{
EM_ABR,
EM_CBR,
EM_VBR
};
enum encode_channel_e
{
EC_MONO,
EC_STEREO
};
enum bitrate_e
{
BR_8kbps = 8,
BR_16kbps = 16,
BR_24kbps = 24,
BR_32kbps = 32,
BR_40kbps = 40,
BR_48kbps = 48,
BR_56kbps = 56,
BR_64kbps = 64,
BR_80kbps = 80,
BR_96kbps = 96,
BR_112kbps = 112,
BR_128kbps = 128,
BR_144kbps = 144,
BR_160kbps = 160,
BR_192kbps = 192,
BR_224kbps = 224,
BR_256kbps = 256,
BR_320kbps = 320
};
enum samplerate_e
{
SR_8khz = 8000,
SR_11khz = 11025,
SR_12khz = 12000,
SR_16khz = 16000,
SR_22khz = 22050,
SR_24khz = 24000,
SR_32khz = 32000,
SR_44khz = 44100,
SR_48khz = 48000
};
struct settings_t
{
char* title;
char* artist;
char* album;
char* comment;
char* year;
char* track;
char* genre;
char* albumart;
encode_channel_e channels;
bitrate_e abr_bitrate;
bitrate_e cbr_bitrate;
int quality;
encode_mode_e enc_mode;
samplerate_e resample_frequency;
samplerate_e in_samplerate;
//The constructor; used to set default values
settings_t();
};
class CLameHelper; //lameHelper prototype, needed because of struct StaticParam_t
//Use to hold parameters for the thread function
struct StaticParam_t
{
char* pcm;
char* mp3;
settings_t settings;
WNDPROC callback_proc;
CLameHelper* lhObj;
};
class CLameHelper
{
public :
static const int PCM_SIZE = 4096;
static const int MP3_SIZE = 4096;
HANDLE m_hThread[MAX_THREAD_COUNT];
StaticParam_t* m_phSParam[MAX_THREAD_COUNT];
static int Decode_s(void* pParam);
void WriteWaveHeader(FILE* const, int, int, int, int);
void Write32BitLowHigh(FILE*, int);
void Write16BitLowHigh(FILE*, int);
int SetID3AlbumArt(lame_t gfp, char const* szFileName);
void errorHandler(char*);
char errMsg[1000];
public:
CLameHelper();
~CLameHelper();
int Decode(char* szMp3_in, char* szPcm_out);
int Decode(char* szMp3_in, char* szPcm_out, WNDPROC callback_proc);
};
#endif
CPP FILE
#include "stdafx.h"
#include "LameHelper.h"
settings_t::settings_t()
{
//Setting the default values
title = "";
artist = "";
album = "";
comment = "";
year = "";
track = "";
genre = "";
albumart = NULL;
channels = EC_STEREO;
abr_bitrate = BR_128kbps;
cbr_bitrate = BR_128kbps;
quality = 5;
enc_mode = EM_CBR;
resample_frequency = SR_44khz;
in_samplerate = SR_44khz;
}
CLameHelper::CLameHelper()
{
//Initialize to NULL, aids deletion/closing later
for(int i = 0; i < MAX_THREAD_COUNT; i++)
{
m_hThread[i] = NULL;
m_phSParam[i] = NULL;
}
}
CLameHelper::~CLameHelper()
{
//Destroy all declared objects
for(int i = 0; i < MAX_THREAD_COUNT; i++)
{
if(m_hThread[i] != NULL)
CloseHandle(m_hThread[i]);
if(m_phSParam[i] != NULL)
delete m_phSParam[i];
}
}
int CLameHelper::SetID3AlbumArt(lame_t gfp, char const* szFileName)
{
int iResult = -1;
FILE *pFileName = 0;
char *szAlbumart = 0;
if(szFileName == NULL)
{
return 0;
}
pFileName = fopen(szFileName, "rb");
if(!pFileName)
{
iResult = 1;
}
else
{
size_t size;
fseek(pFileName, 0, SEEK_END);
size = ftell(pFileName);
fseek(pFileName, 0, SEEK_SET);
szAlbumart = (char*)malloc(size);
if(!szAlbumart)
{
iResult = 2;
}
else
{
if(fread(szAlbumart, 1, size, pFileName) != size)
{
iResult = 3;
}
else
{
iResult = (gfp, szAlbumart, size) ? 4 : 0;
}
free(szAlbumart);
}
fclose(pFileName);
}
switch(iResult)
{
case 1:
sprintf(errMsg, "WARNING: could not find file '%s' for szAlbumart.\n", szFileName);
errorHandler(errMsg);
break;
case 2:
errorHandler("WARNING: insufficient memory for reading the szAlbumart.\n");
break;
case 3:
sprintf(errMsg, "WARNING: read error in '%s' for szAlbumart.\n", szFileName);
errorHandler(errMsg);
break;
case 4:
sprintf(errMsg, "WARNING: unsupported image: '%s' for szAlbumart. Specify JPEG/PNG/GIF image\n", szFileName);
errorHandler(errMsg);
break;
default:
break;
}
return iResult;
}
void CLameHelper::Write16BitLowHigh(FILE * fp, int val)
{
unsigned char bytes[2];
bytes[0] = (val & 0xff);
bytes[1] = ((val >> 8) & 0xff);
fwrite(bytes, 1, 2, fp);
}
void CLameHelper::Write32BitLowHigh(FILE * fp, int val)
{
unsigned char bytes[4];
bytes[0] = (val & 0xff);
bytes[1] = ((val >> 8) & 0xff);
bytes[2] = ((val >> 16) & 0xff);
bytes[3] = ((val >> 24) & 0xff);
fwrite(bytes, 1, 4, fp);
}
void CLameHelper::WriteWaveHeader(FILE * const fp, int pcmbytes, int freq, int channels, int bits)
{
int bytes = (bits + 7) / 8;
/* quick and dirty, but documented */
fwrite("RIFF", 1, 4, fp); /* label */
Write32BitLowHigh(fp, pcmbytes + 44 - 8); /* length in bytes without header */
fwrite("WAVEfmt ", 2, 4, fp); /* 2 labels */
Write32BitLowHigh(fp, 2 + 2 + 4 + 4 + 2 + 2); /* length of PCM format declaration area */
Write16BitLowHigh(fp, 1); /* is PCM? */
Write16BitLowHigh(fp, channels); /* number of channels */
Write32BitLowHigh(fp, freq); /* sample frequency in [Hz] */
Write32BitLowHigh(fp, freq * channels * bytes); /* bytes per second */
Write16BitLowHigh(fp, channels * bytes); /* bytes per sample time */
Write16BitLowHigh(fp, bits); /* bits per sample */
fwrite("data", 1, 4, fp); /* label */
Write32BitLowHigh(fp, pcmbytes); /* length in bytes of raw PCM data */
}
int CLameHelper::Decode(char* szMp3_in, char* szPcm_out)
{
return Decode(szMp3_in, szPcm_out, NULL);
}
//the static function used for the thread
int CLameHelper::Decode_s(void* param)
{
StaticParam_t* sp = (StaticParam_t*)param;
char* szPcm_out = sp->pcm;
char* szMp3_in = sp->mp3;
WNDPROC callback_proc = sp->callback_proc;
CLameHelper* lh = (CLameHelper*)sp->lhObj;
return lh->Decode(szMp3_in, szPcm_out, callback_proc);
}
int CLameHelper::Decode(char* szMp3_in, char* szPcm_out, WNDPROC callback_proc)
{
int read, i, samples;
long wavsize = 0; // use to count the number of mp3 byte read, this is used to write the length of the wave file
long cumulative_read = 0;
short int pcm_l[PCM_SIZE], pcm_r[PCM_SIZE];
unsigned char mp3_buffer[MP3_SIZE];
FILE* mp3 = fopen(szMp3_in, "rb");
if(mp3 == NULL)
{
if(callback_proc != NULL)
{
callback_proc((HWND)GetModuleHandle(NULL), LH_ERROR, -1, NULL);
}
sprintf(errMsg, "FATAL ERROR: file '%s' can't be open for read. Aborting!\n", szMp3_in);
errorHandler(errMsg);
return -1;
}
fseek(mp3, 0, SEEK_END);
long MP3_total_size = ftell(mp3);
fseek(mp3, 0, SEEK_SET);
FILE* pcm = fopen(szPcm_out, "wb");
if(pcm == NULL)
{
if(callback_proc != NULL)
{
callback_proc((HWND)GetModuleHandle(NULL), LH_ERROR, -1, NULL);
}
sprintf(errMsg, "FATAL ERROR: file '%s' can't be open for write. Aborting!\n", szPcm_out);
errorHandler(errMsg);
return -1;
}
lame_t lame = lame_init();
lame_set_decode_only(lame, 1);
if(lame_init_params(lame) == -1)
{
if(callback_proc != NULL)
{
callback_proc((HWND)GetModuleHandle(NULL), LH_ERROR, -2, NULL);
}
sprintf(errMsg, "FATAL ERROR: parameters failed to initialize properly in lame. Aborting!\n", szPcm_out);
errorHandler(errMsg);
return -2;
}
hip_t hip = hip_decode_init();
mp3data_struct mp3data;
memset(&mp3data, 0, sizeof(mp3data));
int nChannels = -1;
int nSampleRate = -1;
int mp3_len;
if(callback_proc != NULL)
{
callback_proc((HWND)GetModuleHandle(NULL), LH_STARTED, NULL, NULL);
}
while((read = fread(mp3_buffer, sizeof(char), MP3_SIZE, mp3)) > 0)
{
mp3_len = read;
cumulative_read += read * sizeof(char);
do
{
samples = hip_decode1_headers(hip, mp3_buffer, mp3_len, pcm_l, pcm_r, &mp3data);
wavsize += samples;
if(mp3data.header_parsed == 1)//header is gotten
{
if(nChannels < 0)//reading for the first time
{
//Write the header
WriteWaveHeader(pcm, 0x7FFFFFFF, mp3data.samplerate, mp3data.stereo, 16); //unknown size, so write maximum 32 bit signed value
}
nChannels = mp3data.stereo;
nSampleRate = mp3data.samplerate;
}
if(samples > 0 && mp3data.header_parsed != 1)
{
errorHandler("WARNING: lame decode error occured!");
break;
}
if(samples > 0)
{
for(i = 0 ; i < samples; i++)
{
fwrite((char*)&pcm_l[i], sizeof(char), sizeof(pcm_l[i]), pcm);
if(nChannels == 2)
{
fwrite((char*)&pcm_r[i], sizeof(char), sizeof(pcm_r[i]), pcm);
}
}
}
mp3_len = 0;
if(callback_proc != NULL)
{
int percentage = ((float)cumulative_read/MP3_total_size)*100;
callback_proc((HWND)GetModuleHandle(NULL), LH_COMPUTED, percentage, NULL);
}
}while(samples>0);
}
i = (16 / 8) * mp3data.stereo;
if (wavsize <= 0)
{
wavsize = 0;
}
else if (wavsize > 0xFFFFFFD0 / i)
{
wavsize = 0xFFFFFFD0;
}
else
{
wavsize *= i;
}
if(!fseek(pcm, 0l, SEEK_SET))//seek back and adjust length
WriteWaveHeader(pcm, (int) wavsize, mp3data.samplerate, mp3data.stereo, 16);
else
errorHandler("WARNING: can't seek back to adjust length in wave header!");
hip_decode_exit(hip);
lame_close(lame);
fclose(mp3);
fclose(pcm);
if(callback_proc != NULL)
{
callback_proc((HWND)GetModuleHandle(NULL), LH_DONE, NULL, NULL);
}
return 0;
}
void CLameHelper::errorHandler(char* msg)
{
printf("%s\n", msg);
}
I wrote the following code that should return a signature of the provided std::string signed using an EC private key.
std::string msg = "test";
size_t messageLength = msg.length();
size_t encMessageLength;
unsigned char* encMessage;
signMessage(msg, evp_private_key, messageLength, &encMessageLength, &encMessage);
bool signMessage(std::string msg, EVP_PKEY *&key, size_t messageLength, size_t *encMessageLength, unsigned char** encMessage) {
unsigned char * u_msg = (unsigned char*) msg.c_str();
EVP_MD_CTX* signCtx = EVP_MD_CTX_new();
if (EVP_DigestSignInit(signCtx,NULL, EVP_sha256(), NULL, key) <= 0) {
return NULL;
}
if (EVP_DigestSignUpdate(signCtx, u_msg, messageLength) <= 0) {
return NULL;
}
if (EVP_DigestSignFinal(signCtx, NULL, encMessageLength) <= 0) {
return NULL;
}
*encMessage = (unsigned char*)malloc(*encMessageLength);
if (EVP_DigestSignFinal(signCtx, *encMessage, encMessageLength) <= 0) {
return NULL;
}
EVP_MD_CTX_free(signCtx);
return true;
}
*encMessage is freed after the function call.
The length of encMessage varies greatly between 3 and 139.
An example output casted and printed as char* as well as hex looks like this:
�D�b���w�x�VS:������dƀ7�#����v�B%M�X2�ky���!�h�};J>�!���h�ħ�n����/�j�U���aL����z�X���>�2�>
Length: 138
hex: 30818702414fa9b7d8cac465525add0451784bc751544702edd0e24bdb0dca44f362fc80a11b3777e78316788e56533a91c0a1eb058bf1a564c68037900640cc13b9aca776c4024201254dd55832a16b799ad2f021f468a07d3b4a3e801321bba284688fc4a7c0156e86da1e84952f8c6ab755da1dd3ec614cec10bea1bc7aac58affa9c3efb32990f3e
Another function call:
msg: 0��B
Length: 5
hex: 3081880242
What am I doing wrong?
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();
}
I'm trying to read a string from a file encrypt it with AES and then save it to other file. Later I need to read the new file, decrypt and save the output to a new file again. The problem is some strange character are appearing.
int Crypt::__aesEncrypt(const unsigned char *msg, size_t msgLen, unsigned char **encMsg) {
EVP_CIPHER_CTX *aesEncryptCtx = (EVP_CIPHER_CTX*)malloc(sizeof(EVP_CIPHER_CTX));
EVP_CIPHER_CTX_init(aesEncryptCtx);
unsigned char *aesKey = (unsigned char*)malloc(AES_KEYLEN/8);
unsigned char *aesIV = (unsigned char*)malloc(AES_KEYLEN/8);
unsigned char *aesPass = (unsigned char*)malloc(AES_KEYLEN/8);
unsigned char *aesSalt = (unsigned char*)malloc(8);
if(RAND_bytes(aesPass, AES_KEYLEN/8) == 0) {
return FAILURE;
}
if(RAND_bytes(aesSalt, 8) == 0) {
return FAILURE;
}
if(EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha1(), aesSalt, aesPass, AES_KEYLEN/8, AES_ROUNDS, aesKey, aesIV) == 0) {
return FAILURE;
}
strncpy((char*)aesKey, (const char*)"B374A26A714904AAB374A26A714904AA", AES_KEYLEN/8);
strncpy((char*)aesIV, (const char*)"B374A26A714904AA", AES_KEYLEN/16);
size_t blockLen = 0;
size_t encMsgLen = 0;
*encMsg = (unsigned char*)malloc(msgLen + AES_BLOCK_SIZE);
if(encMsg == NULL) return FAILURE;
(*encMsg)[0] = '\0';
if(!EVP_EncryptInit_ex(aesEncryptCtx, EVP_aes_256_cbc(), NULL, aesKey, aesIV)) {
return FAILURE;
}
if(!EVP_EncryptUpdate(aesEncryptCtx, *encMsg, (int*)&blockLen, (unsigned char*)msg, msgLen)) {
return FAILURE;
}
encMsgLen += blockLen;
if(!EVP_EncryptFinal_ex(aesEncryptCtx, *encMsg + encMsgLen, (int*)&blockLen)) {
return FAILURE;
}
EVP_CIPHER_CTX_cleanup(aesEncryptCtx);
free(aesEncryptCtx);
free(aesKey);
free(aesIV);
return encMsgLen + blockLen;
}
int Crypt::__aesDecrypt(unsigned char *encMsg, size_t encMsgLen, char **decMsg) {
EVP_CIPHER_CTX *aesDecryptCtx = (EVP_CIPHER_CTX*)malloc(sizeof(EVP_CIPHER_CTX));
EVP_CIPHER_CTX_init(aesDecryptCtx);
unsigned char *aesKey;
unsigned char *aesIV;
aesKey = (unsigned char*)malloc(AES_KEYLEN/8);
aesIV = (unsigned char*)malloc(AES_KEYLEN/8);
unsigned char *aesPass = (unsigned char*)malloc(AES_KEYLEN/8);
unsigned char *aesSalt = (unsigned char*)malloc(8);
if(RAND_bytes(aesPass, AES_KEYLEN/8) == 0) {
return FAILURE;
}
if(RAND_bytes(aesSalt, 8) == 0) {
return FAILURE;
}
if(EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha1(), aesSalt, aesPass, AES_KEYLEN/8, AES_ROUNDS, aesKey, aesIV) == 0) {
return FAILURE;
}
strncpy((char*)aesKey, (const char*)"B374A26A714904AAB374A26A714904AA", AES_KEYLEN/8);
strncpy((char*)aesIV, (const char*)"B374A26A714904AA", AES_KEYLEN/16);
size_t decLen = 0;
size_t blockLen = 0;
*decMsg = (char*)malloc(encMsgLen);
if(*decMsg == NULL) return FAILURE;
if(!EVP_DecryptInit_ex(aesDecryptCtx, EVP_aes_256_cbc(), NULL, aesKey, aesIV)) {
return FAILURE;
}
if(!EVP_DecryptUpdate(aesDecryptCtx, (unsigned char*)*decMsg, (int*)&blockLen, encMsg, (int)encMsgLen)) {
return FAILURE;
}
decLen += blockLen;
if(!EVP_DecryptFinal_ex(aesDecryptCtx, (unsigned char*)*decMsg + decLen, (int*)&blockLen)) {
return FAILURE;
}
decLen += blockLen;
(*decMsg)[decLen] = '\0';
EVP_CIPHER_CTX_cleanup(aesDecryptCtx);
return encMsgLen;
}
Encrypting:
unsigned char *encMsg = NULL;
int size = __aesEncrypt((const unsigned char*)decrypted_string.c_str(), decrypted_string.size(), &encMsg);
return std::string(reinterpret_cast<const char*>(encMsg), size);
Decrypting:
char *decMsg = NULL;
int size = __aesDecrypt((unsigned char*)encrypted_string.c_str(), encrypted_string.size(), &decMsg);
return std::string(reinterpret_cast<const char*>(decMsg), size);
I can successfully crypt and decrypt, but some strange characters are appearing at the end of the encrypted file, they are like empty spaces:
AES is a block cypher. It takes blocks of 16 bytes, and encrypt them into a block of 16 byets. If you try to use it with data whose length is not a multiple of 16, padding (usually random data) is added to round it up to a multiple of 16 bytes. You need to manage the length of the data yourself.
Example:
int encryptHelper(const string& msg, ...)
{
uint32_t msgSize = msg.length();
newMsg.push_back((msgSize >> 0) & 0xFF);
newMsg.push_back((msgSize >> 8) & 0xFF);
newMsg.push_back((msgSize >> 16) & 0xFF);
newMsg.push_back((msgSize >> 24) & 0xFF);
string newMsg(reinterpret_cast<const char*>(&msgSize), sizeof(msgSize));
newMsg += msg;
return __aesEncrypt(newMsg.c_str(), newMsg.length(), ...);
}
int decryptHelper(const string& encrypted, ...)
{
string msg = ... whatever you are doing to decrypt
uint32_t actualSize = 0;
// remove signal first, then widen
actualSize |= static_cast<uint32_t>(static_cast<unsigned char>(msg[0])) << 0;
actualSize |= static_cast<uint32_t>(static_cast<unsigned char>(msg[1])) << 8;
actualSize |= static_cast<uint32_t>(static_cast<unsigned char>(msg[2])) << 16;
actualSize |= static_cast<uint32_t>(static_cast<unsigned char>(msg[3])) << 24;
string actualMsg = msg.substr(4, actualSize);
...
}
I didn't bother to write the exact code to call your functions because all that casting and memory leaking gave me nausea. Fill in the blanks.
How can I generate SHA1 or SHA2 hashes using the OpenSSL libarary?
I searched google and could not find any function or example code.
From the command line, it's simply:
printf "compute sha1" | openssl sha1
You can invoke the library like this:
#include <stdio.h>
#include <string.h>
#include <openssl/sha.h>
int main()
{
unsigned char ibuf[] = "compute sha1";
unsigned char obuf[20];
SHA1(ibuf, strlen(ibuf), obuf);
int i;
for (i = 0; i < 20; i++) {
printf("%02x ", obuf[i]);
}
printf("\n");
return 0;
}
OpenSSL has a horrible documentation with no code examples, but here you are:
#include <openssl/sha.h>
bool simpleSHA256(void* input, unsigned long length, unsigned char* md)
{
SHA256_CTX context;
if(!SHA256_Init(&context))
return false;
if(!SHA256_Update(&context, (unsigned char*)input, length))
return false;
if(!SHA256_Final(md, &context))
return false;
return true;
}
Usage:
unsigned char md[SHA256_DIGEST_LENGTH]; // 32 bytes
if(!simpleSHA256(<data buffer>, <data length>, md))
{
// handle error
}
Afterwards, md will contain the binary SHA-256 message digest. Similar code can be used for the other SHA family members, just replace "256" in the code.
If you have larger data, you of course should feed data chunks as they arrive (multiple SHA256_Update calls).
Adaptation of #AndiDog version for big file:
static const int K_READ_BUF_SIZE{ 1024 * 16 };
std::optional<std::string> CalcSha256(std::string filename)
{
// Initialize openssl
SHA256_CTX context;
if(!SHA256_Init(&context))
{
return std::nullopt;
}
// Read file and update calculated SHA
char buf[K_READ_BUF_SIZE];
std::ifstream file(filename, std::ifstream::binary);
while (file.good())
{
file.read(buf, sizeof(buf));
if(!SHA256_Update(&context, buf, file.gcount()))
{
return std::nullopt;
}
}
// Get Final SHA
unsigned char result[SHA256_DIGEST_LENGTH];
if(!SHA256_Final(result, &context))
{
return std::nullopt;
}
// Transform byte-array to string
std::stringstream shastr;
shastr << std::hex << std::setfill('0');
for (const auto &byte: result)
{
shastr << std::setw(2) << (int)byte;
}
return shastr.str();
}
correct syntax at command line should be
echo -n "compute sha1" | openssl sha1
otherwise you'll hash the trailing newline character as well.
Here is OpenSSL example of calculating sha-1 digest using BIO:
#include <openssl/bio.h>
#include <openssl/evp.h>
std::string sha1(const std::string &input)
{
BIO * p_bio_md = nullptr;
BIO * p_bio_mem = nullptr;
try
{
// make chain: p_bio_md <-> p_bio_mem
p_bio_md = BIO_new(BIO_f_md());
if (!p_bio_md) throw std::bad_alloc();
BIO_set_md(p_bio_md, EVP_sha1());
p_bio_mem = BIO_new_mem_buf((void*)input.c_str(), input.length());
if (!p_bio_mem) throw std::bad_alloc();
BIO_push(p_bio_md, p_bio_mem);
// read through p_bio_md
// read sequence: buf <<-- p_bio_md <<-- p_bio_mem
std::vector<char> buf(input.size());
for (;;)
{
auto nread = BIO_read(p_bio_md, buf.data(), buf.size());
if (nread < 0) { throw std::runtime_error("BIO_read failed"); }
if (nread == 0) { break; } // eof
}
// get result
char md_buf[EVP_MAX_MD_SIZE];
auto md_len = BIO_gets(p_bio_md, md_buf, sizeof(md_buf));
if (md_len <= 0) { throw std::runtime_error("BIO_gets failed"); }
std::string result(md_buf, md_len);
// clean
BIO_free_all(p_bio_md);
return result;
}
catch (...)
{
if (p_bio_md) { BIO_free_all(p_bio_md); }
throw;
}
}
Though it's longer than just calling SHA1 function from OpenSSL, but it's more universal and can be reworked for using with file streams (thus processing data of any length).
C version of #Nayfe code, generating SHA1 hash from file:
#include <stdio.h>
#include <openssl/sha.h>
static const int K_READ_BUF_SIZE = { 1024 * 16 };
unsigned char* calculateSHA1(char *filename)
{
if (!filename) {
return NULL;
}
FILE *fp = fopen(filename, "rb");
if (fp == NULL) {
return NULL;
}
unsigned char* sha1_digest = malloc(sizeof(char)*SHA_DIGEST_LENGTH);
SHA_CTX context;
if(!SHA1_Init(&context))
return NULL;
unsigned char buf[K_READ_BUF_SIZE];
while (!feof(fp))
{
size_t total_read = fread(buf, 1, sizeof(buf), fp);
if(!SHA1_Update(&context, buf, total_read))
{
return NULL;
}
}
fclose(fp);
if(!SHA1_Final(sha1_digest, &context))
return NULL;
return sha1_digest;
}
It can be used as follows:
unsigned char *sha1digest = calculateSHA1("/tmp/file1");
The res variable contains the sha1 hash.
You can print it on the screen using the following for-loop:
char *sha1hash = (char *)malloc(sizeof(char) * 41);
sha1hash[40] = '\0';
int i;
for (i = 0; i < SHA_DIGEST_LENGTH; i++)
{
sprintf(&sha1hash[i*2], "%02x", sha1digest[i]);
}
printf("SHA1 HASH: %s\n", sha1hash);