Android 和 Crypto++ AES 128 位加密结果不匹配
Android and Crypto++ AES 128bit encrypted results not matching
我正在尝试使用相同的密钥和 VI 来加密和解密相同的消息,比如 aabbcc@gmail.com
。密钥长度是128位据我所知Java/Android,256不好实现
这是我使用 Crypto++ 进行 AES 加密的函数
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();
}
密钥是“4ff539a893fed04840749287bb3e4152”,IV 是“79f564e83be16711759ac7c730072bd0”。
它们以二进制形式存储在 x86 Windows 上的 VMWare ubuntu 运行 中 Windows。
将key
和iv
从字节数组转换为十六进制数组的函数是:
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();
}
我检查了十六进制字符串与字节数组 key
和 iv
的内存,它们是匹配的。
加密 aabbcc@gmail.com
的结果是来自 C++ 的 c08a50b45ff16650542e290e05390a6c6fe533e11e9f802ad7d47681fd41f964
。
我通过将返回的字符串 ciphertext
传递到函数 hexToStr
中获得了这个 cout<<TFFHelper::hexStr((unsigned char *)ciphertext.c_str(), ciphertext.length())<<endl;
我也可以用下面的函数解密它,我把原始字符串而不是十六进制字符串传递给这个函数。
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;
}
我在 Android 代码中放入了相同的 VI 和 KEY,并尝试加密。加密后结果为半匹配
Android代码如下:
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;
}
}
我得到了C08A50B45FF16650542E290E05390A6CFE5466FC480F0667517B248410930B69
的结果。
我在 Java8、运行 和 C++ 代码的相同 Ubuntu 上使用了 Netbeans 中的相同代码,得到的结果与我在上一行(Android 结果)。我不认为这是 OS 依赖,但可能我在代码中使用 Java 或 C++ 做错了。
所以十六进制字符串的前半部分匹配,后半部分不匹配。我试图将短语 aabbcc@gmail.com
缩减为 abc@gmail.com
,这导致 C++ 与 Java(Ubuntu 与 Android)完全不同的结果。
但是,如果我解密 Java 中的那个二进制数组,我会得到原始短语 aabbcc@gmail.com
或 abc@gmail.com
。
我有以下问题。
- 我做错了什么?
- 将
const char *
大小写为unsigned char *
是否正确?我认为应该没问题,因为我正在获取二进制文件的十六进制字符串
- 是padding造成的半匹配结果吗?
Crypto++ 邮件中的电子邮件已 '0'
终止,但 Java 中的邮件未终止.
由于 AES 是块长度为 128 位(16 字节)的块密码,而您的电子邮件恰好是 16 字节长,因此第一个块以相同的方式加密在这两种实现中。第二块第一个位置的'0'
给出了加密第二块的区别。
注意下面使用 this online tool 的屏幕截图中的额外 '00'
。 '00'
之后的所有 '0f'
是此工具未在此处删除的 PKCS5 Padding..
我正在尝试使用相同的密钥和 VI 来加密和解密相同的消息,比如 aabbcc@gmail.com
。密钥长度是128位据我所知Java/Android,256不好实现
这是我使用 Crypto++ 进行 AES 加密的函数
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();
}
密钥是“4ff539a893fed04840749287bb3e4152”,IV 是“79f564e83be16711759ac7c730072bd0”。
它们以二进制形式存储在 x86 Windows 上的 VMWare ubuntu 运行 中 Windows。
将key
和iv
从字节数组转换为十六进制数组的函数是:
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();
}
我检查了十六进制字符串与字节数组 key
和 iv
的内存,它们是匹配的。
加密 aabbcc@gmail.com
的结果是来自 C++ 的 c08a50b45ff16650542e290e05390a6c6fe533e11e9f802ad7d47681fd41f964
。
我通过将返回的字符串 ciphertext
传递到函数 hexToStr
中获得了这个 cout<<TFFHelper::hexStr((unsigned char *)ciphertext.c_str(), ciphertext.length())<<endl;
我也可以用下面的函数解密它,我把原始字符串而不是十六进制字符串传递给这个函数。
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;
}
我在 Android 代码中放入了相同的 VI 和 KEY,并尝试加密。加密后结果为半匹配
Android代码如下:
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;
}
}
我得到了C08A50B45FF16650542E290E05390A6CFE5466FC480F0667517B248410930B69
的结果。
我在 Java8、运行 和 C++ 代码的相同 Ubuntu 上使用了 Netbeans 中的相同代码,得到的结果与我在上一行(Android 结果)。我不认为这是 OS 依赖,但可能我在代码中使用 Java 或 C++ 做错了。
所以十六进制字符串的前半部分匹配,后半部分不匹配。我试图将短语 aabbcc@gmail.com
缩减为 abc@gmail.com
,这导致 C++ 与 Java(Ubuntu 与 Android)完全不同的结果。
但是,如果我解密 Java 中的那个二进制数组,我会得到原始短语 aabbcc@gmail.com
或 abc@gmail.com
。
我有以下问题。
- 我做错了什么?
- 将
const char *
大小写为unsigned char *
是否正确?我认为应该没问题,因为我正在获取二进制文件的十六进制字符串 - 是padding造成的半匹配结果吗?
Crypto++ 邮件中的电子邮件已 '0'
终止,但 Java 中的邮件未终止.
由于 AES 是块长度为 128 位(16 字节)的块密码,而您的电子邮件恰好是 16 字节长,因此第一个块以相同的方式加密在这两种实现中。第二块第一个位置的'0'
给出了加密第二块的区别。
注意下面使用 this online tool 的屏幕截图中的额外 '00'
。 '00'
之后的所有 '0f'
是此工具未在此处删除的 PKCS5 Padding..