How to encrypt/decrypt larger file using RSA/AES - c++

Okay so i'm looking for a way to encrypt/decrypt larger files using RSA and AES.
I don't quite understand what I need to do.
The scenario is that i've got larger files (anywhere from 200kb - 50mb). I want to be able to encrypt specific files leaving a key (private key) in the current directory as well as the encrypted file. The user can then save the key, take it with them and come back to decrypt the file at a later time.
I just don't quite understand how to use AES/RSA together to achieve this. I have some code to do simple RSA encryption/decryption and some working AES code. I got this code from other SO questions.
I'm using Openssl with C++.
Current AES program: (from online)
int main() {
int bytes_read, bytes_written;
unsigned char indata[AES_BLOCK_SIZE];
unsigned char outdata[AES_BLOCK_SIZE];
/* ckey and ivec are the two 128-bits keys necesary to
en- and recrypt your data. Note that ckey can be
192 or 256 bits as well */
unsigned char ckey[] = "thiskeyisverybad";
unsigned char ivec[] = "dontusethisinput";
/* data structure that contains the key itself */
AES_KEY key;
/* set the encryption key */
AES_set_encrypt_key(ckey, 128, &key);
/* set where on the 128 bit encrypted block to begin encryption*/
int num = 0;
FILE *ifp = fopen("out.txt", "rb");
FILE *ofp = fopen("outORIG.txt", "wb");
while (true) {
bytes_read = fread(indata, 1, AES_BLOCK_SIZE, ifp);
AES_cfb128_encrypt(indata, outdata, bytes_read, &key, ivec, &num,
AES_DECRYPT); //or AES_DECRYPT
bytes_written = fwrite(outdata, 1, bytes_read, ofp);
if (bytes_read < AES_BLOCK_SIZE)
break;
}

Okay so i'm looking for a way to encrypt/decrypt larger files using RSA and AES. I don't quite understand what I need to do...
All you need to do is:
Generate random AES key
Encrypt large file under AES key
Encrypt AES key under RSA key
Also, encryption alone is usually not enough. That means your choice of AES/CFB could be improved. That's because CFB (and other modes like CBC) provide confidentiality only. You cannot detect accidental and malicious tampering.
To improve upon the mode, you should select a mode that provides confidentiality and authenticity. AES/GCM would be a good choice. There's an example of it on the OpenSSL wiki at EVP Authenticated Encryption and Decryption.
You can read more about Authenticated Encryption on the Crypto++ wiki at Authenticated Encryption. Its a different library and different wiki, but it provides the information on authenticated encryption.
In an ideal world, OpenSSL would provide an Integrated Encryption Scheme like Shoup's Elliptic Curve Integrated Encryption Scheme (ECIES) or Abdalla, Bellare and Rogaway's Diffie-Hellman Authenticated Encryption Scheme (DHAES). An integrated encryption schemes does it all for you.
By the way, Crypto++ is a C++ crypto library that provides both integrated encryption schemes. Maybe you should consider switching security libraries. Here's the documentation with sample code on ECIES.

Related

wc_RsaSSL_Verify returns BAD_FUNC_ARG, and I can't tell why

I am trying to RSA public key decrypt a signed file using wolfcrypt - yes, I may or may not be abusing the "sign/verify" power of RSA to encrypt a separate AES key using the private key and decrypt using the public key.
Unfortunately, I am stuck at wc_RsaSSL_Verify() - for the life of me I can't figure out why it is returning BAD_FUNC_ARG - I figured an error like that should be immediately visible to somebody else so I'm deciding to call upon the collective powers of StackOverflow.
As far as I can tell, I'm giving the function what it's asking for - an input buffer, an output buffer, the size of each, and a pointer to the RsaKey struct. Here is a code snippet from the function in question:
bool VerifyWorker::GetAESKey()
{
bool result = true;
uint8_t en_aes_file_buff[VerifyWorkerLocal::RSA_KEY_SIZE];
uint8_t de_aes_file_buff[VerifyWorkerLocal::RSA_KEY_SIZE];
uint8_t* aes_iv_ptr = NULL;
// keyfile filestream
std::fstream aes_file;
// rsa_key must be initialized
if(rsa_key == NULL)
{
result = false;
}
// Open the key file and read it into a local buffer, then decrypt it and use it to initialize the
// aes struct
if(result)
{
aes_file.open(this->aes_key_file, std::ios_base::in | std::ios_base::binary);
if(aes_file.fail())
{
// Unable to open file - perror?
perror("GetAESKey");
result = false;
}
else
{
aes_file.read(reinterpret_cast<char*>(en_aes_file_buff), VerifyWorkerLocal::RSA_KEY_SIZE + 1);
if(!aes_file.eof())
{
// we didn't have enough space to read the whole signature!
std::cerr << "aes_file read failed! " << aes_file.rdstate() << std::endl;
result = false;
}
}
}
// "Unsign" the aes key file with RSA verify, and load the aes struct with the result
if(result)
{
int wc_ret = 0;
wc_ret = wc_RsaSSL_Verify(const_cast<const byte*>(en_aes_file_buff),
VerifyWorkerLocal::RSA_KEY_SIZE, reinterpret_cast<byte*>(&de_aes_file_buff),
VerifyWorkerLocal::RSA_KEY_SIZE, rsa_key);
The rsa_key is a private member initialized (successfully, using wc_PublicKeyDecode()) in a separate function with a public key DER file. I generated both the public and private key using OpenSSL - which should properly pad my AES key and iv file using PKCS#1 v1.5 b default.
I should also mention that I am using wolfssl version 3.9.8. Thanks!
The issue, I found, was that the file that I had signed with my RSA key was not signed correctly. When I signed the file using OpenSSL, my cli invocation was
openssl rsautl -in keyfile -out keyfile -inkey private.pem -sign
Apparently, openssl does not like you to specify the same file for -in and -out. When I changed it to something like
openssl rsautl -in keyfile -out keyfile_signed -inkey private.pem -sign
I was actually able to verify the file using wc_RsaSSL_Verify.
So, like most stupid late-night, last hour software problems, I was looking in the wrong place entirely. I was a bit thrown off by the BAD_FUNC_ARG being returned and thought that it had to do explicitly with the format of the function arguments, not necessarily their content. Hopefully this answer is useful for somebody else, too.
It sounds like you are trying to use RSA_Sign to perform an "Encrypt" of an AES key. Then I assume you are sending to a remote partner or computer who will then run an RSA_Verify operation to decrypt the AES key do I understand the scenario correctly?
If so I apologize it did not show up if you searched on how to do this initially but we actually have an example of doing exactly that here:
https://github.com/wolfSSL/wolfssl-examples/tree/master/signature/encryption-through-signing
That example includes two separate applications. The first app, "rsa-private-encrypt-app.c", will sign (encrypt) the "fake Aes Key" and output the result to a file. The second app, "rsa-public-decrypt-app.c", then opens the file that was output and does a verify (decrypt) on the data contained in the file to recover the original "fake Aes Key".
I may or may not be abusing the "sign/verify" power of RSA to encrypt a separate AES key using the private key and decrypt using the public key.
No not at all, that is a valid use of RSA sign/verify ASSUMING you are working with fixed-length inputs such as an AES key.
That's why we created the example! We actually had a user ask a very similar question on our forums awhile back which led to us making the example.
One thing to make note of though on the issues you encountered with openssl and wolfssl is actually talked about in the README:
https://github.com/wolfSSL/wolfssl-examples/blob/master/signature/encryption-through-signing/README.md
... Keep in mind this is not a TRUE RSA ENCRYPT and will likely not inter-op with other libraries that offer a RSA_PRIVATE_ENCRYPT type API.
This is a true SIGN operation.
If you have any other questions feel free to post them here (and add the wolfssl tag of course) or you can also send us an email anytime at support#wolfssl.com
Disclaimer: I work for wolfSSL Inc.

How to send EVP_PKEY to other party?

I am struggling with the OpenSSL API for C++ at the moment. I'm using the EVP functions to generate a RSA keypair which then is used to encrypt the AES key that was used for encrypting the data (hybrid encryption).
Key generation:
EVP_PKEY* keypair = NULL;
EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
EVP_PKEY_keygen_init(ctx);
EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, 4096);
EVP_PKEY_keygen(ctx, &keypair);
EVP_PKEY_CTX_free(ctx);
Now I have a keypair. When Encrypting a message on my own machine via EVP_SealInit / EVP_SealUpdate / EVP_SealFinal there is no problem. Same for the decrypting process. I simply give keypair as an argument to the SealInit / OpenInit function.
But consider that I want to generate a keypair and send the public or private key as a char* over a socket to another person: how do I do that?
One way I found in the Internet is using PEM_write_bio_PUBKEY or PEM_write_bio_PrivateKey to convert the Keys to char*. When trying it seemed to work. But I'm still not 100% sure. So Please look at my code and tell me if those functions are the way to go:
unsigned char* publicKey;
BIO* bio = BIO_new(BIO_s_mem());
PEM_write_bio_PUBKEY(bio, keypair);
RSAmakeString(&publicKey, bio);
unsigned char* privateKey;
BIO* bio = BIO_new(BIO_s_mem());
PEM_write_bio_PrivateKey(bio, keypair, NULL, NULL, 0, 0, NULL);
RSAmakeString(&privateKey, bio);
The other thing is how do I convert the char* back to EVP_PKEY*? Are there any functions? Because if I want to use SealInit with my public Key on another computer I have to convert it back from a char* to an EVP_PKEY*, so I can use it in the function. Any suggestions?
But consider that I want to generate a keypair and send the public or private key as a char* over a socket to another person: how do I do that?
You need something for serialization and wire formats or a presentation format. Your public key and encrypted messages will likely have 0 characters, which appear as embedded NULL. So you need to have both a buffer and explicit length .
Use Google's ProtocolBuffers, Binary JSON, or even ASN.1/DER encoding. I think Google's ProtocolBuffers are message oriented, so they won't return a message until the complete message is available.
You could also Hex, Base32 or Base64 encode it. But you still need to communicate a length so the receiving party knows they got the whole message. On a local LAN you will probably never experience a problem. Over the Internet you will probably get occasional failures as your perform short reads on occasion.
Your thoughts on PEM_write_bio_PUBKEY are effectively Base64 encoding the key, so it suffers the same potential problem as Hex, Base32 or Base64 encoding.
how do I convert the char* back to EVP_PKEY
Well, you probably won't be using a char* based on your changes above. Once you refine the design, you should probably ask a new question.
But at the moment, and given you saved the key with PEM_write_bio_PUBKEY and PEM_write_bio_PrivateKey, then you would use PEM_read_bio_PUBKEY or PEM_read_bio_PrivateKey, respectively. Also see OpenSSL's PEM man page.
Related to C++, here are some tricks when working with OpenSSL. If you are using C++11, then unique_ptr really makes it easy to work with some OpenSSL objects.
unique_ptr and OpenSSL's STACK_OF(X509)*
How to get PKCS7_sign result into a char * or std::string
Non-printable character after generating random n-byte Base64 string
EVP Symmetric Encryption and Decryption | C++ Programs on the OpenSSL wiki

What is the default IV when encrypting with aes_256_cbc cipher?

I've generated a random 256 bit symmetric key, in a file, to use for encrypting some data using the OpenSSL command line which I need to decrypt later programmatically using the OpenSSL library. I'm not having success, and I think the problem might be in the initialization vector I'm using (or not using).
I encrypt the data using this command:
/usr/bin/openssl enc -aes-256-cbc -salt -in input_filename -out output_filename -pass file:keyfile
I'm using the following call to initialize the decrypting of the data:
EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), nullptr, keyfile.data(), nullptr))
keyfile is a vector<unsigned char> that holds the 32 bytes of the key. My question is regarding that last parameter. It's supposed to be an initialization vector to the cipher algorithm. I didn't specify an IV when encrypting, so some default must have been used.
Does passing nullptr for that parameter mean "use the default"? Is the default null, and nothing is added to the first cipher block?
I should mention that I'm able to decrypt from the command line without supplying an IV.
What is the default IV when encrypting with EVP_aes_256_cbc() [sic] cipher...
Does passing nullptr for that parameter mean "use the default"? Is the default null, and nothing is added to the first cipher block?
There is none. You have to supply it. For completeness, the IV should be non-predictable.
Non-Predictable is slightly different than both Unique and Random. For example, SSLv3 used to use the last block of ciphertext for the next block's IV. It was Unique, but it was neither Random nor Non-Predictable, and it made SSLv3 vulnerable to chosen plaintext attacks.
Other libraries do clever things like provide a null vector (a string of 0's). Their attackers thank them for it. Also see Why is using a Non-Random IV with CBC Mode a vulnerability? on Stack Overflow and Is AES in CBC mode secure if a known and/or fixed IV is used? on Crypto.SE.
/usr/bin/openssl enc -aes-256-cbc...
I should mention that I'm able to decrypt from the command line without supplying an IV.
OpenSSL uses an internal mashup/key derivation function which takes the password, and derives a key and iv. Its called EVP_BytesToKey, and you can read about it in the man pages. The man pages also say:
If the total key and IV length is less than the digest length and MD5 is used then the derivation algorithm is compatible with PKCS#5 v1.5 otherwise a non standard extension is used to derive the extra data.
There are plenty of examples of EVP_BytesToKey once you know what to look for. Openssl password to key is one in C. How to decrypt file in Java encrypted with openssl command using AES in one in Java.
EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), nullptr, keyfile.data(), nullptr))
I didn't specify an IV when encrypting, so some default must have been used.
Check your return values. A call should have failed somewhere along the path. Maybe not at EVP_DecryptInit_ex, but surely before EVP_DecryptFinal.
If its not failing, then please file a bug report.
EVP_DecryptInit_ex is an interface to the AES decryption primitive. That is just one piece of what you need to decrypt the OpenSSL encryption format. The OpenSSL encryption format is not well documented, but you can work it backwards from the code and some of the docs. The key and IV computation is explained in the EVP_BytesToKey documentation:
The key and IV is derived by concatenating D_1, D_2, etc until enough
data is available for the key and IV. D_i is defined as:
D_i = HASH^count(D_(i-1) || data || salt)
where || denotes concatentaion, D_0 is empty, HASH is the digest
algorithm in use, HASH^1(data) is simply HASH(data), HASH^2(data) is
HASH(HASH(data)) and so on.
The initial bytes are used for the key and the subsequent bytes for the
IV.
"HASH" here is MD5. In practice, this means you compute hashes like this:
Hash0 = ''
Hash1 = MD5(Hash0 + Password + Salt)
Hash2 = MD5(Hash1 + Password + Salt)
Hash3 = MD5(Hash2 + Password + Salt)
...
Then you pull of the bytes you need for the key, and then pull the bytes you need for the IV. For AES-128 that means Hash1 is the key and Hash2 is the IV. For AES-256, the key is Hash1+Hash2 (concatenated, not added) and Hash3 is the IV.
You need to strip off the leading Salted___ header, then use the salt to compute the key and IV. Then you'll have the pieces to feed into EVP_DecryptInit_ex.
Since you're doing this in C++, though, you can probably just dig through the enc code and reuse it (after verifying its license is compatible with your use).
Note that the OpenSSL IV is randomly generated, since it's the output of a hashing process involving a random salt. The security of the first block doesn't depend on the IV being random per se; it just requires that a particular IV+Key pair never be repeated. The OpenSSL process ensures that as long as the random salt is never repeated.
It is possible that using MD5 this way entangles the key and IV in a way that leaks information, but I've never seen an analysis that claims that. If you have to use the OpenSSL format, I wouldn't have any hesitations over its IV generation. The big problems with the OpenSSL format is that it's fast to brute force (4 rounds of MD5 is not enough stretching) and it lacks any authentication.

