Invalid ciphertext when performing RSA decryption - c++

I use the following code to encrypt and decrypt a string making use of Crypto++ 5.6.2 library
string to_BER(string spriv,bool b)
{
string HEADER, FOOTER;
if(b)
{
HEADER = "-----BEGIN RSA PRIVATE KEY-----";
FOOTER = "-----END RSA PRIVATE KEY-----";
}
else
{
HEADER = "-----BEGIN PUBLIC KEY-----";
FOOTER = "-----END PUBLIC KEY-----";
}
size_t pos1, pos2;
pos1 = spriv.find(HEADER);
if(pos1 == string::npos)
throw std::runtime_error("PEM header not found");
pos2 = spriv.find(FOOTER, pos1+1);
if(pos2 == string::npos)
throw std::runtime_error("PEM footer not found");
// Start position and length
pos1 = pos1 + HEADER.length();
pos2 = pos2 - pos1;
string keystr = spriv.substr(pos1, pos2);
return keystr;
}
string rsa_encrypt(const string &message, const string &public_key)
{
string keystr=to_BER(public_key,false);
ByteQueue queue;
Base64Decoder decoder;
decoder.Attach(new Redirector(queue));
decoder.Put((const byte*)keystr.data(), keystr.length());
decoder.MessageEnd();
RSAES_PKCS1v15_Encryptor e;
e.AccessKey().Load(queue);
bool key_ok = e.AccessKey().Validate(rng, 3);
if(!key_ok)
{
printf("ERROR IN RSA KEY\n");
return "";
}
string encrypted_data;
StringSource ss1(message, true,
new PK_EncryptorFilter(rng, e,
new StringSink(encrypted_data)
) // PK_EncryptorFilter
); // StringSource
return encrypted_data;
}
string rsa_decrypt(const string &message, const string &private_key,bool b_Base64decode)
{
string keystr=to_BER(private_key,true);
string decoded=message;
if(b_Base64decode)
{
Base64Decoder decoder;
decoder.Put( (byte*)message.data(), message.size() );
decoder.MessageEnd();
word64 size = decoder.MaxRetrievable();
if(size && size <= SIZE_MAX)
{
decoded.resize(size);
decoder.Get((byte*)decoded.data(), decoded.size());
}
}
RSAES_PKCS1v15_Decryptor pri( rng, 1024 );
TransparentFilter privSink( new StringSink(keystr) );
pri.DEREncode( privSink );
privSink.MessageEnd();
string decrypted_data;
try
{
StringSource ss( decoded, true,
new PK_DecryptorFilter( rng, pri, new StringSink( decrypted_data )));
}
catch(Exception *e)
{
printf("ERROR DECRYPTING RSA\n");
return "";
}
return decrypted_data;
}
Then I use this code:
string enc=rsa_encrypt("hola mundo","-----BEGIN PUBLIC KEY----- MIGeMA0GCSqGSIb3DQEBAQUAA4GMADCBiAKBgHIiecdmRAaiTrCbbEOLBPh+fxp2KyGWyMTeWIj56NRk1CFLfBDT6vOWDceFpuTcrAFGR4Np4JLsssqM24F7bZCKv6eQCV8Xjv6GJiGofKEkI4b0zwHHEwq63p+9Rb5jYXlPZ7JIud7Yi96CHbg3foLQzZSSS9oFItGOFF0jDM7lAgMBAAE= -----END PUBLIC KEY-----");
string aaa=rsa_decrypt(
/*"YTbXg1K4OlXGY6eaLuVTFZrN4qi1zg83h0PjeLd9F3Ge3gKUHsJpoE3iLv1+Gj/PepM8ehiilb5kphxCdcELjaYH9wwYHMpUZUQSRLQUTnofOboI6nfHaKnnNV28QMguM39q1hm7X1wNc69D8R+CkWLka2rQof+BXt+41ivnMj8=",*/
enc
,"-----BEGIN RSA PRIVATE KEY----- MIICWwIBAAKBgHIiecdmRAaiTrCbbEOLBPh+fxp2KyGWyMTeWIj56NRk1CFLfBDT6vOWDceFpuTcrAFGR4Np4JLsssqM24F7bZCKv6eQCV8Xjv6GJiGofKEkI4b0zwHHEwq63p+9Rb5jYXlPZ7JIud7Yi96CHbg3foLQzZSSS9oFItGOFF0jDM7lAgMBAAECgYBxFL9bVHNprz4PtK3bbc2K9qmv6gxpxx88Dp/hdtm8NfoG0uclNRHALZeRa1Yjwo+Y46zPAwPCDkpGbLC+5S9zfBjtrx/+8zjTyMVb2CcGLfR0H2E/hcCjADXNxs+fmpB3+jyPhgH5ANaTmAXqGXOP56I0Fqo8xCfU/zQELCtzMQJBAN2Kq+9bQW3nKAAJEZqWQlAEjuBQfe1lrvLxc/AgVl9XLWrHre7HSlkyqcFemvqhzlZy1wz0Nv5VpOIGcAKefEMCQQCD4xSbkF1kzZyj8k6P3iUW6ezaK4krOZnpq/wDyOtj0DBAtLt3apyv3BUbe7AH1e8llJ5a8UYVHlsOdRUio+m3AkAB0LYR8wR5OxCn12sughavAyqMifxOKqwhT3sst4cdpuA3ZMV3FGj2jCS58eWBMjw3lx9N+t5MfTUpqPXX/6ZzAkBx9eTXqv3YXYZtb7GMxQI9c3Jy7k1/aS1iaXbA+nrTa5BWSRT30cqEduJSNiVcD/KuAWZ35KWPGATMUEUsAoCvAkEAuVg0OA6L3xJphKEbVYXvTwXrXcoVjjLDnNYjUJuNWrFFjBuNE4whWvdg76Panw3vMhgFc6yVr+VE5XOc7rXPvA== -----END RSA PRIVATE KEY-----"
,false
);
The problem is that rsa_decrypt fails with a "invalid ciphertext" exception that isn't caught with the try/catch code. The program crashes.
I would like to know what is wrong with my code and how can I avoid the program to crash even if the ciphertext is wrong.
I have found similar threads (crypto++ RSA and "invalid ciphertext") but none of the solutions is valid for me: I don't see any problem with a 0 at the end of the string, I check that the keys are valid, I know that private and public keys are from the same key pair and they work with other RSA libraries.
This is the line were it crashes:
StringSource ss( decoded, true,
new PK_DecryptorFilter( rng, pri, new StringSink( decrypted_data )));

