用错误的 IV 大小解密 AES
Decrypt AES with wrong IV size
我有一个用 AES 128 CBC 加密的文件(在 cpp 中有 openssl)。加密是用错误的 iv 大小(8 而不是 16)完成的。这是事实,这是错误的,我对此无能为力。
我得到的文件是这样的:
[IV 的 8 位][加密数据]
我必须用 Java (android) 读取这个文件,但我无法得到正确的结果。
这是我用来解密文件的内容:
public String decrypt(byte[] key, byte[] datasencrypted) {
// Split IV and actual Datas into 2 differents buffers
int dataSize = datasencrypted.length - 8; // 8 = wrong iv size
byte[] encrypted = new byte[dataSize];
byte[] ivBuff = new byte[8];
System.arraycopy(datasencrypted,0,ivBuff,0,8);
System.arraycopy(datasencrypted,8,encrypted,0,dataSize);
try {
IvParameterSpec iv = new IvParameterSpec(ivBuff);
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
byte[] original = cipher.doFinal(encrypted);
return new String(original);
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
显然这段代码抛出一个 InvalidAlgorithmParameterException:预期 IV 长度为 16。如果我将 iv 缓冲区的大小更改为 16,则不再有异常,但最终结果只是乱码。
此外,密钥在 CPP 中定义为 unsigned char[],因此要将其转换为 java 中的 byte[],我只是像这样转换值:
mKey = new byte[]{(byte)0x8c,(byte)0x96,0x5f,.....}};
- 是否可以在 java 中解密此文件(它在 cpp 端工作)?
- 我怎么知道要使用哪种填充?
- 键值的转换是否有问题?
--- 编辑 ---
正如此处所建议的那样,是设法解密文件的 CPP 代码:。
AES_KEYLEN = 128.
AES_ROUND = 5.
size_t Helper::DecryptAES(unsigned char* encMsg, size_t encMsgLen, unsigned char** decMsg)
{
size_t decLen = 0;
size_t blockLen = 0;
*decMsg = (unsigned char*)malloc(encMsgLen);
if (*decMsg == nullptr) return 0;
if (!EVP_DecryptUpdate(mAESDecryptCtx, (unsigned char*)*decMsg, (int*)&blockLen, encMsg, (int)encMsgLen)) {
return 0;
}
decLen += blockLen;
if (!EVP_DecryptFinal_ex(mAESDecryptCtx, (unsigned char*)*decMsg + decLen, (int*)&blockLen)) {
return 0;
}
decLen += blockLen;
return decLen;
}
bool Helper::InitAES(unsigned char* key, unsigned char* salt)
{
mAESEncryptCtx = static_cast<EVP_CIPHER_CTX*>(malloc(sizeof(EVP_CIPHER_CTX)));
mAESDecryptCtx = static_cast<EVP_CIPHER_CTX*>(malloc(sizeof(EVP_CIPHER_CTX)));
mAESKey = static_cast<unsigned char*>(malloc(AES_KEYLEN / 8));
mAESIv = static_cast<unsigned char*>(malloc(AES_KEYLEN / 8));
int size = EVP_BytesToKey(EVP_aes_128_cbc(), EVP_sha256(), salt, key, AES_KEYLEN / 8, AES_ROUNDS, mAESKey, mAESIv);
if (size != AES_KEYLEN / 8)
{
return false;
}
EVP_CIPHER_CTX_init(mAESEncryptCtx);
if (!EVP_EncryptInit_ex(mAESEncryptCtx, EVP_aes_128_cbc(), nullptr, mAESKey, mAESIv))
return false;
EVP_CIPHER_CTX_init(mAESDecryptCtx);
if (!EVP_DecryptInit_ex(mAESDecryptCtx, EVP_aes_128_cbc(), nullptr, mAESKey, mAESIv))
return false;
return true;
}
IvSIze 在这里没问题,但是当 iv 连接到文件时,只读取 8 位而不是 16 位。
密钥和IV都是通过EVP_BytesToKey
导出的,所以我猜密文前面的是salt而不是IV。但是,这是您未显示的代码。
如果您想实施主要专有的 EVP_BytesToKey
,那么有 multiple Java implementations available。请注意,您必须自己执行密钥和 IV 之间的拆分(每个 16 个字节)。
备注:
- CBC 需要 16 字节的 IV,不可能用 8 字节的 IV 执行 CBC(当然,实现可能会尝试自己填充缺失的字节或使用超出缓冲区的数据);
- C++ 确实把事情弄得一团糟,IV 大小应该例如不依赖于密钥大小;
- 当你在做的时候,请对协议进行描述,而不是让下一个开发人员处于与你现在相同的位置;
- 这都是使用旧技术,错误的密钥派生机制,未经身份验证的 CBC 加密,我会更新您的协议。
我有一个用 AES 128 CBC 加密的文件(在 cpp 中有 openssl)。加密是用错误的 iv 大小(8 而不是 16)完成的。这是事实,这是错误的,我对此无能为力。
我得到的文件是这样的:
[IV 的 8 位][加密数据]
我必须用 Java (android) 读取这个文件,但我无法得到正确的结果。
这是我用来解密文件的内容:
public String decrypt(byte[] key, byte[] datasencrypted) {
// Split IV and actual Datas into 2 differents buffers
int dataSize = datasencrypted.length - 8; // 8 = wrong iv size
byte[] encrypted = new byte[dataSize];
byte[] ivBuff = new byte[8];
System.arraycopy(datasencrypted,0,ivBuff,0,8);
System.arraycopy(datasencrypted,8,encrypted,0,dataSize);
try {
IvParameterSpec iv = new IvParameterSpec(ivBuff);
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
byte[] original = cipher.doFinal(encrypted);
return new String(original);
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
显然这段代码抛出一个 InvalidAlgorithmParameterException:预期 IV 长度为 16。如果我将 iv 缓冲区的大小更改为 16,则不再有异常,但最终结果只是乱码。
此外,密钥在 CPP 中定义为 unsigned char[],因此要将其转换为 java 中的 byte[],我只是像这样转换值:
mKey = new byte[]{(byte)0x8c,(byte)0x96,0x5f,.....}};
- 是否可以在 java 中解密此文件(它在 cpp 端工作)?
- 我怎么知道要使用哪种填充?
- 键值的转换是否有问题?
--- 编辑 ---
正如此处所建议的那样,是设法解密文件的 CPP 代码:。
AES_KEYLEN = 128.
AES_ROUND = 5.
size_t Helper::DecryptAES(unsigned char* encMsg, size_t encMsgLen, unsigned char** decMsg)
{
size_t decLen = 0;
size_t blockLen = 0;
*decMsg = (unsigned char*)malloc(encMsgLen);
if (*decMsg == nullptr) return 0;
if (!EVP_DecryptUpdate(mAESDecryptCtx, (unsigned char*)*decMsg, (int*)&blockLen, encMsg, (int)encMsgLen)) {
return 0;
}
decLen += blockLen;
if (!EVP_DecryptFinal_ex(mAESDecryptCtx, (unsigned char*)*decMsg + decLen, (int*)&blockLen)) {
return 0;
}
decLen += blockLen;
return decLen;
}
bool Helper::InitAES(unsigned char* key, unsigned char* salt)
{
mAESEncryptCtx = static_cast<EVP_CIPHER_CTX*>(malloc(sizeof(EVP_CIPHER_CTX)));
mAESDecryptCtx = static_cast<EVP_CIPHER_CTX*>(malloc(sizeof(EVP_CIPHER_CTX)));
mAESKey = static_cast<unsigned char*>(malloc(AES_KEYLEN / 8));
mAESIv = static_cast<unsigned char*>(malloc(AES_KEYLEN / 8));
int size = EVP_BytesToKey(EVP_aes_128_cbc(), EVP_sha256(), salt, key, AES_KEYLEN / 8, AES_ROUNDS, mAESKey, mAESIv);
if (size != AES_KEYLEN / 8)
{
return false;
}
EVP_CIPHER_CTX_init(mAESEncryptCtx);
if (!EVP_EncryptInit_ex(mAESEncryptCtx, EVP_aes_128_cbc(), nullptr, mAESKey, mAESIv))
return false;
EVP_CIPHER_CTX_init(mAESDecryptCtx);
if (!EVP_DecryptInit_ex(mAESDecryptCtx, EVP_aes_128_cbc(), nullptr, mAESKey, mAESIv))
return false;
return true;
}
IvSIze 在这里没问题,但是当 iv 连接到文件时,只读取 8 位而不是 16 位。
密钥和IV都是通过EVP_BytesToKey
导出的,所以我猜密文前面的是salt而不是IV。但是,这是您未显示的代码。
如果您想实施主要专有的 EVP_BytesToKey
,那么有 multiple Java implementations available。请注意,您必须自己执行密钥和 IV 之间的拆分(每个 16 个字节)。
备注:
- CBC 需要 16 字节的 IV,不可能用 8 字节的 IV 执行 CBC(当然,实现可能会尝试自己填充缺失的字节或使用超出缓冲区的数据);
- C++ 确实把事情弄得一团糟,IV 大小应该例如不依赖于密钥大小;
- 当你在做的时候,请对协议进行描述,而不是让下一个开发人员处于与你现在相同的位置;
- 这都是使用旧技术,错误的密钥派生机制,未经身份验证的 CBC 加密,我会更新您的协议。