What are highest performance SQLite encryption codecs in Botan?

When using Botan encryption with botansqlite3, what are the optimal configuration settings for performance?
OR
How can I configure Botansqlite3 to use CAST5?
I am currently using AES and it is too slow. My use case is a game.
I am looking for weak or moderate encryption to protect my game's data (not end user data) so security is less of a consideration than performance.
Here is my current BotanSqlite3 codec.h
/*These constants can be used to tweak the codec behavior as follows */
//BLOCK_CIPHER_STR: Cipher and mode used for encrypting the database
//make sure to add "/NoPadding" for modes that use padding schemes
const string BLOCK_CIPHER_STR = "Twofish/XTS";
//PBKDF_STR: Key derivation function used to derive both the encryption
//and IV derivation keys from the given database passphrase
const string PBKDF_STR = "PBKDF2(SHA-160)";
//SALT_STR: Hard coded salt used to derive the key from the passphrase.
const string SALT_STR = "&g#nB'9]";
//SALT_SIZE: Size of the salt in bytes (as given in SALT_STR)
const int SALT_SIZE = 64/8; //64 bit, 8 byte salt
//MAC_STR: CMAC used to derive the IV that is used for db page
//encryption
const string MAC_STR = "CMAC(Twofish)";
//PBKDF_ITERATIONS: Number of hash iterations used in the key derivation
//process.
const int PBKDF_ITERATIONS = 10000;
//KEY_SIZE: Size of the encryption key. Note that XTS splits the key
//between two ciphers, so if you're using XTS, double the intended key
//size. (ie, "AES-128/XTS" should have a 256 bit KEY_SIZE)
const int KEY_SIZE = 512/8; //512 bit, 64 byte key. (256 bit XTS key)
//IV_DERIVATION_KEY_SIZE: Size of the key used with the CMAC (MAC_STR)
//above.
const int IV_DERIVATION_KEY_SIZE = 256/8; //256 bit, 32 byte key
//This is definited in sqlite.h and very unlikely to change
#define SQLITE_MAX_PAGE_SIZE 32768
I believe that I need to find replacements for BLOCK_CIPHER_STR, PBKDF_STR, MAC_STR, KEY_SIZE and IV_DERIVATION_KEY_SIZE to reconfigure BotanSqlite3 to use a different codec.
I found a extensive comparison test of Botan codec performance here:
http://panthema.net/2008/0714-cryptography-speedtest-comparison/crypto-speedtest-0.1/results/cpu-sidebyside-comparison-3x2.pdf#page=5
However, the testing was done with Botan directly, not botansqlite3 as I intend to use it. Looking at the charts, a good candidate appears to be CAST5 from a performance perspective.
The database in question is 300KB, mostly INTEGER fields with some text blobs.
I am configuring Botan as suggested by OlivierJG of botansqlite3 fame, using the amalgamation
'./configure.py --no-autoload --enable-modules=twofish,xts,pbkdf2,cmac,sha1 --gen-amalgamation --cc=msvc --os=win32 --cpu=x86 --disable-shared --disable-asm'
References:
http://github.com/OlivierJG/botansqlite3 - botansqlite3 is an encryption codec for SQLite3 that can use any algorithms in Botan for encryption
http://www.sqlite.org - sqlite3 is a cross-platform SQL database
http://botan.randombit.net/ - botan is a C++ encryption library with support for a number of codecs
You can get CAST-128 (or as I was calling it, CAST5) to work, it is a block cipher.
The best bet is the above with different configuration of key size.
Twofish is pretty fast.
Thank you to 'Olivier JG' for all the excellent code.

