I have a simple program that encrypts and decrypts text from input gotten from a text file. When I encrypt and decrypt in one cycle, I get the desired result, but if I encrypt, close application then re-run application, this time decrypt, the process fails.
The decryption snippet looks like this :
string decoded, plainText;
string fileData((istreambuf_iterator<char>(fileDecrypt)), (istreambuf_iterator<char>()));
ECB_Mode<AES>::Decryption decryption;
decryption.SetKey((byte*)key.c_str(), sizeof(key));
StringSource(fileData, true, new HexDecoder(new StringSink(decoded)));
StringSource(decoded, true, new StreamTransformationFilter(decryption, new StringSink(plainText)));
When I run debugger in VS2010, I get error on the last line
StringSource(decoded, true, new StreamTransformationFilter(decryption, new StringSink(plainText)));
When I wrap a try-catch block around decrypt function, I get this error
StreamTransformationFilter: invalid PKCS #7 block padding found
Not sure why it works if I encrypt and decrypt in one build, but fail if I try to decrypt without first encrypting first on the same run.
ECB_Mode<AES>::Decryption decryption;
ECB mode operates on a full block size, and no padding is required.
You can pad it, but it does not look like you are doing so. The caveat is the plain text must be a multiple of 16, which is AES's blocksize.
When I wrap a try-catch block around decrypt function, I get this
error
StreamTransformationFilter: invalid PKCS #7 block padding found
That's because you are padding it in:
StreamTransformationFilter(decryption, new StringSink(...)).
StreamTransformationFilter has a padding parameter. As you probably realize, it is BlockPaddingScheme::PKCS_PADDING
Try:
ECB_Mode<AES>::Decryption decryption;
decryption.SetKey((byte*)key.data(), key.size());
std::string plainText;
StreamTransformationFilter filter(decryption, new StringSink(plainText),
StreamTransformationFilter::NO_PADDING);
FileSource fs(filename.c_str(), true, new HexDecoder(new Redirector(filter)));
...
Other errata:
ECB_Mode<AES>::Decryption decryption;
decryption.SetKey((byte*)key.c_str(), sizeof(key));
sizeof(key) is wrong. Use 16, 24, or 32. If the std::string is properly sized, then you can use key.size().
And name you objects. I've seen GCC generate bad code with Crypto++:
ECB_Mode<AES>::Decryption decryption;
StringSource ss1(fileData, ...);
StringSource ss2(decoded, ...);
And a quick warning....
ECB mode is usually wrong. I'm not saying it is in this case, or that you are wrong. But you might want to have a look at EAX mode, GCM mode or CCM mode. My apologies if this is more than it seems.
Even better, use a scheme like Elliptic Curve Integrated Encryption Scheme (ECIES) or Discrete Logarithm Integrated Encryption Scheme (DLIES). The schemes are IND-CCA, which is a very strong notion of security.
When using ECIES or DLIES, your problem reduces to sharing the public keys. But you have that problem now with the symmetric keys, so its a lateral move for key distribution, and a win for encryption.
Related
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.
So I am using the Crypto++ Library to encrypt a file. I need to save the key and iv for future use. I am following this tutorial. Here is my function :
void AESUtil::encrypt(string filename,bool savekeys,string savefilename){
AutoSeededRandomPool rnd;
// Generate a random key
byte key[AES::DEFAULT_KEYLENGTH];
rnd.GenerateBlock(key, AES::DEFAULT_KEYLENGTH);
// Generate a random IV
byte iv[AES::BLOCKSIZE];
rnd.GenerateBlock(iv, AES::BLOCKSIZE);
Binary b;
string plaintext = b.decoder(filename);
unsigned char *ciphertext= new unsigned char[plaintext.size()+1];
ciphertext[plaintext.size()]='\0';
if(savekeys){
ofstream("key.bin", ios::binary).write((char*)key, sizeof(key));
}
CFB_Mode<AES>::Encryption cfbEncryption(key, AES::DEFAULT_KEYLENGTH, iv);
cfbEncryption.ProcessData(ciphertext,reinterpret_cast<const unsigned char*>(plaintext.c_str()),plaintext.size()+1);
ofstream outfile(savefilename.c_str());
outfile.write((char*)ciphertext,sizeof(ciphertext));
}
The files contain data in �/���� format. I want to know the best method to save the key and iv programmatically which are a byte array to a file and the ciphertext which is a unsigned char* to a separate file.
The key could be saved in a separate file. Normally the key is established between sender / receiver in advance, or it is encrypted using a public key of the receiver. Note that it doesn't make sense to save the key next to the ciphertext, as it would provide no protection whatsoever. The handling of keys is called key management and entire books have been written about it.
The IV is a different animal. The IV should be randomly generated. For CFB it should at least be unique and identical at both sides. Usually the IV is simply prefixed to the ciphertext, it doesn't have to be kept secret.
Your key and iv variables are the key and IV used to encrypt the plain text.
You didn't fill either; you're actually using an array filled with 0 bytes as both the key and IV for your encryption.
The IV is public information. You don't need to hide it. Save it the way you want.
The KEY is what you must keep safe. To do that you may decide how much effort you want to put on it to hide it from the external factors.
I have some keys that I don't care to leave them as a "plain text" in the binary code. (NO SECURITY, but my mom can't figure out what to do, but a beginner in reverse engineer will laugh at it.)
Some keys I do a play with the bytes, like inverting parts, separating them, XOR with something. (Very unsafe, but better than nothing, a programmer with decent knowledge in reverse engineering will be able to spend some time and eventually break the security)
Some other cases I use 3rd party advanced obfuscation... If possible, depending on what you want, you may even replace your encryption engine with some "white-box" cryptography. Then you will have your keys very well protected. But this is usually hard/expensive. It doesn't seem to be your case. (Yes, even a very advanced assembly guru will not be happy to start reverse engineer this case.)
Another solution, if you don't need the key on your binary, is to give it to the system's password manager. On Windows, it's called "Data Protection API", and on Mac, it's called "Keychain". Take a look at these, and then you will understand why this is considered security. But it's because all the passwords here are encrypted with the "user password" so nothing is stored "on disk". A turned-off device in this scenario is considered very secure.
I have an issue using CryptoPP. I'm using AES, and am wanting to represent the binary ciphertext by encoding it to base64.
My problem is that I am randomly getting assertion errors when running the following code:
std::string encoded;
// ciphertext is of type std::string from AES
CryptoPP::StringSource(ciphertext, true,
new CryptoPP::Base64Encoder(new CryptoPP::StringSink(encoded)));
The specific assertion error is:
Assertion failed: m_allocated, file include\cryptopp\secblock.h, line 197
Because of this "random" behavior, it's leading me to believe that the issue lies within the contents of the ciphertext.
My question is: Am I doing this the correct way? I've been stumped for a while, and have been researching a bit without success. The closest thing I can find is: http://www.mail-archive.com/cryptopp-users#googlegroups.com/msg06053.html
My complete implementation is:
std::string key = "key";
std::string in = "This is a secret message.";
CryptoPP::SHA1 sha;
byte digest[CryptoPP::SHA1::DIGESTSIZE];
sha.CalculateDigest(digest, reinterpret_cast<const byte *>(key.c_str()), key.length());
byte iv[CryptoPP::AES::BLOCKSIZE];
memset(iv, 0x00, CryptoPP::AES::BLOCKSIZE);
CryptoPP::AES::Encryption encrypt(reinterpret_cast<const byte *>(digest), CryptoPP::AES::DEFAULT_KEYLENGTH);
CryptoPP::CBC_Mode_ExternalCipher::Encryption cbc_encrypt(encrypt, iv);
std::string ciphertext;
CryptoPP::StreamTransformationFilter encryptor(cbc_encrypt,
new CryptoPP::StringSink(ciphertext));
encryptor.Put(reinterpret_cast<const unsigned char *>(in.c_str()), in.length() + 1);
encryptor.MessageEnd();
std::string encoded;
CryptoPP::StringSource(ciphertext, true,
new CryptoPP::Base64Encoder(new CryptoPP::StringSink(encoded)));
My question is: Am I doing this the correct way?
Yes, the code is fine (except for the digest.erase();).
I've been stumped for a while, and have been researching a bit without success.
Run it under a memory checker. Valgrind or Clang Asan (address sanitizer).
The closest thing I can find is: http://www.mail-archive.com/cryptopp-users#googlegroups.com/msg06053.html
I've come across that assertion in the past, too. I don't recall if it was iOS or Linux. I think it was Linux with a specific version of GCC (maybe 4.4 or 4.5).
My problem is that I am randomly getting assertion errors when running the following code:
CryptoPP::StringSource(ciphertext, true,
new CryptoPP::Base64Encoder(new CryptoPP::StringSink(encoded)));
Change the above to this:
CryptoPP::StringSource ss(ciphertext, true,
new CryptoPP::Base64Encoder(new CryptoPP::StringSink(encoded)));
One version of GCC had problems with anonymous declarations. It would start running object destructors too soon.
Your intend is a bit unclear at the moment.
Why would you like to use the SHA digest as the key for the AES encryption?
And about the error in your code,
Your cipher at the end is a string. And if want to communicate it to somebody
you can readily send it.
Why did you use a Base 64 encoder at the end of your code ?
Had your cipher text been in the binary form you could have used Base64 Encoder
to convert it into the ASCII String format.
As long as it is not, you don't need the following part in your code.
std::string encoded;
StringSource(ciphertext, true, new Base64Encoder(new StringSink(encoded)));
I've recently been making a server which uses AES256 to encrypt/decrypt data, it took awhile to get it to send correctly. However now I'm having an issue I believe is down to memory, if I send the word "hello" it'll decrypt fine, if I then send "helloo", it'll also decrypt fine, but if I send anything shorter than "helloo" after, it'll error during decryption and if you print the encrypted string it received it's got what it should have plus the additional length of the old string.
e.g
hello: ####################
helloo: ##############################
hi: #####(#########################) //has the additional length made up from the encrypted string of "helloo" minus the first however many characters "hi" is
The code:
std::string decryptString(std::string ciphertext, byte *key, byte *iv)
{
std::string decodedtext;
CryptoPP::StringSource(ciphertext, true,
new CryptoPP::HexDecoder(new CryptoPP::StringSink(decodedtext)));
std::string plaintext;
CryptoPP::GCM<CryptoPP::AES>::Decryption dec;
dec.SetKeyWithIV((const byte *)key, CryptoPP::AES::MAX_KEYLENGTH,
(const byte *)iv, CryptoPP::AES::BLOCKSIZE);
CryptoPP::AuthenticatedDecryptionFilter adf(dec, new CryptoPP::StringSink(plaintext));
adf.Put((const byte *)decodedtext.data(), decodedtext.size());
adf.MessageEnd();
return plaintext;
}
Try using valgrind to find memory errors in your code.
Oh, and a tip: post the code itself, it might lead to more interesting answers.
If you always pass the same initialization vector to this method, may be the reason is in it. Try
dec.Resynchronize(iv);
Well, I've been going through my personal hell these days
I am having some trouble decrypting a message that was encrypted using
RSA and I'm always failing with a "RSA/OAEP-MGF1(SHA-1): invalid
ciphertext"
I have a private key encoded in base64 and I load it:
RSA::PrivateKey private_key;
StringSource file_pk(PK,true,new Base64Decoder);
private_key.Load( file_pk );
I then proceed to decode the message by doing:
RSAES_OAEP_SHA_Decryptor decryptor(private_key);
AutoSeededRandomPool rng;
string result;
StringSource(ciphertext, true,
new PK_DecryptorFilter(rng, decryptor,
new StringSink(result)
)
);
As far as I can tell, the message should be being parsed without any
problems. ciphertext is an std::string, so no \0 at the end that could
do something unexpected.
I just though of something, and what if the private key is incorrect
but can be loaded anyway without throwing a BER decode error. What
would that throw when decrypting?
Hope that anyone can shed some light on this.
Cheers
If the key was actually corrupted, the Load function should have failed. However you can ask the key to self-test itself, which should detect any corruption, by calling Validate, like:
bool key_ok = private_key.Validate(rng, 3);
The second parameter (here, 3) specifies how much checking to be done. For RSA, this will cause it to run all available tests, even the slow/expensive ones.
Another reason the decoding might fail is if the key simply is not the one that was used to encrypt the original message.
Obviously the ciphertext input must be completely identical to what was originally produced on the encrypting side. For debugging, one good way to check this would be to feed the ciphertext at both sides into a hash function (conveniently already available to you, of course) and comparing the outputs. If you hex or base64 encoded the ciphertext for transmission you must undo that before you give it to the RSA decryptor.