The problem is that rsa_decrypt fails with a "invalid ciphertext" exception
You create a new private key, and then encode it into keystr:
RSAES_PKCS1v15_Decryptor pri( rng, 1024 );
TransparentFilter privSink( new StringSink(keystr) );
pri.DEREncode( privSink );
Instead, you should use private_key (and not the new key) and do something like you did in the encryptor:
string keystr=to_BER(private_key,true);
ByteQueue queue;
Base64Decoder decoder;
decoder.Attach(new Redirector(queue));
decoder.Put((const byte*)keystr.data(), keystr.length());
decoder.MessageEnd();
RSAES_PKCS1v15_Decryptor d;
d.AccessKey().Load(queue);
... that isn't caught...
Wrap everything in a try/catch block, and not just the PK_DecryptorFilter call.
You can see the difference (or lackof) between the public and private keys with something like:
try {
string s1("-----BEGIN PUBLIC KEY-----\n"
"MIGeMA0GCSqGSIb3DQEBAQUAA4GMADCBiAKBgHIiecdmRAai"
"TrCbbEOLBPh+fxp2KyGWyMTeWIj56NRk1CFLfBDT6vOWDceF"
"puTcrAFGR4Np4JLsssqM24F7bZCKv6eQCV8Xjv6GJiGofKEk"
"I4b0zwHHEwq63p+9Rb5jYXlPZ7JIud7Yi96CHbg3foLQzZSS"
"S9oFItGOFF0jDM7lAgMBAAE="
"\n-----END PUBLIC KEY-----");
string s2("-----BEGIN RSA PRIVATE KEY-----\n"
"MIICWwIBAAKBgHIiecdmRAaiTrCbbEOLBPh+fxp2KyGWyMTeWIj"
"56NRk1CFLfBDT6vOWDceFpuTcrAFGR4Np4JLsssqM24F7bZCKv6"
"eQCV8Xjv6GJiGofKEkI4b0zwHHEwq63p+9Rb5jYXlPZ7JIud7Yi"
"96CHbg3foLQzZSSS9oFItGOFF0jDM7lAgMBAAECgYBxFL9bVHNp"
"rz4PtK3bbc2K9qmv6gxpxx88Dp/hdtm8NfoG0uclNRHALZeRa1Y"
"jwo+Y46zPAwPCDkpGbLC+5S9zfBjtrx/+8zjTyMVb2CcGLfR0H2"
"E/hcCjADXNxs+fmpB3+jyPhgH5ANaTmAXqGXOP56I0Fqo8xCfU/"
"zQELCtzMQJBAN2Kq+9bQW3nKAAJEZqWQlAEjuBQfe1lrvLxc/Ag"
"Vl9XLWrHre7HSlkyqcFemvqhzlZy1wz0Nv5VpOIGcAKefEMCQQC"
"D4xSbkF1kzZyj8k6P3iUW6ezaK4krOZnpq/wDyOtj0DBAtLt3ap"
"yv3BUbe7AH1e8llJ5a8UYVHlsOdRUio+m3AkAB0LYR8wR5OxCn1"
"2sughavAyqMifxOKqwhT3sst4cdpuA3ZMV3FGj2jCS58eWBMjw3"
"lx9N+t5MfTUpqPXX/6ZzAkBx9eTXqv3YXYZtb7GMxQI9c3Jy7k1"
"/aS1iaXbA+nrTa5BWSRT30cqEduJSNiVcD/KuAWZ35KWPGATMUE"
"UsAoCvAkEAuVg0OA6L3xJphKEbVYXvTwXrXcoVjjLDnNYjUJuNW"
"rFFjBuNE4whWvdg76Panw3vMhgFc6yVr+VE5XOc7rXPvA=="
"\n-----END RSA PRIVATE KEY-----");
ArraySource as1(s1, true), as2(s2, true);
RSA::PublicKey k1;
RSA::PrivateKey k2;
PEM_Load(as1, k1);
PEM_Load(as2, k2);
AutoSeededRandomPool prng;
k1.ThrowIfInvalid(prng, 3);
k2.ThrowIfInvalid(prng, 3);
Integer i1 = k1.GetModulus() - k2.GetModulus();
Integer i2 = k1.GetPublicExponent() - k2.GetPublicExponent();
cout << i1 << " " << i2 << endl;
} catch (const Exception& ex) {
cerr << ex.what() << endl;
}
Related, the encapsulation boundaries (-----BEGIN PUBLIC KEY----- and -----END PUBLIC KEY-----) should be on their own lines. Instead of a space, you should use a new line (\n). So something like:
string enc=rsa_encrypt(
"hola mundo",
"-----BEGIN PUBLIC KEY-----\nMIGeMA0G...gMBAAE=\n-----END PUBLIC KEY-----"
);
And:
string aaa=rsa_decrypt(
enc,
"-----BEGIN RSA PRIVATE KEY-----\nMIICWwIB...7rXPvA==\n-----END RSA PRIVATE KEY-----",
false
);
Related, you should probably use RSAES_OAEP_SHA_Encryptor and RSAES_OAEP_SHA_Decryptor. An aaproachable treatment is available at A bad couple of years for the cryptographic token industry.

