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));
Related
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();
}
I'm trying to use the Botan library to generate a detached signature file. The resulting signature file is not validated by OpenSSL (no other checks). Prompt in what there can be an error of formation of the signature file.
A couple of keys for signing and the certificate is stored in the HSM, it was not difficult to get them. For tests I use RSA keys and SoftHSM, later another key format and physical HSM will be used. PKCS#11 is used to communicate with HSM.
For create PKCS#7:
static const Botan::BigInt CMSVersion(1ull);
std::vector<uint8_t> createAttributes(std::vector<uint8_t> &digestData)
{
std::chrono::time_point<std::chrono::system_clock> time = std::chrono::system_clock::now();
Botan::OID dataOID("1.2.840.113549.1.7.1");
Botan::Attribute contentType(Botan::OIDS::str2oid("PKCS9.ContentType"),
dataOID.BER_encode());
Botan::X509_Time timeASN1(time);
std::vector<uint8_t> attributesData;
Botan::DER_Encoder attrib(attributesData);
attrib.start_cons(Botan::ASN1_Tag(0),
Botan::ASN1_Tag(Botan::ASN1_Tag::CONTEXT_SPECIFIC));
attrib.encode(contentType)
.start_cons(Botan::ASN1_Tag::SEQUENCE)
.encode(Botan::OID("1.2.840.113549.1.9.5"))
.start_cons(Botan::ASN1_Tag::SET).encode(timeASN1).end_cons()
.end_cons()
.start_cons(Botan::ASN1_Tag::SEQUENCE)
.encode(Botan::OIDS::str2oid("PKCS9.MessageDigest"))
.start_cons(Botan::ASN1_Tag::SET)
.encode(digestData, Botan::ASN1_Tag::OCTET_STRING,
Botan::ASN1_Tag::OCTET_STRING, Botan::ASN1_Tag::UNIVERSAL)
.end_cons()
.end_cons();
attrib.end_cons();
return attributesData;
}
std::vector<uint8_t> createCMS(const Botan::AlgorithmIdentifier &digestAlg,
Botan::X509_Certificate &cert,
const Botan::AlgorithmIdentifier &keyAlg,
std::vector<uint8_t> &sigData,
std::vector<uint8_t> &signedAttributes)
{
Botan::secure_vector<uint8_t> msgData;
Botan::DER_Encoder encoder(msgData);
encoder.start_cons(Botan::ASN1_Tag::SEQUENCE).encode(CMSVersion)
.start_cons(Botan::ASN1_Tag::SET).start_cons(Botan::ASN1_Tag::SEQUENCE)
.encode(digestAlg.get_oid()).end_cons().end_cons();
Botan::OID dataOID("1.2.840.113549.1.7.1");
encoder.start_cons(Botan::ASN1_Tag::SEQUENCE).encode(dataOID).end_cons();
encoder.start_cons(Botan::ASN1_Tag::UNIVERSAL, Botan::ASN1_Tag::PRIVATE)
.encode(cert).end_cons();
encoder.start_cons(Botan::ASN1_Tag::SET);
Botan::secure_vector<uint8_t> signerInfoData;
Botan::DER_Encoder signerInfo(signerInfoData);
signerInfo.start_cons(Botan::ASN1_Tag::SEQUENCE);
signerInfo.encode(CMSVersion);
signerInfo.start_cons(Botan::ASN1_Tag::SEQUENCE)
.encode(cert.issuer_dn())
.encode(Botan::BigInt(cert.serial_number())).end_cons();
signerInfo.start_cons(Botan::ASN1_Tag::SEQUENCE).encode(digestAlg.get_oid())
.end_cons();
signerInfo.raw_bytes(signedAttributes);
signerInfo.encode(keyAlg)
.encode(sigData, Botan::ASN1_Tag::OCTET_STRING,
Botan::ASN1_Tag::OCTET_STRING, Botan::ASN1_Tag::UNIVERSAL);
signerInfo.end_cons();
encoder.raw_bytes(signerInfoData).end_cons().end_cons();
std::vector<uint8_t> resulData;
Botan::DER_Encoder result(resulData);
result.start_cons(Botan::ASN1_Tag::SEQUENCE)
.encode(Botan::OID("1.2.840.113549.1.7.2"))
.start_cons(Botan::ASN1_Tag::UNIVERSAL, Botan::ASN1_Tag::PRIVATE)
.raw_bytes(msgData).end_cons().end_cons();
return resulData;
}
To calculate the hash and signature using PKCS#11, as follows:
QFile input(m_content->text()), output(m_sigFile->text());
if(!input.open(QFile::ReadOnly))
{
QMessageBox::critical(this, tr("Error"),
tr("Content file '%1' not open.\n"
"Error message: %2").arg(m_content->text())
.arg(input.errorString()));
return;
}
Botan::PKCS11::PKCS11_X509_Certificate *cert = nullptr;
Botan::Private_Key *key = nullptr;
// извлечение ключа и сертификата из токена
while(!input.atEnd())
{
static const qint64 maxLen = 1024;
QByteArray data = input.read(maxLen);
(*module)->C_DigestUpdate(session->handle(),
reinterpret_cast<uchar*>(data.data()),
data.size(), &rv);
if(rv != Botan::PKCS11::ReturnValue::OK)
{
QMessageBox::critical(this, tr("Error"),
tr("Digest not run.\nError code: 0x%3")
.arg(static_cast<int>(rv), 0, 16));
delete key;
delete cert;
delete session;
delete slot;
delete module;
return;
}
}
digest.resize(102400);
ulong digestLen;
(*module)->C_DigestFinal(session->handle(), digest.data(), &digestLen, &rv);
if(rv != Botan::PKCS11::ReturnValue::OK)
{
QMessageBox::critical(this, tr("Error"),
tr("Digest not start.\nError code: 0x%3")
.arg(static_cast<int>(rv), 0, 16));
delete key;
delete cert;
delete session;
delete slot;
delete module;
return;
}
digest.resize(digestLen);
{
Botan::PKCS11::PKCS11_RNG rng(*session);
std::unique_ptr<Botan::PK_Ops::Signature> signer =
key->create_signature_op(rng,
"EMSA3(SHA-256)",
"");
signer->update(digest.data(), digest.size());
std::vector<uint8_t> attr = createAttributes(digest);
auto signData = signer->sign(rng);
for(uint8_t i : signData)
signature.push_back(i);
Botan::AlgorithmIdentifier digAlg("SHA-256", {});
auto fileData = createCMS(digAlg, *cert, key->algorithm_identifier(),
signature, attr);
output.write(reinterpret_cast<const char*>(fileData.data()),
fileData.size());
output.close();
}
When checking the received signature file, OpenSSL says
Verification failure
140365848428992:error:04091068:rsa routines:int_rsa_verify:bad signature:../crypto/rsa/rsa_sign.c:220:
140365848428992:error:2E09A09E:CMS routines:CMS_SignerInfo_verify_content:verification failure:../crypto/cms/cms_sd.c:842:
140365848428992:error:2E09D06D:CMS routines:CMS_verify:content verify error:../crypto/cms/cms_smime.c:393:
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.
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.
I am using Crypto++ to encrypt an array of bytes using RSA. I have followed Crypto++ wiki's samples with no luck getting them to work. Encryption and Decryption in all the samples are done within a single process but I am trying to decrypt the content which is already encrypted in another process.
Here is my code:
class FixedRNG : public CryptoPP::RandomNumberGenerator
{
public:
FixedRNG(CryptoPP::BufferedTransformation &source) : m_source(source) {}
void GenerateBlock(byte *output, size_t size)
{
m_source.Get(output, size);
}
private:
CryptoPP::BufferedTransformation &m_source;
};
uint16_t Encrypt()
{
byte *oaepSeed = new byte[2048];
for (int i = 0; i < 2048; i++)
{
oaepSeed[i] = (byte)i;
}
CryptoPP::ByteQueue bq;
bq.Put(oaepSeed, 2048);
FixedRNG prng(bq);
Integer n("Value of N"),
e("11H"),
d("Value of D");
RSA::PrivateKey privKey;
privKey.Initialize(n, e, d);
RSA::PublicKey pubKey(privKey);
CryptoPP::RSAES_OAEP_SHA_Encryptor encryptor( pubKey );
assert( 0 != encryptor.FixedMaxPlaintextLength() );
byte blockSize = encryptor.FixedMaxPlaintextLength();
int divisionCount = fileSize / blockSize;
int proccessedBytes = 0;
// Create cipher text space
uint16_t cipherSize = encryptor.CiphertextLength( blockSize );
assert( 0 != cipherSize );
encryptor.Encrypt(prng, (byte*)plaintext, blockSize, (byte*)output);
return cipherSize;
}
void Decrypt(uint16_t cipherSize)
{
byte *oaepSeed = new byte[2048];
for (int i = 0; i < 2048; i++)
{
oaepSeed[i] = (byte)i;
}
CryptoPP::ByteQueue bq;
bq.Put(oaepSeed, 2048);
FixedRNG prng(bq);
Integer n("Value of N"),
e("11H"),
d("Value of D");
RSA::PrivateKey privKey;
privKey.Initialize(n, e, d);
//RSA::PublicKey pubKey(privKey);
CryptoPP::RSAES_OAEP_SHA_Decryptor decryptor( privKey );
byte blockSize = decryptor.FixedMaxPlaintextLength();
assert(blockSize != 0);
size_t maxPlainTextSize = decryptor.MaxPlaintextLength( cipherSize );
assert( 0 != maxPlainTextSize );
void* subBuffer = malloc(maxPlainTextSize);
CryptoPP::DecodingResult result = decryptor.Decrypt(prng, (byte*)cipherText, cipherSize, (byte*)subBuffer);
assert( result.isValidCoding );
assert( result.messageLength <= maxPlainTextSize );
}
Unfortunately, value of isValidCoding is false. I think I am misunderstanding something about RSA encryption/decryption!!
Note that, privKey and pubKey have been validated using KEY.Validate(prng, 3).
I have also tried to use RAW RSA instead of OAEP and SHA with no luck. I have tried to debug through crypto++ code, what I am suspicious about is prng variable. I think there is something wrong with it. I have also used AutoSeededRandomPool instead of FixedRNG but it didn't help. Worth to know that, if I copy the decryption code right after encryption code and execute it in Encrypt() method, everything is fine and isValidCoding is true!!
This is probably not be correct:
byte blockSize = encryptor.FixedMaxPlaintextLength();
...
encryptor.Encrypt(prng, (byte*)plaintext, blockSize, (byte*)output);
return cipherSize;
Try:
size_t maxLength = encryptor.FixedMaxPlaintextLength();
size_t cipherLength = encryptor.CiphertextLength( blockSize );
...
SecureByteBlock secBlock(cipherLength);
cipherLength = encryptor.Encrypt(prng, (byte*)plaintext, blockSize, secBlock);
secBlock.resize(cipherLength);
FixedMaxPlaintextLength returns a size_t, not a byte.
You should probably be calling CiphertextLength on plaintext.
I'm not really sure how you are just returning an uint_t from encrypt().
You might do better by starting fresh, and using an example from the Crypto++ as a starting point. I'm not sure this design is worth pursuing.
If you start over, then Shoup's Elliptic Curve Integrated Encryption Scheme (ECIES) would be a good choice since it combines public key with symmetric ciphers and authentication tags.