Joe Joe - 1 month ago 18
Android Question

Android and Crypto++ AES 128bit encrypted results not matching

I am trying to use the same key and VI to encrypt and decryp the same message, say

aabbcc@gmail.com
. The key length is 128 bit as I know that in Java/Android, 256 is not easy to implement.

Here is my function to do the AES encryption using Crypto++

string encryptString(string toBeEncrypted) {
//
// Create Cipher Text
//
CryptoPP::AES::Encryption aesEncryption(key, CryptoPP::AES::DEFAULT_KEYLENGTH);
CryptoPP::CBC_Mode_ExternalCipher::Encryption cbcEncryption(aesEncryption, iv);

std::string ciphertext;

std::cout << "To be encrypted (" << toBeEncrypted.size() << " bytes)" << std::endl;
std::cout << toBeEncrypted;
std::cout << std::endl << std::endl;

CryptoPP::StreamTransformationFilter stfEncryptor(cbcEncryption, new CryptoPP::StringSink(ciphertext), CryptoPP::StreamTransformationFilter::PKCS_PADDING);
stfEncryptor.Put(reinterpret_cast<const unsigned char*> (toBeEncrypted.c_str()), toBeEncrypted.length() + 1);
stfEncryptor.MessageEnd();

}


key is "4ff539a893fed04840749287bb3e4152" and IV is "79f564e83be16711759ac7c730072bd0".

They are stored in binary in a ubuntu running in VMWare on a x86 Windows.

The function to convert
key
and
iv
from byte to hex array is:

std::string hexToStr(unsigned char *data, int len)
{
std::stringstream ss;
ss<<std::hex;
for(int i(0);i<len;++i){
ss<<std::setfill('0')<<std::setw(2)<<(int)data[i];
}

return ss.str();
}


I checked the hex string vs the memory of byte array
key
and
iv
, and they are matching.

The results for encrypting
aabbcc@gmail.com
is
c08a50b45ff16650542e290e05390a6c6fe533e11e9f802ad7d47681fd41f964
from C++.

I obtained this by passing the returned string
ciphertext
into the function
hexToStr
like
cout<<TFFHelper::hexStr((unsigned char *)ciphertext.c_str(), ciphertext.length())<<endl;


I can also decrpt it with the following function, and I passed the raw string rather than the hex string into this function.

string TFFEncryption::decryptString(string toBeDecrypted) {

string decryptedtext;
CryptoPP::AES::Decryption aesDecryption(key, CryptoPP::AES::DEFAULT_KEYLENGTH);
CryptoPP::CBC_Mode_ExternalCipher::Decryption cbcDecryption(aesDecryption, iv);

CryptoPP::StreamTransformationFilter stfDecryptor(cbcDecryption, new CryptoPP::StringSink(decryptedtext), CryptoPP::StreamTransformationFilter::PKCS_PADDING);
stfDecryptor.Put(reinterpret_cast<const unsigned char*> (toBeDecrypted.c_str()), toBeDecrypted.size());
stfDecryptor.MessageEnd();

return decryptedtext;
}





I put the same VI and KEY in my Android code, and try to encrypt. It ends up in a half matching results after encrypting.

Android code is as follow:

public class myAES {
private static final String key = "4ff539a893fed04840749287bb3e4152";
private static final String initVector = "79f564e83be16711759ac7c730072bd0";
private final static char[] hexArray = "0123456789ABCDEF".toCharArray();

public static byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i+1), 16));
}
return data;
}

public static String bytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
for ( int j = 0; j < bytes.length; j++ ) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}

public static byte[] encrypt(String value) {
try {
IvParameterSpec iv = new IvParameterSpec(hexStringToByteArray(initVector));
SecretKeySpec skeySpec = new SecretKeySpec(hexStringToByteArray(key), "AES");

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);

byte[] encrypted = cipher.doFinal(value.getBytes());
Log.v("Encryption successful", bytesToHex(encrypted));
return encrypted;
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}

public static String decrypt(byte[] encrypted) {
try {
IvParameterSpec iv = new IvParameterSpec(hexStringToByteArray(initVector));
SecretKeySpec skeySpec = new SecretKeySpec(hexStringToByteArray(key), "AES");

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);

byte[] original = cipher.doFinal(encrypted);
Log.v("Decryption successful", new String(original, "UTF-8"));
return new String(original);
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
}


I got the result of
C08A50B45FF16650542E290E05390A6CFE5466FC480F0667517B248410930B69
.

I used the same piece of code in Netbeans on Java8, running on the same Ubuntu of the C++ code, and got exactly the same results as what I mentioned on the previous line (Android results). I don't think this is OS dependent, but probably I did something wrong with either Java or C++ in my code.

So the first half of the hex strings are matching, and the later half is not. I tried to reduced the phrase
aabbcc@gmail.com
into
abc@gmail.com
, which results in complete different results from C++ vs Java (Ubuntu vs Android).

However, if I decrpt that binary array in Java, I got the original phrase
aabbcc@gmail.com
or
abc@gmail.com
.

I have the following questions.


  1. What did I do wrong?

  2. Is it the proper way to case
    const char *
    into
    unsigned char *
    ? I think it should be OK as I am getting the hex string of the binary

  3. Is the half matching results caused by padding?


Answer

The email in the Crypto++ message is '0' terminated, but the message in Java is not.

As AES is a block cipher with a block length of 128 bits (16 bytes), and your email is exactly 16 bytes long, the first block gets encrypted the same way in both implementations. The '0' in the first position of the second block gives the difference in the second block of the encryption.

Notice the extra '00' in below screenshot using this online tool. All the '0f' following the '00' is the PKCS5 Padding that this tool did not remove here ..

enter image description here