RSA encrypt for C++ (BB10)

Resolved (look for my answer). I need help encrypting a password with RSA in C++. I'm working for Blackberry10 in C++ and I've been looking for a RSA tutorial or something that walk me through the encrypting process but sadly I haven't found anything consistant.
I've been told that the process consist basicaly in three steps:
1)Generate the RSA public key with the module and exponent.
2)Encrypt the text with the public key.
3)Encrypt the text again in base640.
If you are wondering why I need to follow this three steps is because I'm working in a banking app, and those are their requirements.
I've been searching all over and I haven't found even how to make the first step, generate the public key with the module and the exponent (I already have both).
Any help will be appreciated.
Thanks.
Is not that I'm building the whole app by myself. We are a team and I need to encrypt just the client password with RSA and send it to the next step of the process. I do have experiencie working with Blackberry and RSA but in Java, where the process is a little bit easier, e.g: In java the first step once you got the module and public exponent is create the RSA Public Key Object through a very simple syntaxis: RSAPublicKey publicKey = new RSAPublicKey(new RSACryptoSystem(2048),ebytes, mbytes);. In this part is where I'm little bit lost because I haven't found the proper BB10 documentation. I'm sorry if I created the illusion that I'm creating a whole encryption API by myself. BB10 has this security API based in hursa.h (https://developer.blackberry.com/native/beta/reference/com.qnx.doc.crypto/topic/about_rsa_raw.html) I'm trying to implement it, but I haven't been successful. #owlstead #dajames #bta
I highly recommend that you take advantage of an existing encryption library to handle all of this for you. OpenSSL is widely used, and the LibTom libraries include an encryption lib as well. Encryption is not an easy thing to implement from scratch, and you will save a considerable amount of time and frustration by using an existing implementation. OpenSSL in particular is a good choice, as it has passed FIPS certification tests multiple times. Since you're working on a banking app, you (and your customers) will most likely want to use an implementation that has been certified.
Even if you insist on implementing your own encryption libraries from scratch, I encourage you to check out the aforementioned libraries as examples.
It seems to me that you know even less than you think you do about how this cryptography works.
One doesn't usually use RSA encryption to encrypt data. It can be done for something short like a PIN or password, but what is usually done is to generate a symmetric key and to encrypt the data with the symmetric key and then use RSA to encrypt the symmetric key.
If you are sending a password to a bank then what you are probably supposed to be doing is to use the RSA key supplied by the bank in their own key certificate. Only the bank has the private key, so only they can decrypt the password. If that's right then you don't need to generate an RSA key, but you do need to verify that the certificate is trusted.
I don't program Blackberries, so I don't know what cryptography APIs they support, but I would expect everything you need to be built-in. This is all pretty standard stuff.
I recommend that you start by reading up on public key cryptography (e.g. on Wikipedia here and here) before starting to design your solution.
Resolved. After doing some research and getting to know better with BB10 coding and RSA, I finally came out with the solution to successfuly encrypt a plain text with RSA and Base64 in C/C++ for BB10 or any other platform. Please take in consideration that I have the modulus and the public exponent of the rsa object from the services that I'm working with.
The code:
QByteArray answer;
RSA* rsa = RSA_new();
BIGNUM *modulus = BN_new();
BIGNUM *exponent = BN_new();
const char *modulusString = rsaObj->getM(); //My Modulus
const char *exponentString = rsaObj->getE(); //My exponent
BN_hex2bn(&modulus, modulusString);
BN_hex2bn(&exponent, exponentString);
rsa->n = BN_new();
BN_copy(rsa->n, modulus);
rsa->e = BN_new();
BN_copy(rsa->e, exponent);
int maxSize = RSA_size(rsa);
qDebug() << "maxSize:" << maxSize;
const char *inn = "1234";
unsigned char *encrypted = (unsigned char*) malloc(maxSize);
int bufferSize = RSA_public_encrypt(strlen(inn), (unsigned char *) inn,
encrypted, rsa, RSA_PKCS1_PADDING);
if (bufferSize == -1) {
RSA_free(rsa);
qDebug() << "Error";
}
QByteArray enc = QByteArray::fromRawData((const char*) encrypted, 256);
answer = enc.toBase64();
return answer;
Thanks and I expect this helps to the new BB10 developers