Java AES CBC 解密第一个块

4

我在Java和C++(具体来说是CryptoPP)之间使用AES加密算法遇到了一个大问题,我本以为这比非对称加密容易多了,因为我之前已经解决了非对称加密。

当我解密48个字节并得到38个字节的byte[]数组结果时(大小+代码+代码哈希值),最后的22个字节被正确解密,但前16个字节却是错误的。

try {
        cipher = Cipher.getInstance("AES/CBC/PKCS5Padding", "BC");
        byte[] key = { 107, -39, 87, -65, -1, -28, -85, -94, 105, 76, -94,
                110, 48, 116, -115, 86 };
        byte[] vector = { -94, 112, -23, 93, -112, -58, 18, 78, 1, 69, -92,
                102, 33, -96, -94, 59 };
        SecretKey aesKey = new SecretKeySpec(key, "AES");
        byte[] message = { 32, -26, -72, 25, 63, 114, -58, -5, 4, 90, 54,
                88, -28, 3, -72, 25, -54, -60, 17, -53, -27, -91, 34, -101,
                -93, -3, -47, 47, -12, -35, -118, -122, -77, -7, -9, -123,
                7, -66, 10, -93, -29, 4, -60, -102, 16, -57, -118, 94 };

        IvParameterSpec aesVector = new IvParameterSpec(vector);
        cipher.init(Cipher.DECRYPT_MODE, aesKey, aesVector);
        byte[] wynik = cipher.doFinal(message);
        Log.d("Solution here", "Solution");
        for (byte i : wynik)
            Log.d("Solution", "" + i);
    } catch (Exception e) {
        Log.d("ERROR", "TU");
        e.printStackTrace();
    }

我期望收到的解密消息是:

 0 0 0 32 10 0 16 43 81 -71 118 90 86 -93 -24 -103 -9 -49 14 -29 -114 82 81 -7 -59 3 -77 87 -77 48 -92 -111 -125 -21 123 21 86 4

但我理解的是:
28 127 -111 92 -75 26 18 103 79 13 -51 -60 -60 -44 18 126 -9 49 14 -29 -114 82 81 -7 -59 3 -77 87 -77 48 -92 -111 -125 -21 123 21 86 4 

正如您所看到的,只有最后22个字节是相同的。

我知道AES是按块处理的,所以我想也许初始化向量有问题(因为只有第一个块被破坏了),但是你可以看到我设置向量的方式是正确的。

我不知道为什么会出现这种情况。任何帮助都将不胜感激,因为我时间已经快用完了。

[编辑] 我添加了密码初始化。正如您所写的,它是AES/CBC/PKCS5Padding。

在CryptoPP/C++方面(实际上不是我的代码,所以我提供了我可以找到的最少的有用信息):

CryptoPP::CBC_Mode< CryptoPP::AES>::Encryption m_aesEncryption;
CryptoPP::CBC_Mode< CryptoPP::AES>::Decryption m_aesDecryption;

QByteArray AESAlgorithmCBCMode::encrypt(const QByteArray& plain)
{
std::string encrypted;

try {
    StringSource(reinterpret_cast<const byte*>(plain.data()), plain.length(), true,
            new StreamTransformationFilter(m_aesEncryption,
                    new StringSink(encrypted)));

} catch (const CryptoPP::Exception& e) {
    throw SymmetricAlgorithmException(e.what());
}

return QByteArray(encrypted.c_str(), encrypted.length());

}

QByteArray AESAlgorithmCBCMode::decrypt(const QByteArray& encrypted)
{
std::string plain;
try {
    StringSource(reinterpret_cast<const byte*>(encrypted.data()), encrypted.length(), true,
            new StreamTransformationFilter(m_aesDecryption,
                    new StringSink(plain)));
} catch (const CryptoPP::Exception& e) {
    throw SymmetricAlgorithmException(e.what());
}

return QByteArray(plain.c_str(), plain.length());

密钥和初始化向量完全相同(我已经检查过了)。 有趣的是,它是更大通信协议的一部分,之前的消息被加密和解密得非常好。而且开头也有零。


我认为你还应该在帖子中添加以下内容:1. 如何实例化密码,例如Cipher.getInstance("AES/CBC/PKCS5Padding") 2. 你的代码的C++版本。 - user1516873
你期望结果中的前三个零看起来非常可疑。 - user1516873
如果前16个字节损坏了,那么很可能是使用了错误的初始化向量。确保它们匹配。我还想指出,在加密消息时应始终生成随机IV-使用静态IV泄露有关明文的信息,就像ECB模式一样。 - ntoskrnl
是的,我知道,每个消息都应该更改向量,但是只要我解决了这个问题,我就会这样做。而且两边的向量完全相同-我检查了几次。有没有一种方法(也许是一些在线加密/解密器),可以测试哪一边是正确的?因为我找不到一个可以使用字节而不是字符串的工具。 - markubik
这个问题显示为一个有很多赞但没有解决方案的问题。你能否将问题的最后一部分改为答案,以便标记为已解决?大约2天后你可以接受自己的答案。 - Maarten Bodewes
1个回答

0

问题中已经提供了答案,即使在明确指出应该作为答案发布的评论之后,这也没有改变。

以下是答案:

关键在于,每次调用 doFinal() 都会重置密码的状态。你应该做的是存储消息的最后一个块(加密的解密器和解密的加密器),作为新的初始化向量使用。然后应调用带有此新 IV 的 init()。自然而然地,应提供加密和解密的不同实例。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接