Related

How to create a secp256k1 key pair with OpenSSL?

Since the EC_KEY functions are marked deprecated in OpenSSL 3 I tried to do it with EVP_PKEY functions.
Here is my OpenSSL 1.1 code:
void MainWindowOpenSsl::createKeyPair()
{
EC_KEY *key_pair_obj = nullptr;;
BIGNUM *priv_key;
EC_POINT *pub_key;
EC_GROUP *secp256k1_group;
char *priv_key_char;
char *pub_key_char;
char *pub_key_char_comp;
int ret_error;
// Generate secp256k1 key pair
key_pair_obj = EC_KEY_new_by_curve_name(NID_secp256k1);
ret_error = EC_KEY_generate_key(key_pair_obj);
// Get private key
priv_key = (BIGNUM *)EC_KEY_get0_private_key(key_pair_obj);
priv_key_char = BN_bn2hex(priv_key);
// Get public key
pub_key = (EC_POINT *)EC_KEY_get0_public_key(key_pair_obj);
secp256k1_group = EC_GROUP_new_by_curve_name(NID_secp256k1);
// Uncompressed
pub_key_char = EC_POINT_point2hex(secp256k1_group, pub_key, POINT_CONVERSION_UNCOMPRESSED, nullptr);
// Compressed
pub_key_char_comp = EC_POINT_point2hex(secp256k1_group, pub_key, POINT_CONVERSION_COMPRESSED, nullptr);
EC_GROUP_free(secp256k1_group);
qDebug().noquote() << "\n";
qDebug().noquote() << Q_FUNC_INFO;
printf("Private key : %s\n\n", priv_key_char);
printf("Public key ompressed : %s\n\n", pub_key_char);
printf("Public key uncompressed: %s\n", pub_key_char_comp);
printf("Error code : %d\n", ret_error);
}
With the EVP_PKEY functions I managed to get a private key, but I couldn't figure out how to extract the public key from it. That is how I started:
void MainWindowOpenSsl::createKeyPairNew()
{
char *priv_key_char;
// Private Key
EVP_PKEY *evp_pkey = EVP_EC_gen("secp256k1");
BIGNUM *bignum_priv_key = nullptr;
EVP_PKEY_get_bn_param(evp_pkey, OSSL_PKEY_PARAM_PRIV_KEY, &bignum_priv_key);
priv_key_char = BN_bn2hex(bignum_priv_key);
printf("Private key : %s\n\n", priv_key_char);
// Extract the compressed and uncompressed public key from the private key
}
but then I got stuck.
Thank you very much for your help.

