I'm having an issue with allocating a new key for 3 key Triple DES in crypto++.
I've generated a new key as a string but need to allocate it to SecByteBlock for use in Crypto++.
Currently I generate a random key using the PRNG at the start, but when I attempt to change the key using string output from DES_EDE3, it appears to use the same key.
I think the issue is with the conversion between string and SecByteBlock, or the allocation to SecByteBlock as shown below.
Any help would be greatly appreciated!
SecByteBlock GENERATOR::setKey(string keyString){
SecByteBlock replacementKey(24);
replacementKey= SecByteBlock(reinterpret_cast<const byte*>(keyString.data()), keyString.size());
return newKey = replacementKey;
}
I attempt to change the key using string output from DES_EDE3, it appears to use the same key
It almost sounds like you are trying to use 3-DES as a PRF keyed with a password. If so, use HKDF. Its designed for these types of expand-then extract operations.
HKDF is available in Crypto++ 5.6.3 and above. If you need it for a downlevel client, then copy the header where you need it.
SecByteBlock GENERATOR::setKey(string keyString){
SecByteBlock replacementKey(24);
replacementKey= SecByteBlock(reinterpret_cast<const byte*>(keyString.data()), keyString.size());
return newKey = replacementKey;
}
Though you size replacementKey to 24, it could be resized by the assignment replacementKey= SecByteBlock(...).
You might want to try the following:
SecByteBlock GENERATOR::setKey(const string& keyString)
{
SecByteBlock key((const byte*)keyString.data(), keyString.size());
if(key,size() < DES_EDE3::KEYLENGTH)
key.CleanGrow(DES_EDE3::KEYLENGTH);
else
key.resize(DES_EDE3::KEYLENGTH);
return key;
}
CleanGrow sizes the memory block to DES_EDE3::KEYLENGTH and backfills the block with 0's as needed. resize will truncate to DES_EDE3::KEYLENGTH if its too large.
You could also do something like:
SecByteBlock key(DES_EDE3::KEYLENGTH);
size_t s = STDMIN(key.size(), keyString.size());
memcpy(key.data(), keyString.data(), s);
if(s < DES_EDE3::KEYLENGTH)
memset(key.data()+s, 0, DES_EDE3::KEYLENGTH-s);
-----
To combine the first two, you might consider this:
SecByteBlock GENERATOR::setKey(const string& keyString)
{
// Block is unintialized
SecByteBlock key(DES_EDE3::KEYLENGTH);
HKDF<SHA256> kdf;
kdf.Derivekey(key.data(), key.size(), (const byte*)keyString.data(), keyString.size(), NULL, 0);
return key;
}
-----
You can output a SecByteBlock with code like:
SecByteBlock b = GENERATOR::setKey(...);
...
cout << "Derived key: "
ArraySource as(b.data(), b.size(), true, new HexEncoder(new FileSink(cout)));
cout << endl;
The following with encode it using Base64:
ArraySource as(b.data(), b.size(), true, new Base64Encoder(new FileSink(cout)));
Related
I have searched a lot for this issue but did not find any solution. In my current project, I have to work on encrypting images with a sender receiver form. So i have to generate a key in the sender part to encrypt the file, and i have to use the same key (which is passed as an argument to the main) to get the original data, to continue program execution.
I save the key on a text file:
void GetKeyAndIv() {
// Initialize the key and IV
prng.GenerateBlock( key, key.size() );
prng.GenerateBlock(iv, iv.size());
};
/*********************Begin of the Function***********************/
//Function encrypt a file (original file) and store the result in another file (encrypted_file)
void Encrypt(std::string original_file, std::string encrypted_file_hex,string encrypted_file,string binary) {
ofstream out;
out.open("Key.txt");
out.clear();
out<<"key = "<< key<<endl;
out<<"iv = "<< iv<<endl;
string cipher, encoded;
//Getting the encryptor ready
CBC_Mode< CryptoPP::AES >::Encryption e;
e.SetKeyWithIV( key, key.size(), iv );
try
{
ifstream infile(original_file.c_str(), ios::binary);
ifstream::pos_type size = infile.seekg(0, std::ios_base::end).tellg();
infile.seekg(0, std::ios_base::beg);
//read the original file and print it
string temp;
temp.resize(size);
infile.read((char*)temp.data(), temp.size());
infile.close();
// Encryption
CryptoPP::StringSource ss( temp, true,
new CryptoPP::StreamTransformationFilter( e,
new CryptoPP::StringSink( cipher )//,
//CryptoPP::BlockPaddingSchemeDef::NO_PADDING
) // StreamTransformationFilter
); // StringSource
std::ofstream outfile1(encrypted_file.c_str(),ios::out | ios::binary);
outfile1.write(cipher.c_str() , cipher.size());
}
catch( const CryptoPP::Exception& e )
{
cout <<"Encryption Error:\n" <<e.what() << endl;
system("pause");
exit(1);
}
Then i pass it to the client side using the following code:
int main(int argc, char* argv[])
{
.....
string s1=argv[7];
SecByteBlock b1(reinterpret_cast<const byte*>(&s1[0]), s1.size());
string s2=argv[8];
SecByteBlock iv1(reinterpret_cast<const byte*>(&s2[0]), s2.size());
.....
}
I got an error while trying to decrypt the file, using the following code
void Decrypt(std::string encrypted_file,SecByteBlock key,SecByteBlock iv,string decrypted_file) {
string recovered;
try
{
// Read the encrypted file contents to a string as binary data.
std::ifstream infile(encrypted_file.c_str(), std::ios::binary);
const std::string cipher_text((std::istreambuf_iterator<char>(infile)),
std::istreambuf_iterator<char>());
infile.close();
CBC_Mode< CryptoPP::AES >::Decryption d;
d.SetKeyWithIV( key, key.size(), iv );
Decryption Error:
StreamTransformationFilter: invalid PKCS #7 block padding found
Which means i have different key during decryption process. Why this happened, and if anyone can help solving this issue.
It happen if the key used for decryption in not the same as the key that has been used for encryption.
During decryption, in PKCS#7 mode, just after decrypting the last block of 16 bytes, there is a check of padding bytes in order to know the original length of the message (which is not necessary a multiple of 16 bytes) : the last byte should be 0x01, or the last two bytes should be equal to 0x02, or the last three bytes should be equal to 0x03, ... When the decryption key is not the same as the encryption key, the padding bytes are not decrypted correctly and this implies a PKCS#7 block padding error when decrypting.
I change the CBC_Mode to another modes instead, ODB_Mode work for me
What if I want to encrypt data, using the Crypto++ library and having a user-defined password that is shorter than 32 Byte?
Right now I have the following code:
byte passwordBytes[AES::MAX_KEYLENGTH];
byte ivBytes[AES::BLOCKSIZE];
std::string textToEncrypt("encryptMe");
std::string aesKey("passwordFromUser");
std::string ivText("Iv16BytesOfText...");
memset(passwordBytes, 0, sizeof(passwordBytes)); //fill with zeroes first
memcpy(passwordBytes, aesKey.data(), aesKey.size()); //fill with key data
memcpy(ivBytes, ivText.data(), CryptoPP::AES::BLOCKSIZE); //fill iv bytes
CTR_Mode<AES>::Encryption encryption;
encryption.SetKeyWithIV(passwordBytes, sizeof(passwordBytes), ivBytes);
StringSource encryptor(textToEncrypt, true,
new StreamTransformationFilter(encryption,
new StringSink(verschluesselterText)
,StreamTransformationFilter::NO_PADDING
)
);
As you can see, aesKey is shorter than 32 Bytes.
To apply the full 32 Bytes to the encrypting function, I just fill out the unused space with zeroes, but that doesn't seem to be the best solution to me.
Am I missing something regarding creating an AES Key? With a user-defined password?
My second question, what if the user chooses a password that is longer then 32 Byte? In My case, the password would be truncated, which doesn't sound right to me.
Thanks for your help!
What if i want to encrypt data, using the Crypto++ library and having a user defined password that is shorter then 32 Byte?
Use a key derivation function (KDF) to digest the password. The modern one is Krawczyk and Eronen's HKDF using the Extract-then-Expand model. The paper is located at Cryptographic Extraction and Key Derivation: The HKDF Scheme.
You should consider using it for the IV, too. Rather than deriving 32 bytes (AES::MAX_KEYLENGTH), derive 48 bytes (AES::MAX_KEYLENGTH+AES::BLOCKSIZE) instead. The IV in your design can then be used for the salt parameter to the KDF.
Maybe something like:
#include <iostream>
#include <string>
using namespace std;
#include "cryptlib.h"
#include "aes.h"
#include "sha.h"
#include "hkdf.h"
#include "modes.h"
#include "filters.h"
using namespace CryptoPP;
int main(int argc, char* argv[])
{
SecByteBlock key(AES::MAX_KEYLENGTH+AES::BLOCKSIZE);
string password("passwordFromUser"), iv("<random value>"), message("encryptMe");
string encrypted, recovered;
try
{
HKDF<SHA256> hkdf;
hkdf.DeriveKey(key, key.size(), (const byte*)password.data(), password.size(), (const byte*)iv.data(), iv.size(), NULL, 0);
///////////////////////////////////////////////////////////////////////
CTR_Mode<AES>::Encryption encryption;
encryption.SetKeyWithIV(key, AES::MAX_KEYLENGTH, key+AES::MAX_KEYLENGTH);
StringSource encryptor(message, true,
new StreamTransformationFilter(encryption,
new StringSink(encrypted))
);
///////////////////////////////////////////////////////////////////////
CTR_Mode<AES>::Decryption decryption;
decryption.SetKeyWithIV(key, AES::MAX_KEYLENGTH, key+AES::MAX_KEYLENGTH);
StringSource decryptor(encrypted, true,
new StreamTransformationFilter(decryption,
new StringSink(recovered))
);
cout << "Message: " << message << endl;
cout << "Recovered: " << recovered << endl;
}
catch(const Exception& ex)
{
cerr << ex.what() << endl;
return 1;
}
return 0;
}
When using the encryption method above, you have to track the {iv,message} pair. The IV is needed to ensure uniqueness per-message since the password effectively fixes the AES key.
What if the user chooses a password that is longer then 32 Byte? In My case, the password would by truncated, which doesn't sound right to me.
A KDF handles it for you. It extracts the entropy regardless of how little or how much.
StringSource encryptor(textToEncrypt, true,
new StreamTransformationFilter(encryption,
new StringSink(verschluesselterText),
StreamTransformationFilter::NO_PADDING
)
There's no need to specify the padding mode. Also see the documentation for BlockPaddingScheme.
You should be very careful with modes like CTR. CTR mode xor's the keystream with the plain text. If someone reuses their password on different messages, then its possible to recover the keystream which leads to plaintext recovery.
If ivText is unique for each message, then you should add it to your KDF to ensure a unique keystream for each message. Add the IV as the salt parameter for HKDF. Here, "unique" means if I have a message "Hello World", then the IV is different each time I encrypt the message.
If the IV is truly just "Iv16BytesOfText..." (i.e., its fixed), then there's nothing unique about it. Just derive an additional 16 bytes from the user's password. Then, to avoid the keystream xor attack, switch to a mode like CBC.
Finally, you should probably use CCM, EAX or GCM mode. Right now, you only have confidentiality. Usually you want authenticity, too. To gain authenticity, you often select an Authenticated Encryption mode of operation.
I'm using the Crypto++ library for C++ to encrypt 128-bit messages with their public key and my private key from disk. However, when I call my function the program terminates with the error message:
terminate called after throwing an instance of
'CryptoPP::InvalidArgument' what(): RSA/OAEP-MGF1(SHA-1): message
length of 256 exceeds the maximum of 214 for this public key
I'm not sure why it thinks my messages are 256-bit when they are clearly 128-bit. Below is my code:
std::string encryptWithBothKeys(std::string key_name1, std::string key_name2, std::string plain){
std::string cipher1, cipher2;
AutoSeededRandomPool rng;
RSA::PublicKey pub_key;
RSA::PrivateKey priv_key;
loadPublicKey(key_name1, pub_key);
loadPrivateKey(key_name2, priv_key);
RSAES_OAEP_SHA_Encryptor e_pub(pub_key);
RSAES_OAEP_SHA_Encryptor e_priv(priv_key);
StringSource ss1(plain, true,
new PK_EncryptorFilter(rng, e_pub,
new StringSink(cipher1)
)
);
StringSource ss2(cipher1, true,
new PK_EncryptorFilter(rng, e_priv,
new StringSink(cipher2)
)
);
return cipher2;
}
void load(const std::string& filename, BufferedTransformation& bt){
FileSource file(filename.c_str(), true /*pumpAll*/);
file.TransferTo(bt);
bt.MessageEnd();
}
void loadPrivateKey(const std::string& filename, PrivateKey& key){
ByteQueue queue;
load(filename, queue);
key.Load(queue);
}
void loadPublicKey(const std::string& filename, PublicKey& key){
ByteQueue queue;
load(filename, queue);
key.Load(queue);
}
And in the main function I call it like this:
std::string encrypted_msg = encryptWithBothKeys("their.pub", "my.key", "0123456789ABCDEF");
Any suggestions?
EDIT: Turns out the ciphertext after the first encryption is size 256. I guess I need to figure out why it's increasing the size.
I'm not sure why it thinks my messages are 256-bit when they are clearly 128-bit. Below is my code:
Those are bytes, not bits.
What size RSA modulus are you using? I'm guessing its a 2048-bit key, which is 256 bytes, which leaves ≈214 bytes due to OAEP padding.
So the question becomes, what is the size of plain and cipher1 during this:
StringSource ss1(plain, true,
new PK_EncryptorFilter(rng, e_pub,
new StringSink(cipher1)
)
);
My guess is plain1 is small enough. However, when its padded and encrypted, it expands to 256 bytes as cipher1. So the message cipher1 is too large when this is executed:
StringSource ss2(cipher1, true,
new PK_EncryptorFilter(rng, e_priv,
new StringSink(cipher2)
)
);
Also, should the above be using PK_DecryptorFilter? Are you certain you want to encrypt again? If so, then why are you using e_priv?
Encrypting with the private key is not a valid cryptographic operation. If you want the "encrypt with the private key" thing, it usually means you want a Probabilistic Signature Scheme with Recovery (PSSR). Crypto++ supplies a couple of them.
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 am new to cryptopp and have been struggling for a while with the creation of private keys for ECDSA signing.
I have a hex encoded private exponent E4A6CFB431471CFCAE491FD566D19C87082CF9FA7722D7FA24B2B3F5669DBEFB. This is stored as a string.
I want to use this to sign a text block using ECDSA. My code looks a bit like this
string Sig::genSignature(const string& privKeyIn, const string& messageIn)
{
AutoSeededRandomPool prng;
ECDSA<ECP, SHA256>::PrivateKey privateKey;
privateKey.AccessGroupParameters().Initialize(ASN1::secp256r1());
privateKey.Load(StringSource(privKeyIn, true, NULL).Ref());
ECDSA<ECP, SHA256>::Signer signer(privateKey);
// Determine maximum size, allocate a string with that size
size_t siglen = signer.MaxSignatureLength();
string signature(siglen, 0x00);
// Sign, and trim signature to actual size
siglen = signer.SignMessage(prng, (const byte *) messageIn.data(), (size_t) messageIn.length(), (byte*)signature.data());
signature.resize(siglen);
cout << signature.data() << endl;
return signature;
}
This code generates the following error in Visual studio on the when I try to do privateKey.load(...)
First-chance exception at 0x7693C42D in DLLTest.exe: Microsoft C++ exception: CryptoPP::BERDecodeErr at memory location 0x0033EEA8.
Unhandled exception at 0x7693C42D in DLLTest.exe: Microsoft C++ exception: CryptoPP::BERDecodeErr at memory location 0x0033EEA8.
I am guessing I am doing something a bit stupid... any help would be great???
PS I had a similar issue using ECDH for GMAC generation but got round this by saving the key as a SECByteBlock but this 'trick' doesnt seem to work in this case.
DLLTest.exe: Microsoft C++ exception: CryptoPP::BERDecodeErr ...
You have a private exponent, and not a private key. So you should not call Load on it. That's causing the Crypto++ BERDecodeErr exception.
The answer is detailed on the ECDSA wiki page, but its not readily apparent. You need to perform the following to initialize the privateKey given the curve and exponent::
string exp = "E4A6CFB431471CFCAE491FD566D19C87082CF9FA7722D7FA24B2B3F5669DBEFB";
exp.insert(0, "0x");
Integer x(exp.c_str());
privateKey.Initialize(ASN1::secp256r1(), x);
Prepending the "0x" ensures the Integer class will parse the ASCII string correctly. You can also append a "h" character to the string. You can see the parsing code for Integer class at Integer.cpp around line 2960 in the StringToInteger function.
Here's another way to do the same thing:
string exp = "E4A6CFB431471CFCAE491FD566D19C87082CF9FA7722D7FA24B2B3F5669DBEFB";
HexDecoder decoder;
decoder.Put((byte*)exp.data(), exp.size());
decoder.MessageEnd();
Integer x;
x.Decode(decoder, decoder.MaxRetrievable());
privateKey.Initialize(ASN1::secp256r1(), x);
The HexDecoder will perform the ASCII to binary conversion for you. The buffer held by the HexDecoder will then be consumed by the Integer using its Decode (BufferedTransformation &bt, size_t inputLen, Signedness=UNSIGNED) method.
And here is another way using HexDecoder (Crypto++ is as bad as scripting languages at times :)...
string exp = "E4A6CFB431471CFCAE491FD566D19C87082CF9FA7722D7FA24B2B3F5669DBEFB";
StringSource ss(exp, true /*punpAll*/, new HexDecoder);
Integer x;
x.Decode(ss, ss.MaxRetrievable());
privateKey.Initialize(ASN1::secp256r1(), x);
After initializing the key, you should validate it:
bool result = privateKey.Validate( prng, 3 );
if( !result ) { /* Handle error */ }
This will output binary data:
cout << signature.data() << endl;
If you want something printable/readable, run it though a Crypto++ HexEncoder.
for others looking for this later
string genSignature(const string& privKeyIn, const string& messageIn)
{
CryptoPP::Integer secretNumber(genSecretNumber(privKeyIn, messageIn));
AutoSeededRandomPool secretNumberGenerator;
if (encryptBase::debug)
{
cout << "secret number: " << secretNumber << endl;
}
SecByteBlock message(convertHexStrToSecByteBlock(messageIn));
ECDSA<ECP, SHA256>::PrivateKey privateKey;
string exp(privKeyIn);
exp.insert(0, "0x");
Integer x(exp.c_str());
privateKey.Initialize(ASN1::secp256r1(), x);
AutoSeededRandomPool prng;
if (!privateKey.Validate(prng, 3))
{
cout << "unable to verify key" << endl;
return "failed to verify key";
}
ECDSA<ECP, SHA256>::Signer signer(privateKey);
size_t siglen = signer.MaxSignatureLength();
string signature(siglen, 0x00);
siglen = signer.SignMessage(secretNumberGenerator, message.BytePtr(), message.size(), (byte*)signature.data());
signature.resize(siglen);
string encoded;
HexEncoder encoder;
encoder.Put((byte *) signature.data(), signature.size());
encoder.MessageEnd();
word64 size = encoder.MaxRetrievable();
if (size)
{
encoded.resize(size);
encoder.Get((byte*)encoded.data(), encoded.size());
}
return encoded;
}