I'm almost new in Cryptopp.
This is my encrypted data:
...
char* mDataBuffer = new char[length];
inFile.read((char*)(&mDataBuffer[0]), length);
By reading the inFile, now the mDataBuffer array contains encrypted binary data.
There's no problem with this mDataBuffer (tested carefully!).
Now I need to decrypt this array.
Note that the file (now presented as mDataBuffer array) is encrypted in java or .net in AES/CBC/PKCS7PADDING.
The decrypted data should be in char type (or could be converted to it!)
Here's my c++ code for decryption:
byte mkey[] = "12345678"; // note that the key is only 8 chars
CBC_Mode< AES >::Decryption decryptor;
decryptor.SetKeyWithIV(mkey, 16, mkey); // IV and key are same. I used 16 bytes and I think it causes Cryptopp to use PKCS#7 padding (Am I write?!)
char* decryptedBytes = new char[length];
Then I've tried many things, for example:
Try 1:
std::string plain;
StreamTransformationFilter stf(decryptor, new CryptoPP::StringSink(plain));
for (int i = 0; i < length; i++) {
stf.Put(mDataBuffer[i], sizeof(mDataBuffer[i]));
}
stf.MessageEnd();
With this code I get this error message:
CryptoPP::InvalidCiphertext at memory location
The mDataBuffer is binary data and contains 0x00 data (or '\0').
Try 2:
ArraySink cs((byte*)&decryptedBytes[0], length);
StreamTransformationFilter stf(decryptor, new Redirector(cs));
ArraySource((const char*)(&mDataBuffer[0]), true,
new StreamTransformationFilter(d, new Redirector(cs)));
With this code I get this error message:
CryptoPP::InvalidCiphertext at memory location
Note: I cant use FileSource (maybe I'm wrong!). Because the file has extra data and this data is removed manually after it has been read to mDataBuffer (for simplicity I didn't include the code here)
I wonder how I stock to this!
Thanks any help or hint.
Related
I'm trying to encrypt 320 bytes of binary data using AES-128 in CBC mode and store the cipher into a file. The output file should have been of 320 bytes, but I got 336 bytes. Here is my code:
#include <iostream>
#include <fstream>
#include <crypto++/aes.h>
#include <crypto++/modes.h>
#include <crypto++/base64.h>
#include <crypto++/sha.h>
#include <cryptopp/osrng.h>
#include <crypto++/filters.h>
#include <crypto++/files.h>
namespace CryptoPP
{
using byte = unsigned char;
}
void myAESTest()
{
std::string password = "testPassWord";
// hash the password string
// -------------------------------
CryptoPP::byte key[CryptoPP::AES::DEFAULT_KEYLENGTH], iv[CryptoPP::AES::BLOCKSIZE];
CryptoPP::byte passHash[CryptoPP::SHA256::DIGESTSIZE];
CryptoPP::SHA256().CalculateDigest(passHash, (CryptoPP::byte*) password.data(), password.size());
std::memcpy(key, passHash, CryptoPP::AES::DEFAULT_KEYLENGTH);
std::memcpy(iv, passHash+CryptoPP::AES::DEFAULT_KEYLENGTH, CryptoPP::AES::BLOCKSIZE);
// encrypt
// ---------------------------------
int chunkSize = 20*CryptoPP::AES::BLOCKSIZE;
CryptoPP::CBC_Mode<CryptoPP::AES>::Encryption encryptor;
encryptor.SetKeyWithIV(key, sizeof(key), iv);
std::ofstream testOut("./test.enc", std::ios::binary);
CryptoPP::FileSink outSink(testOut);
CryptoPP::byte message[chunkSize];
CryptoPP::StreamTransformationFilter stfenc(encryptor, new CryptoPP::Redirector(outSink));
for(int i = 0; i < chunkSize; i ++)
{
message[i] = (CryptoPP::byte)i;
}
stfenc.Put(message, chunkSize);
stfenc.MessageEnd();
testOut.close();
// decrypt
// ------------------------------------
// Because of some unknown reason increase chuksize by 1 block
// chunkSize+=16;
CryptoPP::byte cipher[chunkSize], decrypted[chunkSize];
CryptoPP::CBC_Mode<CryptoPP::AES>::Decryption decryptor;
decryptor.SetKeyWithIV(key, sizeof(key), iv);
std::ifstream inFile("./test.enc", std::ios::binary);
inFile.read((char *)cipher, chunkSize);
CryptoPP::ArraySink decSink(decrypted, chunkSize);
CryptoPP::StreamTransformationFilter stfdec(decryptor, new CryptoPP::Redirector(decSink));
stfdec.Put(cipher, chunkSize);
stfdec.MessageEnd();
inFile.close();
for(int i = 0; i < chunkSize; i++)
{
std::cout << (int)decrypted[i] << ' ';
}
std::cout << std::endl;
}
int main(int argc, char* argv[])
{
myAESTest();
return 0;
}
I'm not able to understand how the last 16 bytes are generated. If I choose to ignore the last 16 bytes in the decryption, CryptoPP throws CryptoPP::InvalidCiphertext error:
terminate called after throwing an instance of 'CryptoPP::InvalidCiphertext'
what(): StreamTransformationFilter: invalid PKCS #7 block padding found
I'm not able to understand how the last 16 bytes are generated. If I choose to ignore the last 16 bytes in the decryption, Crypto++ throws InvalidCiphertext error
The last 16 bytes are padding. Padding is added by the StreamTransformationFilter filter; see StreamTransformationFilter Class Reference in the manual. Though not obvious, DEFAULT_PADDING is PKCS_PADDING for ECB_Mode and CBC_Mode. It is NO_PADDING for other modes like OFB_Mode and CTR_Mode.
You only need to specify NO_PADDING for both the encryption and decryption filters. However, you have to ensure the plaintext and ciphertext are a multiple of the blocksize, which is 16 for AES.
You can sidestep the blocksize restriction by switching to another mode like CTR_Mode. But then you have to be very careful of key or IV reuse, which may be difficult due to the password derivation scheme you are using.
So instead of:
CBC_Mode<AES>::Encryption encryptor;
...
StreamTransformationFilter stfenc(encryptor, new Redirector(outSink));
Use:
CBC_Mode<AES>::Encryption encryptor;
...
StreamTransformationFilter stfenc(encryptor, new Redirector(outSink), NO_PADDING);
Also see CBC_Mode on the Crypto++ wiki. You might also be interested in Authenticated Encryption on the wiki.
For this, you can also:
#ifndef CRYPTOPP_NO_GLOBAL_BYTE
namespace CryptoPP
{
using byte = unsigned char;
}
#endif
CRYPTOPP_NO_GLOBAL_BYTE is defined after the C++17 std::byte fixes. If CRYPTOPP_NO_GLOBAL_BYTE is not defined, then byte is in the global namespace (Crypto++ 5.6.5 and earlier). If CRYPTOPP_NO_GLOBAL_BYTE is defined, then byte is in the CryptoPP namespace (Crypto++ 6.0 and later).
For this:
std::ofstream testOut("./test.enc", std::ios::binary);
FileSink outSink(testOut);
You can also do:
FileSink outSink("./test.enc");
For this:
SHA256().CalculateDigest(passHash, (byte*) password.data(), password.size());
std::memcpy(key, passHash, AES::DEFAULT_KEYLENGTH);
std::memcpy(iv, passHash+AES::DEFAULT_KEYLENGTH, AES::BLOCKSIZE);
You might consider using HKDF as a derivation function. Use the one password but two different labels to ensure independent derivation. One label might be the string "AES key derivation version 1" and the other label might be "AES iv derivation version 1".
The label would be used as the info parameter for DeriveKey. You just need to call it twice, once for the key and once for the iv.
unsigned int DeriveKey (byte *derived, size_t derivedLen,
const byte *secret, size_t secretLen,
const byte *salt, size_t saltLen,
const byte *info, size_t infoLen) const
secret is the password. If you have salt then use it. Otherwise HKDF uses a default salt.
Also see HKDF on the Crypto++ wiki.
Finally, regarding this:
You can sidestep the blocksize restriction by switching to another mode like CTR_Mode. But then you have to be very careful of key or IV reuse, which may be difficult due to the password derivation scheme you are using.
You might also consider an Integrated Encryption Scheme, like Elliptic Curve Integrated Encryption Scheme. It is IND-CCA2, which is a strong notion of security. Everything you need is packaged into the encryption scheme.
Under ECIES each user gets a public/private keypair. Then, a large random secret is used as a seed for the AES key, iv and mac key. The the plaintext is encrypted and authenticated. Finally, the seed is encrypted under the user's public key. The password is still used, but it is used to decrypt the private key.
I implemented simple file encryption/decryption with OpenSSL in C according to the instructions here. I do not need this to be truly secure (just want the files to not be readily readable on drive), the keys are hardcoded in the application and after reading the encrypted files from drive I decrypt them.
On first call, the decryptFileAsBytes function returns the correct decrypted file as byte vector. On the second call (within the same application run) the first 16 bytes of the result are garbage and the rest is correct. Does this have something to do with the size of the key (128 bits) I am using?
static bool decryptFileAsBytes(std::string filename, unsigned char *ckey, unsigned char *ivec, std::vector<unsigned char> &fileBytes)
{
std::ifstream ifs(filename, std::ios::binary | std::ios::ate);
if (ifs.fail())
return false;
std::ifstream::pos_type pos = ifs.tellg();
fileBytes.resize(pos);
ifs.close();
FILE *ifp;
if (fopen_s(&ifp, filename.c_str(), "rb") != NULL)
return false;
int bytesRead;
unsigned char indata[AES_BLOCK_SIZE];
unsigned char *writePtr = fileBytes.data();
/* 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;
while (1)
{
bytesRead = fread(indata, 1, AES_BLOCK_SIZE, ifp);
AES_cfb128_encrypt(indata, writePtr, bytesRead, &key, ivec, &num, AES_DECRYPT);
writePtr += bytesRead;
if (bytesRead < AES_BLOCK_SIZE)
break;
}
if (fclose(ifp) != NULL)
return false;
return true;
}
Alternatively to solving this, I welcome suggestions of a simple solution to the problem stated above ('encrypt' file on drive in a not bulletproof way so that it is not readily readable but the application can decrypt it).
The problem is likely that you're not retaining the original initialization vector for subsequent decryption operations.
As the AES encryption/decryption operations transpire, that memory is updated to continue with subsequent frames. If you instrument your code you'll see, with each encrypt/decrypt frame passing through the API, the ivec is changed.
If all you're doing this for is obfuscation (eg. you have a static key in your application) my suggestion is to do the following:
Don't pass the ivec into either the encryptor or decryptor.
Instead, generate a random ivec using RAND_bytes when encrypting. Store the ivec as the first block of data before continuing with the file content.
When decrypting, read the first block of data to prime your ivec.
Then, decrypt the remainder of the file as normal.
The benefits are:
Each encryption of a file will create a different byte representation, dependent on the initial random ivec. Eg. if you encrypt a file twice the resulting encrypted bytes will not be the same
You no longer have to use a static ivec from somewhere else in your code. The file contains it as the first block of data.
Just a suggestion. Unrelated, I prefer the EVP encryption interface, and suggest it worth a look.
I am working to encrypt and decrypt files using Crypto++. In encryption, key and random IV are generated and hexencoded where as text from file is encrypted. Both IV and cipher text are written to the same file.
In decryption, key is generated using same criteria as encryption and random IV is extracted from the file and hexdecoded. Text after iv length is stored in a string and decrypted.
What happens is I can see the original file so I know that it is working but it also displays cipher text after the original file text. Does any one how to solve it?
//some code to declare variables, read from file and so on
unsigned char * inputContent = (unsigned char *) malloc(fileSize * sizeof(char)); //create char array of same size as file content
//inputContent is for storing file data
string rawString(reinterpret_cast<char*>(inputContent), fileSize); //convert char array to string
//extract iv, key and cipher from rawString
string rawIV;
rawIV = rawString.substr(0, 32);
//code to hexdecode iv
string cipher;
cipher = rawString.substr(32, fileSize - 32);
string recovered;
CBC_Mode< AES >::Decryption d;
d.SetKeyWithIV(key, sizeof(key), iv);
StringSource s_recover(cipher, true,
new StreamTransformationFilter(d,
new StringSink(recovered)
)
);
const char * writeContent = recovered.c_str();
if(pwrite(fd, writeContent, recovered.length(), 0) <= 0)
{
return -1; //error
}
Thanks in advance. ☺
You might try something like this. But its hard to say if it will actually work since its not clear what you are actually doing or where the problem lies.
FileSource fs("<filename>", false /*pumpAll*/);
SecByteBlock key(AES::DEFAULT_KEYLENGTH), iv(AES::BLOCKSIZE);
// Fetch key from somewhere
key = ...;
// Fetch IV from file
fs.Detach(new HexDecoder(new ArraySink(iv, iv.size()));
fs.Pump(32);
CBC_Mode< AES >::Decryption dec;
dec.SetKeyWithIV(key, key.size(), iv, iv.size());
string recovered;
fs.Detach(new HexDecoder(new StreamTransformationFilter(dec, new StringSink(recovered))));
fs.PumpAll();
You can also use the following if you get the SecByteBlockSink patch:
SecByteBlock recovered;
fs.Detach(new HexDecoder(new StreamTransformationFilter(dec, new SecByteBlockSink(recovered))));
fs.PumpAll();
rawString isn't needed below:
//create char array of same size as file content
unsigned char * inputContent = (unsigned char *) malloc(fileSize * sizeof(char));
//inputContent is for storing file data
//convert char array to string
string rawString(reinterpret_cast<char*>(inputContent), fileSize);
Maybe you should try:
ArraySource as(inputContent, fileSize, false /*pumpAll*/);
Using the ArraySource means you don't make a copy of the data (the string copies the data), and its ready to go for Crypto++.
Also, since you're already into C++ code, use an unique_ptr and new rather than malloc. The unique_ptr will handle cleanup for you. (Or, use a std::vector).
unique_ptr<byte[]> buffer(new byte[fileSize]);
I don't know how you are going to make a file descriptor work in the grand scheme of things. Crypto++ is a C++ library, and C++ uses I/O streams. Maybe this will help: How to construct a c++ fstream from a POSIX file descriptor?
Also see Retrieving file descriptor from a std::fstream and Getting a FILE* from a std::fstream.
I have c++ code that encrypts a string as a plaintext using AES_CFB and generates a same size ciphertext, but the problem is the data type of input and output, So could anyone help me to let it encrypts an unsigned int number and generates unsigned int number ciphertext withe keeping the same length for the plaintext and chipertext (length of bits ).
string ENCRYPTOR(const std::string& PlainText)
{
byte key[16]= "1234ff";// byte key[ CryptoPP::AES::DEFAULT_KEYLENGTH ];
byte iv[16]= "123456";//byte iv[ CryptoPP::AES::BLOCKSIZE ];
std::string CipherText;
// Encryptor
CryptoPP::CFB_Mode< CryptoPP::AES >::Encryption encryptor( key, sizeof(key), iv);
// Encryption
CryptoPP::StringSource( PlainText, true,
new CryptoPP::StreamTransformationFilter( encryptor,
new CryptoPP::StringSink( CipherText ) ) );
return (CipherText);
}
string DECRYPTOR(const string& CipherText)
{
byte key[16]= "1234ff";
byte iv[16]= "123456";
std::string RecoveredText;
// Decryptor
CryptoPP::CFB_Mode< CryptoPP::AES >::Decryption decryptor( key, sizeof(key), iv );
// Decryption
CryptoPP::StringSource( CipherText, true,
new CryptoPP::StreamTransformationFilter( decryptor,
new CryptoPP::StringSink( RecoveredText ) ) );
return (RecoveredText);
}
int main()
{
string ciphertext;
string plaintext = "3555";
ciphertext= ENCRYPTOR(plaintext);
string retrivdat = DECRYPTOR(ciphertext);
cout<<"The plaintext data is: "<<plaintext<<endl;
cout<<"The ciphertextdata is: "<<ciphertext<<endl;
Coot<<"The retrieved data is: "<<retrivdat<<end;
return 0;
}
The output is
The plaintext data is: 3555
The chepertext data is: ï¥R_
The retrieved data is: 3555
Encrypt unsigned int value in form of bits stream by AES_CFB mode
Igor and Owlstead raised some valid points about size of integers and endianess. The easiest solution to avoid them is probably encode the integer as a string:
unsigned int n = ...;
ostringstream oss;
oss << n;
string plainText = oss.str();
Later, you can convert it back with:
string recovered = ...;
istringstream iss(recovered);
unsigned int n;
iss >> n;
byte key[16]= "1234ff";// byte key[ CryptoPP::AES::DEFAULT_KEYLENGTH ];
byte iv[16]= "123456";//byte iv[ CryptoPP::AES::BLOCKSIZE ];
Your key and IV are too small. You should be getting compiler warnings because of it. AES::DEFAULT_KEYLENGTH is 16, so you need at least 16 characters for the key. AES::BLOCKSIZE is 16, so you need at least 16 characters for the initialization vector.
If the code above happens to work, then its purely because of luck. You should probably visit CFB Mode on the Crypto++ wiki. It has a working example.
Alternately, use PBKDF to stretch the short key and short IV. You can find an example at Crypto++ pbkdf2 output is different than Rfc2898DeriveBytes (C#) and crypto.pbkdf2 (JavaScript) on Stack Overflow.
The chepertext data is: ï¥R_
You can make this printable with:
string encoded;
HexEncoder hexer(new StringSink(encoded));
hexer.Put((byte*)cipherText.data(), cipherText.size());
hexer.MessageEnd();
cout << encoded << endl;
Alternately, you can use the following (with pipelines):
string encoded;
StringSource ss(cipherText, true,
new HexEncoder(
new StringSink(encoded)));
cout << encoded << endl;
HexEncoder and HexDecoder are discussed on the Crypto++ wiki, too.
So you can:
encode the number into the minimum number of x bytes, for instance using an unsigned big endian number
encrypt with CFB, resulting in the same number of x bytes
decrypt the number
decode the number from the resulting x bytes (using the same encoding scheme of course)
If you want to see the ciphertext as number you'll have to decode the ciphertext as if it was a (signed or unsigned) number.
Note that you will still have to deal with the uniqueness of the IV. If you need to store the IV then there will be significant overhead.
Well, I downloaded Rijndael sources from here: http://www.codeproject.com/Articles/1380/A-C-Implementation-of-the-Rijndael-Encryption-Decr
I have the following code:
int AutoUpdater::GetVersion()
{
std::ifstream file("ver.dat", std::ios::out );
if(file.fail())
return 0;
file.seekg(0,std::ios::end);
int len = (int)file.tellg();
file.seekg(0,std::ios::beg);
char* line = new char[len];
file.read(line,len);
file.close();
CRijndael crypt;
crypt.MakeKey("MIUJkHyHnjHyGtqO", "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16, 16);
char * decrypted = new char[len];
crypt.Decrypt(line,decrypted,len);
delete[] line;
delete [] decrypted;
return atoi(line);
}
But it is giving this error: "Data not multiple of Block Size"
My file to encrypt has to have a fixed length?
Pad it with trailing zeros to fill out the block size. You could also consider prefixing the byte length, to recover the original exact length.
Rijndael is a block cipher. Your input doesn't need fixed length, but it does need to be a multiple of the block size (16 bytes for Rijndael). There are lots of padding methods you can use to ensure that. I like PKCS7, nice and simple.
What I'm confused by is why your input file is not a multiple of the block size. You couldn't have properly encrypted data using Rijndael that gave you such a file.
Yes. Rijndael is a block cipher. You should encode your data with a length field in front. The bits beyond the length field can be safely ignored by the decoder.