Issues reimplementing some C# encryption stuff in C++ using cryptopp

So I have this piece of C# code:
void Decrypt(Stream input, Stream output, string password, int bufferSize) {
using (var algorithm = Aes.Create()) {
var IV = new byte[16];
input.Read(IV, 0, 16);
algorithm.IV = IV;
var key = new Rfc2898DeriveBytes(password, algorithm.IV, 100);
algorithm.Key = key.GetBytes(16);
using(var decryptor = algorithm.CreateDecryptor())
using(var cryptoStream = new CryptoStream(input, decryptor, CryptoStreamMode.Read)) {
CopyStream(cryptoStream, output, bufferSize);
}
}
}
and I am trying to translate this into C++ with CryptoPP.
So this is what I have written:
void decrypt(std::ifstream& in_file, std::ofstream& out_file, std::string_view password, size_t bufSize) {
using namespace CryptoPP;
// Get IV
byte iv[16];
in_file.read(reinterpret_cast<char*>(iv), sizeof(iv));
// Read cypher
std::string cypher;
while (in_file && cypher.size() != bufSize) {
char c;
in_file.read(&c, 1);
cypher.push_back(c);
}
// Get key
byte key[16];
PKCS5_PBKDF2_HMAC<SHA1> pbkdf2;
pbkdf2.DeriveKey(key, sizeof(key), 0, reinterpret_cast<const byte*>(password.data()), password.size(), iv, sizeof(iv), 100);
// Decrypt
CTR_Mode<AES>::Decryption decrypt(key, sizeof(key), iv);
std::string output;
StringSource(cypher, true, new StreamTransformationFilter(decrypt, new StringSink(output)));
// Write output to file
out_file.write(output.data(), output.size());
}
However, from this function, I am only getting back trash data. What could I be doing wrong?
Thanks
Tuxifan!
So I found the solution! First of all, as #mbd mentioned, C# uses CBC by default. Additionally, I need to cut away the rest of the data like this:
while ((cipher.size() % 16) != 0) {
cipher.pop_back();
}

HMAC SHA256 sign on Java, Verify on C++ private-public keys

