Java 使用错误的IV大小解密AES

Java 使用错误的IV大小解密AES,java,encryption,aes,Java,Encryption,Aes,我有一个文件加密在AES 128 CBC与openssl在cpp。加密使用了错误的iv大小8而不是16。这是事实,这是错误的,我对此无能为力。 我得到的文件如下所示: [8位IV][加密数据] 我必须用javaandroid读取这个文件,但我无法得到正确的结果 下面是我用来解密文件的内容: public String decrypt(byte[] key, byte[] datasencrypted) { // Split IV and actual Datas into 2

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

我必须用javaandroid读取这个文件,但我无法得到正确的结果

下面是我用来解密文件的内容:

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;
    }
显然,这段代码给我带来了一个invalidalgorithParameterException:预期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。但是这是在代码中,您没有显示

如果您想通过Testokey实现大部分专有的EVP_,那么就有了。请注意,您必须在key和IV之间分别执行16个字节的拆分

注:

CBC需要16字节的IV,用8字节的IV执行CBC是不可能的。实现可能会尝试自己填充缺少的字节,或者在C中使用缓冲区以外的数据; C++确实使事情变得一团糟,IV大小应该不依赖于密钥大小; 当你这样做的时候,请描述一下协议,而不是把下一个开发者放在你现在的位置上; 这都是使用旧技术,一个坏的密钥派生机制,没有身份验证的CBC加密,我会更新你的协议。
密钥和IV都是通过EVP_BytesToKey派生的,所以我猜密文前面是salt,而不是IV。但是这是在代码中,您没有显示的

如果您想通过Testokey实现大部分专有的EVP_,那么就有了。请注意,您必须在key和IV之间分别执行16个字节的拆分

注:

CBC需要16字节的IV,用8字节的IV执行CBC是不可能的。实现可能会尝试自己填充缺少的字节,或者在C中使用缓冲区以外的数据; C++确实使事情变得一团糟,IV大小应该不依赖于密钥大小; 当你这样做的时候,请描述一下协议,而不是把下一个开发者放在你现在的位置上; 这都是使用旧技术,一个坏的密钥派生机制,没有身份验证的CBC加密,我会更新你的协议。
你尝试过用前导零点或尾部零点扩展IV到16字节吗?你是如何使用OpenSSL C++来实现短IV的?您可以尝试打印它。注意,它在cpp端工作,似乎暗示您在cpp中有等效的解密代码。请在您的问题中显示该代码。也许我们可以看到它是如何导出适当的IV的。InitAES中的C代码似乎生成了实际的密钥mAESKey和IV mAESIv,用于使用函数EVP_BytesToKey从密钥和salt进行解密/加密。Java代码中的参数key和IV是否可能对应于key和salt而不是mAESKey和mAESIv,并且后者必须首先在Java代码中生成?非常惊人的是,假设的IV的长度只有8个字节,这正是EVP_BytesToKey使用的salt的长度。从代码中可以看到,salt似乎被设置为nullptr。在java代码中,密钥是对应于CPP中的密钥的定义符,而不是MasiKe。是否尝试用IDESL C++或Tracle零将IV扩展到16字节?您可以尝试打印它。注意,它在cpp端工作,似乎暗示您在cpp中有等效的解密代码。请在您的问题中显示该代码。也许我们可以看到它是如何导出适当的IV的。InitAES中的C代码似乎生成了实际的密钥mAESKey和IV mAESIv,用于使用函数EVP_BytesToKey从密钥和salt进行解密/加密。Java代码中的参数key和IV是否可能对应于key和salt而不是mAESKey和mAESIv,并且后者必须首先在Java代码中生成?非常惊人的是,假设的IV的长度只有8个字节,这正是EVP_BytesToKey使用的salt的长度。从代码中可以看到,salt似乎被设置为nullptr。在java代码中,键是definitevey,与cpp中的键相对应,而不是mAESKey
考虑到更新所有这些乱七八糟的openssl对于我所理解的加密来说是非常复杂的。。。AES现在仍然是一个不错的选择吗?如果我知道它需要跨多种语言(cpp、java、php、swift,也许还有js)可移植,那么我是否应该换一种其他类型的密码。AES是并且仍然是分组密码的好选择,但通常我们会将其与GCM模式配对,例如,GCM模式也提供消息完整性和真实性。EVP_BytesToKey通常被PBKDF2或Argon2取代,对于后者,您可能需要进一步查找实现。我正在考虑更新所有这些乱七八糟的openssl,因为我对加密的理解是复杂的。。。AES现在仍然是一个不错的选择吗?如果我知道它需要跨多种语言(cpp、java、php、swift,也许还有js)可移植,那么我是否应该换一种其他类型的密码。AES是并且仍然是分组密码的好选择,但通常我们会将其与GCM模式配对,例如,GCM模式也提供消息完整性和真实性。EVP_BytesToKey通常由PBKDF2或Argon2替代。对于后者,您可能需要进一步查找实现。