-->

Decrypted file has strange characters after AES CB

2019-09-04 03:02发布

问题:

I am decrypting a file with AES CBC method using the Cryptopp library in vc++, VS2015 and QT libraries. I could obtain a result file decrypted but there are some characters which are not being decrypted correctly.

The code I use is:

const std::string encrypted_file("C:\\TEMP\\G0030013.xml");
const std::string decrypted_file("C:\\TEMP\\decrypted0.xml");
const int key_size(CryptoPP::AES::DEFAULT_KEYLENGTH);
const int iv_size(CryptoPP::AES::BLOCKSIZE);

CryptoPP::CBC_Mode<CryptoPP::AES>::Decryption decryption;

CryptoPP::StreamTransformationFilter *decryptor =
        new CryptoPP::StreamTransformationFilter(decryption,
        new CryptoPP::FileSink(decrypted_file.c_str()),
            CryptoPP::StreamTransformationFilter::BlockPaddingScheme::ZEROS_PADDING);


decryption.SetKeyWithIV(key, key_size, iv, iv_size);
CryptoPP::FileSource file_source(encrypted_file.c_str(), true, decryptor);

The resultant xml is:

<BG Val="149" Dt="2014-01-14" Tm="15:37" D="1"/>
<BG Val="158" Dt="2014-01-14" Tm="15:39" Flg="M3" D="í/ˉOæÚ…f÷ûâÄò" Dt="2014-01-14" Tm="16:00" D="1"/>
<BG Val="56" Dt="2014-01-14" Tm="16:01" D="1"/>
<BG Val="60" Dt="2014-01-14" Tm="16:12" D="1"/>
<BG Val="58" Dt="2014-01-14" Tm="16:13" D="1"/>
<BG Val="56" Dt="2014-01-14" Tm="16:16" Flg="M3" D="1"/>
<BG Val="155" Dt="2014-01-14" Tm="16:32" D="1"/>
<BG Val="168" Dt="2014-01-14" Tm="16:33" D="1"/>
<BG Val="155" Dt="2015-06-29" Tm="17:52" Flg="M3" D="1"/>
<BG Val="138" Dt="2015-07-03" Tm="09:00" Flg="M1" D="1"/>
<BG Val="141" Dt="2015-07-03" Tm="18:24" D="ðÂwÝfIïs¯¤eåˆöm5" Dt="2015-07-06" Tm="08:28" Flg="M1" D="1"/>
<BG Val="147" Dt="2013-10-18" Tm="08:40" Ctrl="?" D="1"/>
<BG Val="142" Dt="2015-11-19" Tm="10:57" Ctrl="?" D="1"/>

When it should be:

<BG Val="149" Dt="2014-01-14" Tm="15:37" D="1"/>
<BG Val="158" Dt="2014-01-14" Tm="15:39" Flg="M3" D="1"/>
<BG Val="57" Dt="2014-01-14" Tm="16:00" D="1"/>
<BG Val="56" Dt="2014-01-14" Tm="16:01" D="1"/>
<BG Val="60" Dt="2014-01-14" Tm="16:12" D="1"/>
<BG Val="58" Dt="2014-01-14" Tm="16:13" D="1"/>
<BG Val="56" Dt="2014-01-14" Tm="16:16" Flg="M3" D="1"/>
<BG Val="155" Dt="2014-01-14" Tm="16:32" D="1"/>
<BG Val="168" Dt="2014-01-14" Tm="16:33" D="1"/>
<BG Val="155" Dt="2015-06-29" Tm="17:52" Flg="M3" D="1"/>
<BG Val="138" Dt="2015-07-03" Tm="09:00" Flg="M1" D="1"/>
<BG Val="141" Dt="2015-07-03" Tm="18:24" D="1"/>
<BG Val="135" Dt="2015-07-06" Tm="08:28" Flg="M1" D="1"/>
<BG Val="147" Dt="2013-10-18" Tm="08:40" Ctrl="?" D="1"/>
<BG Val="142" Dt="2015-11-19" Tm="10:57" Ctrl="?" D="1"/>

I am wondering if there is a Charset issue and it exist a way to read the encrypted file from hexadecimal to avoid this kind of problems.

回答1:

It is not the encryption, encryption just works on 8-bit bytes ad has no concept of what they are.

Padding only adds bytes to the end of the data and can only affect the last 16 bytes so it is not a padding problem.

It could be a difference in the string encoding, the string to be encrypted is converted to bytes based on an encoding and after decryption the bytes are re-encoded to a string with sone encoding.



回答2:

This does not look right:

const int key_size(CryptoPP::AES::DEFAULT_KEYLENGTH);
const int iv_size(CryptoPP::AES::BLOCKSIZE);

I don't think its illegal, its just unusual. I think you are getting 1 int initialized to the value (either AES::DEFAULT_KEYLENGTH or AES::BLOCKSIZE).

Usually you do something like:

// Memory is allocated
SecByteBlock key(AES::DEFAULT_KEYLENGTH);
SecByteBlock iv(AES::BLOCKSIZE);

// Set them to 0
memset(key, 0x00, key.size());
memset(iv, 0x00, iv.size());

Then, if you only copy 14-bytes into key, the two tail bytes will be in a known state.

Based on the above, I'm guessing you are using some garbage for the keys.


... CryptoPP::FileSource file_source(encrypted_file.c_str(), ...

In general, you have to be careful of using c_str() because there's no telling when an embedded NULL can show up in the plain text or the cipher text. You should be OK for the code above when using it as a filename. But here's some plain text that includes an embedded NULL:

std::string plain("asdfg\0hjkl", 10); 

Better, use the overload of the source, filters and sinks that take either (1) the byte* (data) and size_t (length); or (2) the std::string (not the C-string). (2) uses string::data and string::length so embedded NULLs don't fool the machinery.

Maybe something like the following from CBC Mode on the Crypto++ wiki:

CBC_Mode< AES >::Encryption e;
e.SetKeyWithIV( key, key.size(), iv );

// The StreamTransformationFilter adds padding
//  as required. ECB and CBC Mode must be padded
//  to the block size of the cipher.
StringSource ss( plain, true, 
    new StreamTransformationFilter( e,
        new StringSink( cipher )
    ) // StreamTransformationFilter      
); // StringSource

And:

CBC_Mode< AES >::Decryption d;
d.SetKeyWithIV( key, key.size(), iv );

// The StreamTransformationFilter removes
//  padding as required.
StringSource ss( cipher, true, 
    new StreamTransformationFilter( d,
        new StringSink( recovered )
    ) // StreamTransformationFilter
); // StringSource

Instead of a StringSink, you can simply use a FileSink:

... new FileSink(decrypted_file.c_str())

CBC mode provides confidentiality only, and it does not detect tampering. Usually, you want to detect tampering too. To that end, check out EAX mode, CCM mode and GCM mode. Also see Authenticated Encryption on the Crypto++ wiki.