I try to sign some data by Java with private key and then verify it by C++ with public key. I user Java as client and C++ as server.
Java run on Windows, C++ on Ubuntu
in Java I use
key = "MIIEowIBAAKCAQ......s8mFoA2"; //private key
byte[] b1 = Base64.decodeBase64(key);
this.Sign = hmacSha256Base64("test", b1);
/**************/
public static String hmacSha256Base64(String message, byte[] secretKey) throws
NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException, NoSuchProviderException {
Mac hmacSha256;
try {
hmacSha256 = Mac.getInstance("HmacSHA256", "BC");
} catch (NoSuchAlgorithmException nsae) {
hmacSha256 = Mac.getInstance("HMAC-SHA-256");
}
SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey, "HmacSHA256");
hmacSha256.init(secretKeySpec);
// Build and return signature
return Base64.encodeBase64String(hmacSha256.doFinal(message.getBytes("UTF-8")));
}
and on C++, to verify I real try different code, for example:
int verify_it(const unsigned char *msg, size_t mlen, const unsigned char *val, size_t vlen, EVP_PKEY *pkey)
{
/* Returned to caller */
int result = 0;
EVP_MD_CTX* ctx = NULL;
unsigned char buff[EVP_MAX_MD_SIZE];
size_t size;
int rc;
if (!msg || !mlen || !val || !vlen || !pkey)
return 0;
ctx = EVP_MD_CTX_new();
if (ctx == NULL) {
printf("EVP_MD_CTX_create failed, error 0x%lx\n", ERR_get_error());
goto err;
}
rc = EVP_DigestSignInit(ctx, NULL, EVP_sha256(), NULL, pkey);
if (rc != 1) {
printf("EVP_DigestSignInit failed, error 0x%lx\n", ERR_get_error());
goto err;
}
rc = EVP_DigestSignUpdate(ctx, msg, mlen);
if (rc != 1) {
printf("EVP_DigestSignUpdate failed, error 0x%lx\n", ERR_get_error());
goto err;
}
size = sizeof(buff);
rc = EVP_DigestSignFinal(ctx, buff, &size);
if (rc != 1) {
printf("EVP_DigestSignFinal failed, error 0x%lx\n", ERR_get_error());
goto err;
}
result = (vlen == size) && (CRYPTO_memcmp(val, buff, size) == 0);
err:
EVP_MD_CTX_free(ctx);
return result;
}
RSA* createPublicRSA(std::string TermId, bool is_local) {
RSA *rsa = NULL;
BIO *keybio;
FILE * fp = fopen((SettingsConfig["UserKeys"] + "user_public/" + TermId).c_str(), "rb");
if (fp != 0)
{
rsa = PEM_read_RSA_PUBKEY(fp, &rsa, NULL, NULL);
fclose(fp);
}
return rsa;
}
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);
}
std::string test = "XChhsTE....NkE="; //Sign from Java
std::string msg = "test";
RSA* publicRSA = createPublicRSA("#1.pem", false); //public key
EVP_PKEY* pubKey = EVP_PKEY_new();
EVP_PKEY_assign_RSA(pubKey, publicRSA);
unsigned char* encMessage;
size_t encMessageLength;
Base64Decode(test.c_str(), &encMessage, &encMessageLength);
int result_sign = verify_it((unsigned char*)msg.c_str(), msg.length(), encMessage, encMessageLength, pubKey);
std::cout << std::to_string(result_sign) << std::endl; //return 0
And any others examples return false. I don't know what is problem, please help! Thanks!
p.s. private key:
MIIEowIBAAKCAQEAra2jau89VIfcunyOth5O08EZqFVSgVzk9Tv0ELG+zH89D/s0DMLSkACXUSYq2EFRXUS05doajB55ZVoD2qYiUjJPrZDnPS+H3f/9tqRf+o2bbb4DWRd9MJbMt2E2Q8auIN3M49XvlQnZ2+dSvplLepYv6H+fbILBsYfQUxh4RX5B+qvk1JdbMh1rhgLV6y9/lYkF3UlL8W5EBA2A1YQvgrwl/nBjXTTk3PVv+OmWGFRFE0BGuf7oYEuoX86732gAtLkImqLNeNNhgUVVhFiDUOOyWjybxH9UiH28eYBZqzJlyY9D3xeC3ZUkTvfJOURK5t8vagS/t8Vu3xsMHWQ7DwIDAQABAoIBAHbNlkGp0Uwne6fdWEnfxZA4QPLTGpL/FmdiUXux+pAsYXqzHVG1Ww/CN7/82cYAOEYSn6OzZAGBPw1DW+uPRV7wp2xU+Ljz8H69g7ISEs1zXGTfW67v0GUSYor2ZoZKPAajcmpPh4ltqacxP3q9pdH/NlpWIpm5gAGOo8STsoHl0PItHpxYbWXRylzWIgysalYPRERicT/ibQlJ4w8jhdk1lqYZAyEg2trJXDXxiNGx19OxEfRoqDVumK+W7Pn38ye9zgjuR8TYRAMPJ1WcQ9HZPLZKbVBvjztLSvUk/Q+Z8PoomIN9s+Ggev1y6+ccOiRWpPQLp45483k5fHHXTpECgYEA4KJsRwGTw3yomIAN+k0eFSL/+bJJBimQXjRcc0qw+NbeLoytfVrnSCBD85QYamcB8tMg+CvcCdJve46ByOsmYN6jXLdUmai4Nt/kJfUU6bWpPwBdtUOGKb9mYH4xLGnnJqyUhCJ+vhY6WrOUBXu1KfkQZUEc/r/EWyEo09UNsCsCgYEAxe3IQ2tXI1zJ91xu0R309eToH/ZhfVSKR6UBoCKptEweBBPlCj8tQcKS6Ao9Oyk28rSoAYM8thy1V9+XItku97L+fP1JSZMkGq3g/r4DHuklshDoR3xAYOSZ6/89BxsV0O9a92bb0CV472wM7HHH0KAMtODwRqw8IpC5qlMHiq0CgYEAxYTMJJt0bF3+eSmQINkSbI97+PkVUL/XW5469H1mo0d70f6Mxj7aQwdr+I/t8BFnGzceNFmMf25z7HbgE+UAuAjMKEhjsUEzybyQhfe8TcwYZ3dQ7oPTQn4z7QDJCD6Oq+jwJkeWnlo5MWvZ6gBeyetgyUe50R6Z72920NzzzkUCgYBeY/V7YXde3+NZWfVnONgXZCDnDUKU2HpRjHln+t/foeU2oJ478sEMeVRB4JAu5IrV2B2/Cu0rFCnPTEvxTI2/htcimFAZDFjNeFqyYb9vQFS/xJxhavnwu1REXaam+t2+lEdXcPAnJZe05lyLbf+SmKE2qYcszPqoqUhB1/LiyQKBgGDXVyw05oSvR9GGKfMKIghRimeF97+EZhS718zcuDqXJ8Qmn+S+qrrwvn1X7TZbZ3bnM6JSnC5FcgLLVTWulLShjIo2ctsqaZUPnUJTBPoCJMkmGCR8H6XaVuFlfElT/jXglqwS+UkMMM2WPkDubnLzuTuslH1DJnrBBs8mFoA2
public key:
-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAra2jau89VIfcunyOth5O
08EZqFVSgVzk9Tv0ELG+zH89D/s0DMLSkACXUSYq2EFRXUS05doajB55ZVoD2qYi
UjJPrZDnPS+H3f/9tqRf+o2bbb4DWRd9MJbMt2E2Q8auIN3M49XvlQnZ2+dSvplL
epYv6H+fbILBsYfQUxh4RX5B+qvk1JdbMh1rhgLV6y9/lYkF3UlL8W5EBA2A1YQv
grwl/nBjXTTk3PVv+OmWGFRFE0BGuf7oYEuoX86732gAtLkImqLNeNNhgUVVhFiD
UOOyWjybxH9UiH28eYBZqzJlyY9D3xeC3ZUkTvfJOURK5t8vagS/t8Vu3xsMHWQ7
DwIDAQAB
-----END PUBLIC KEY-----
message: 12105333071
signaturee from Java: XChhsTE+Yr4wkiibvTFiLTMhJ8tLqYo7WQs///VtNkE=
Just using HMACSHA256 is not the same as Private/Public Key signature. The full name of HMACSHA256 is "Hash-based Message Authentication Code" and you "sign" and "verify" this with the same "key" that is just a byte array and has nothing to do with Private or Public Key.
Of course you can take the encoded bytes of the Private/Public key as input, but when doing so (I do NOT recommend this)
you need to pass the same key to the verification part.
I setup two small programs to show how it works. For Java I'm using your code except of using Bouncy Castle as "native" Java
should have this build in. As well I left out the apache-Base64-conversion as it's build in as well. The C#-part is the same program but has a "verification" output.
Both code samples do not have any exceptional handling and are for educational purposes only.
Result of Java-code:
HMAC SHA256 sign on Java, Verify on C++ private-public keys
hmacSha256 (Base64): /1qkanJi8onWOxVe02MO/Wf1922aKzSTSfJk6E7o1x0=
Result of C#-code:
HMAC SHA256 sign on Java, Verify on C++ private-public keys
HMACSHA256 in C#: /1qkanJi8onWOxVe02MO/Wf1922aKzSTSfJk6E7o1x0=
HMACSHA256 Java : /1qkanJi8onWOxVe02MO/Wf1922aKzSTSfJk6E7o1x0=
Hashes are equal: True
Java-code:
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
public class Org {
public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException {
System.out.println("HMAC SHA256 sign on Java, Verify on C++ private-public keys");
String message = "12105333071";
String key = "12345678901234567";
String result = hmacSha256Base64(message, key.getBytes(StandardCharsets.UTF_8));
System.out.println("hmacSha256 (Base64): " + result);
}
public static String hmacSha256Base64(String message, byte[] secretKey) throws
NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException {
Mac hmacSha256;
try {
hmacSha256 = Mac.getInstance("HmacSHA256");
} catch (NoSuchAlgorithmException nsae) {
hmacSha256 = Mac.getInstance("HMAC-SHA-256");
}
SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey, "HmacSHA256");
hmacSha256.init(secretKeySpec);
// Build and return signature
return Base64.getEncoder().encodeToString(hmacSha256.doFinal(message.getBytes("UTF-8")));
}
}
C#-code:
using System;
using System.Text;
using System.Security.Cryptography;
class Program
{
static void Main(string[] args)
{
Console.WriteLine("HMAC SHA256 sign on Java, Verify on C++ private-public keys");
string message = "12105333071";
string key = "12345678901234567";
string expectedHashBase64 = "/1qkanJi8onWOxVe02MO/Wf1922aKzSTSfJk6E7o1x0="; // from Java
// generate HMACSHA256
string hmacSha256DigestBase64 = HmacSha256DigestBase64(key, message);
Console.WriteLine("HMACSHA256 in C#: " + hmacSha256DigestBase64);
Console.WriteLine("HMACSHA256 Java : " + expectedHashBase64);
Console.WriteLine("Hashes are equal: " + hmacSha256DigestBase64.Equals(expectedHashBase64, StringComparison.OrdinalIgnoreCase));
//Console.ReadLine();
}
private static string HmacSha256DigestBase64(string secret, string message)
{
ASCIIEncoding encoding = new ASCIIEncoding();
byte[] keyBytes = encoding.GetBytes(secret);
byte[] messageBytes = encoding.GetBytes(message);
System.Security.Cryptography.HMACSHA256 cryptographer = new System.Security.Cryptography.HMACSHA256(keyBytes);
byte[] bytes = cryptographer.ComputeHash(messageBytes);
return Convert.ToBase64String(bytes);
}
}
Golang code to complete the collection (tested to produce the exactly same result as the java code form Michael Fehr:
package main
import (
"crypto/hmac"
"crypto/sha256"
"fmt"
b64 "encoding/base64"
)
func main() {
secret := "12345678901234567"
data := "12105333071"
fmt.Printf("Secret: %s Data: %s\n", secret, data)
// Create a new HMAC by defining the hash type and the key (as byte array)
h := hmac.New(sha256.New, []byte(secret))
// Write Data to it
h.Write([]byte(data))
// Get result and base64 encode the string
sha := b64.StdEncoding.EncodeToString(h.Sum(nil))
fmt.Println("Result: " + sha)
}

AES IV into file with FileSource and FileSink

I have the need to crypt big files (multi GB) with crypto++. I managed to find an example on the documentation that helped me create the 2 followings functions :
bool AESEncryptFile(const std::string& clearfile, const std::string& encfile, const std::string& key) {
try {
byte iv[CryptoPP::AES::BLOCKSIZE] = {};
CryptoPP::CBC_Mode<CryptoPP::AES>::Encryption encryptor;
encryptor.SetKeyWithIV((unsigned char*)key.c_str(), CryptoPP::AES::DEFAULT_KEYLENGTH, iv);
CryptoPP::StreamTransformationFilter filter(encryptor);
CryptoPP::FileSource source(clearfile.c_str(), false);
CryptoPP::FileSink sink(encfile.c_str());
source.Attach(new CryptoPP::Redirector(filter));
filter.Attach(new CryptoPP::Redirector(sink));
const CryptoPP::word64 BLOCK_SIZE = 4096;
CryptoPP::word64 processed = 0;
while (!EndOfFile(source) && !source.SourceExhausted()) {
source.Pump(BLOCK_SIZE);
filter.Flush(false);
processed += BLOCK_SIZE;
}
filter.MessageEnd();
return true;
} catch (const CryptoPP::Exception& ex) {
return false;
}
}
bool AESDecryptFile(const std::string& encfile, const std::string& clearfile, const std::string& key) {
try {
byte iv[CryptoPP::AES::BLOCKSIZE] = {};
CryptoPP::CBC_Mode<CryptoPP::AES>::Decryption decryptor;
decryptor.SetKeyWithIV((unsigned char*)key.c_str(), CryptoPP::AES::DEFAULT_KEYLENGTH, iv);
CryptoPP::StreamTransformationFilter filter(decryptor);
CryptoPP::FileSource source(encfile.c_str(), false);
CryptoPP::FileSink sink(clearfile.c_str());
source.Attach(new CryptoPP::Redirector(filter));
filter.Attach(new CryptoPP::Redirector(sink));
const CryptoPP::word64 BLOCK_SIZE = 4096;
CryptoPP::word64 processed = 0;
while (!EndOfFile(source) && !source.SourceExhausted()) {
source.Pump(BLOCK_SIZE);
filter.Flush(false);
processed += BLOCK_SIZE;
}
.
filter.MessageEnd();
return true;
} catch (const CryptoPP::Exception& ex) {
return false;
}
}
This is working great. On 8 GB files i'm using very little memory.
But as you can see the IV is (empty for now) hardcoded and i would like to :
While encrypting , put it a the end of the file.
While decrypting : get the IV from the file to init the decryptor.
Is there a way to do that with crypto++ or should i handle it manually after/before the enc/decryption process ?
Thanks to all the differents comments here is what i managed to do. As suggested by #Sam Mason i put the iv at the beginning of the file :
So before starting to encrypt i 'm putting the iv at the beginning of the file:
CryptoPP::ArraySource(iv, sizeof(iv), true,
new CryptoPP::Redirector(sink)
);
// Encrypt
And then when decrypting i'm getting the IV back like this :
unsigned char iv[CryptoPP::AES::BLOCKSIZE];
CryptoPP::ArraySink ivSink(iv, sizeof(iv));
source.Attach(new CryptoPP::Redirector(ivSink));
source.Pump(CryptoPP::AES::BLOCKSIZE);
// Decrypt
Note for future reader : Don't use an empty IV like show in my OP , instead generate one randomly , for example :
CryptoPP::AutoSeededRandomPool prng;
unsigned char iv[CryptoPP::AES::BLOCKSIZE];
prng.GenerateBlock(iv, sizeof(iv));

AES 256 encryption in C++ and Qt 5

I have a Java code for encryption in place as follows!
private static byte[] encrypt(byte[] raw, byte[] clear) throws
Exception {
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = null;
if(isIVUsedForCrypto) {
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(IV));
}
else
{
cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
}
byte[] encrypted = cipher.doFinal(clear);
return encrypted;
}
public static byte[] toByte(String hexString) {
int len = hexString.length()/2;
byte[] result = new byte[len];
try{
for (int i = 0; i < len; i++) {
result[i] = Integer.valueOf(hexString.substring(2*i, 2*i+2),16).byteValue();
}
}catch (Exception e) {
}
return result;
}
public static String toHex(byte[] buf) {
if (buf == null)
return "";
StringBuffer result = new StringBuffer(2*buf.length);
for (int i = 0; i < buf.length; i++) {
appendHex(result, buf[i]);
}
return result.toString();
}
private final static String HEX = "0123456789ABCDEF";
private static void appendHex(StringBuffer sb, byte b) {
sb.append(HEX.charAt((b>>4)&0x0f)).append(HEX.charAt(b&0x0f));
}
From Java main method:
byte[] result = encrypt(toByte(rawKey), plaintext.getBytes());
I need to write the C++ equivalent for the above methods (in java). I am not aware of the C++ classes for Cryptography and would like someone to please provide an example showing the same.
Thanks in advance
EDIT
My raw key would be in hexadecimal like -> 729308A8E815F6A46EB3A8AE6D5463CA7B64A0E2E11BC26A68106FC7697E727E
and my final encrypted password is --> 812DCE870D82E93DB62CDA66AAF37FB2
This works in Java but I need a similar solution for C++
Try this:
#include <crypto++/aes.h>
#include <crypto++/modes.h>
#include <crypto++/filters.h>
#include <crypto++/hex.h>
#include <crypto++/sha.h>
#include <crypto++/md5.h>
QString Foo::decrypt(const QString &password)
{
string plain;
string encrypted = password.toStdString();
// Hex decode symmetric key:
HexDecoder decoder;
decoder.Put( (byte *)PRIVATE_KEY,32*2 );
decoder.MessageEnd();
word64 size = decoder.MaxRetrievable();
char *decodedKey = new char[size];
decoder.Get((byte *)decodedKey, size);
// Generate Cipher, Key, and CBC
byte key[ AES::MAX_KEYLENGTH ], iv[ AES::BLOCKSIZE ];
StringSource( reinterpret_cast<const char *>(decodedKey), true,
new HashFilter(*(new SHA256), new ArraySink(key, AES::MAX_KEYLENGTH)) );
memset( iv, 0x00, AES::BLOCKSIZE );
try {
CBC_Mode<AES>::Decryption Decryptor
( key, sizeof(key), iv );
StringSource( encrypted, true,
new HexDecoder(new StreamTransformationFilter( Decryptor,
new StringSink( plain ) ) ) );
}
catch (Exception &e) { // ...
}
catch (...) { // ...
}
return QString::fromStdString(plain);
}
QString Foo::encrypt(const QString &password)
{
string plain = password.toStdString();
string ciphertext;
// Hex decode symmetric key:
HexDecoder decoder;
decoder.Put( (byte *)PRIVATE_KEY, 32*2 );
decoder.MessageEnd();
word64 size = decoder.MaxRetrievable();
char *decodedKey = new char[size];
decoder.Get((byte *)decodedKey, size);
// Generate Cipher, Key, and CBC
byte key[ AES::MAX_KEYLENGTH ], iv[ AES::BLOCKSIZE ];
StringSource( reinterpret_cast<const char *>(decodedKey), true,
new HashFilter(*(new SHA256), new ArraySink(key, AES::MAX_KEYLENGTH)) );
memset( iv, 0x00, AES::BLOCKSIZE );
CBC_Mode<AES>::Encryption Encryptor( key, sizeof(key), iv );
StringSource( plain, true, new StreamTransformationFilter( Encryptor,
new HexEncoder(new StringSink( ciphertext ) ) ) );
return QString::fromStdString(ciphertext);
}
Update:
Use above code as follows:
//...
#define PRIVATE_KEY "729308A8E815F6A46EB3A8AE6D5463CA7B64A0E2E11BC26A68106FC7697E727E37011"
QString encrypted = Foo::encryptPassword("test");
// use encrypted
Personally I don't like to reveal private key in source code. So I'll pass it to compiler in command line:
g++ -DPRIVATE_KEY \"\"\"123...\"\"\" ...
Where PRIVATE_KEY is your private key in plain text. If you have the key in HEX encode, just remove Hex decode symmetric key step.