【问题标题】:Decrypt AES with wrong IV size用错误的 IV 大小解密 AES
【发布时间】:2020-02-12 04:59:24
【问题描述】:

我有一个使用 AES 128 CBC 加密的文件(在 cpp 中使用 openssl)。使用错误的 iv 大小(8 而不是 16)完成了加密。这是事实,这是错误的,我对此无能为力。 我得到的文件是这样的:
[8位IV][加密数据]

我必须用 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 扩展到 16 个字节?
  • 您如何使用 OpenSSL c++ 应用短 IV?你可以试试打印一下。
  • 您注意到它在 cpp 端工作,似乎暗示您在 cpp 中有等效的解密代码。请在您的问题中显示该代码。也许我们可以看到它是如何得出适当的 IV 的。
  • InitAES 中的 C 代码似乎使用函数 EVP_BytesToKeykeysalt 生成用于解密/加密的实际密钥 mAESKey 和 IV mAESIv。 Java代码中的参数keyIV是否有可能对应于keysalt而不是mAESKeymAESIv,并且后者必须首先在Java中生成-代码?相当惊人的是,supposed IV 的长度只有 8 个字节,这正是EVP_BytesToKey 使用的盐的长度。
  • 从我在代码中可以看到,salt 似乎设置为 nullptr。在 java 代码中,key 是确定对应于 cpp 中的 key 而不是 mAESKey。

标签: java encryption aes


【解决方案1】:

密钥和IV都是通过EVP_BytesToKey派生的,所以我猜密文前面是salt而不是IV。但是,这是您未显示的代码。

如果您想实现大部分专有的EVP_BytesToKey,那么有multiple Java implementations available。请注意,您必须自己执行 key 和 IV 之间的拆分(每个 16 个字节)。

注意事项:

  • CBC 需要 16 字节的 IV,不可能用 8 字节的 IV 执行 CBC(实现可以尝试自己填充丢失的字节,或者使用 C 中缓冲区之外的数据);
  • C++ 真的把事情弄得一团糟,IV 大小应该例如不依赖于密钥大小;
  • 当您使用它时,请对协议进行描述,而不是让下一个开发人员处于与您现在相同的位置;
  • 这都是使用旧技术,错误的密钥派生机制,未经身份验证的 CBC 加密,我会更新您的协议。

【讨论】:

  • 我正在考虑更新所有这些乱七八糟的东西(openssl 对于我对加密的理解来说是一种复杂的方式......)现在 AES 仍然是一个不错的选择吗?如果我知道它需要跨多种语言(cpp、java、php、swift,也许还有 js)是可移植的,我是否应该改变它。
  • AES 是并且仍然是一个不错的选择(对于分组密码),但通常我们将它与例如GCM 模式还提供消息完整性和真实性。 EVP_BytesToKey 通常被 PBKDF2 或 Argon2 取代(对于后者,您可能需要进一步了解实现)。
猜你喜欢
  • 2023-04-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-21
  • 1970-01-01
  • 2018-11-28
相关资源
最近更新 更多