Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/android/196.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如果我初始化AES密码,有和没有IvParameterSpec有什么区别吗_Java_Android_Security_Encryption_Aes - Fatal编程技术网

Java 如果我初始化AES密码,有和没有IvParameterSpec有什么区别吗

Java 如果我初始化AES密码,有和没有IvParameterSpec有什么区别吗,java,android,security,encryption,aes,Java,Android,Security,Encryption,Aes,我想知道,如果我初始化AES密码,有和没有IvParameterSpec,有什么区别吗 使用IvParameterSpec 无IvParameterSpec 我用一些样本测试数据进行了测试,它们的加密和解密结果是相同的 然而,由于我不是安全专家,我不想错过任何东西,并创建一个潜在的安全循环漏洞。我想知道,哪种方法是正确的?当没有提供IvParameterSpec时,密码应该初始化随机IV本身,但在您的情况下,它似乎不这样做(新字节[16]是一个填充0x00字节的数组)。似乎密码实现被破坏了。在这

我想知道,如果我初始化AES密码,有和没有IvParameterSpec,有什么区别吗

使用IvParameterSpec 无IvParameterSpec 我用一些样本测试数据进行了测试,它们的加密和解密结果是相同的


然而,由于我不是安全专家,我不想错过任何东西,并创建一个潜在的安全循环漏洞。我想知道,哪种方法是正确的?

当没有提供IvParameterSpec时,密码应该初始化随机IV本身,但在您的情况下,它似乎不这样做(
新字节[16]
是一个填充0x00字节的数组)。似乎密码实现被破坏了。在这种情况下,您应该始终提供新的random IV(语义安全所必需的)

这通常是这样做的:

SecureRandom r = new SecureRandom(); // should be the best PRNG
byte[] iv = new byte[16];
r.nextBytes(iv);

cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(iv));
然后,当您发送或存储密文时,应在其前面加上IV。在解密过程中,您只需将IV从密文的前面切掉即可使用它。它不需要保密,但应该是独一无二的


请注意,CBC模式仅为您提供保密性。如果任何类型的密文操作(恶意或非恶意)都是可能的,那么您应该使用经过身份验证的模式,如GCM或EAX。除了保密之外,这些也会给你带来诚信。如果您没有访问权限(SpongyCastle拥有它们),您可以在encrypt-then-MAC方案中使用消息身份验证码(MAC),但要正确实现要困难得多。

一点背景知识(很抱歉,如果您已经知道这一点,请确保我们使用相同的术语):

  • AES是一种分组密码,是一种对128位块进行操作的加密算法
  • CBC是一种分组密码模式,它使用分组密码对大量数据进行加密
  • 分组密码模式需要一个初始化向量(IV),它是初始化数据块,通常与基础密码的块大小相同
(关于分组密码模式的维基百科——非常好,它清楚地说明了为什么你需要静脉注射。)

不同的闭塞模式对IV选择过程提出了不同的要求,但它们都有一个共同点:

切勿使用相同的IV和密钥加密两封不同的邮件。 如果您这样做,攻击者通常可以获取您的明文,有时还可以获取您的密钥(或同等有用的数据)

CBC施加了一个额外的约束,即IV对攻击者来说必须是不可预测的-因此artjom-b建议使用
SecureRandom
来生成它是一个很好的方法


此外,正如artjob-b所指出的,CBC只会给你保密。这在实践中意味着,您的数据是保密的,但不能保证它是完整的。理想情况下,您应该使用经过身份验证的模式,如GCM、CCM或EAX


使用这些模式之一是一个非常非常好的主意。即使对于专家来说,加密MAC也是笨拙的;尽量避免。(如果必须这样做,请记住,必须使用不同的密钥进行加密和MAC。)

默认情况下,加密时,您的密码将生成一个随机IV。在解密该数据时,您必须完全使用该特定IV

好消息是,静脉注射并不是一件秘密的事情——你可以公开储存它。其主要思想是在每次加密-解密操作中保持其不同

在大多数情况下,您需要对各种数据进行加密和解密,并且为每段数据存储每个IV是一件痛苦的事情。 这就是为什么IV通常和加密数据一起存储在单个字符串中,作为固定大小的前缀。 所以当你解密你的字符串时,你肯定知道前16个字节(在我的例子中)是你的IV,其余的字节是加密的数据,你需要解密它

您的有效负载(存储或发送)将具有以下结构:

[{IV固定长度未加密}{使用密钥加密的数据}]

让我分享我的加密和解密方法,我使用AES、256位密钥、16位IV、CBC模式和PKCS7Padding。 正如Justin King Lacroix在上文中所述,您最好使用GCM、CCM或EAX块模式。不要使用ECB

encrypt()
方法的结果是安全的,可以存储在数据库中或发送到任何地方

请注意一条注释,其中您可以使用自定义IV-只需将新的SecureRandom()替换为新的IvParameterSpec(getIV())(您可以在其中输入静态IV,但这是强烈不推荐的)

私钥secretas256key
是一个带有密钥的类字段,它在构造函数中初始化

private static final String AES\u TRANSFORMATION\u MODE=“AES/CBC/PKCS7Padding”

下面是
decrypt()
方法:

   public String decrypt(String encryptedString) {
    String decryptedText = "";

    if (encryptedString == null || secretAes256Key == null)
        return decryptedText;

    try {
        //separate prefix with IV from the rest of encrypted data
        byte[] encryptedPayload = Base64.decode(encryptedString, Base64.DEFAULT);
        byte[] iv = new byte[16];
        byte[] encryptedBytes = new byte[encryptedPayload.length - iv.length];

        //populate iv with bytes:
        System.arraycopy(encryptedPayload, 0, iv, 0, 16);

        //populate encryptedBytes with bytes:
        System.arraycopy(encryptedPayload, iv.length, encryptedBytes, 0, encryptedBytes.length);

        Cipher decryptCipher = Cipher.getInstance(AES_TRANSFORMATION_MODE);
        decryptCipher.init(Cipher.DECRYPT_MODE, secretAes256Key, new IvParameterSpec(iv));

        byte[] decryptedBytes = decryptCipher.doFinal(encryptedBytes);
        decryptedText = new String(decryptedBytes);

    } catch (NoSuchAlgorithmException | BadPaddingException | NoSuchPaddingException | IllegalBlockSizeException | InvalidAlgorithmParameterException | InvalidKeyException e) {
        e.printStackTrace();
    }

    return decryptedText;
}

希望这有帮助。

JavaDoc声明,如果需要,密码将初始化自己的参数。因此,您可能会提供标准参数,如果缺少这些参数,将生成这些参数,从而得到相同的结果。你试过不同的参数吗?但是,如果我用一个随机IV加密,然后用另一个随机IV解密,我的解密会失败吗?是的,这就是为什么IV应该在密文前面,这样你可以在解密过程中直接使用它。我已经相应地更新了我的答案。当你说“你永远不能用相同的IV和密钥加密两条不同的消息”时,在这种情况下我们该怎么办?从你的评论来看,听起来我们不能签入IV,我们应该为每条消息生成一个新的IV,但是我们需要IV来解密消息来回答我自己的问题:似乎IV可以是公共信息,所以可以在加密后添加到字符串的前面。那么盐是用来做什么的?我的意思是盐就是为了这个,不是吗?谢谢你的例子,基里尔,这对我真的很有帮助。刚才注意到
AES\u转换\u模式
SecureRandom r = new SecureRandom(); // should be the best PRNG
byte[] iv = new byte[16];
r.nextBytes(iv);

cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(iv));
    public String encrypt(String data) {
    String encryptedText = "";

    if (data == null || secretAes256Key == null)
        return encryptedText;

    try {
        Cipher encryptCipher = Cipher.getInstance(AES_TRANSFORMATION_MODE);
        encryptCipher.init(Cipher.ENCRYPT_MODE, secretAes256Key, new SecureRandom());//new IvParameterSpec(getIV()) - if you want custom IV

        //encrypted data:
        byte[] encryptedBytes = encryptCipher.doFinal(data.getBytes("UTF-8"));

        //take IV from this cipher
        byte[] iv = encryptCipher.getIV();

        //append Initiation Vector as a prefix to use it during decryption:
        byte[] combinedPayload = new byte[iv.length + encryptedBytes.length];

        //populate payload with prefix IV and encrypted data
        System.arraycopy(iv, 0, combinedPayload, 0, iv.length);
        System.arraycopy(encryptedBytes, 0, combinedPayload, iv.length, encryptedBytes.length);

        encryptedText = Base64.encodeToString(combinedPayload, Base64.DEFAULT);

    } catch (NoSuchAlgorithmException | BadPaddingException | NoSuchPaddingException | IllegalBlockSizeException | UnsupportedEncodingException | InvalidKeyException e) {
        e.printStackTrace();
    }

    return encryptedText;
}
   public String decrypt(String encryptedString) {
    String decryptedText = "";

    if (encryptedString == null || secretAes256Key == null)
        return decryptedText;

    try {
        //separate prefix with IV from the rest of encrypted data
        byte[] encryptedPayload = Base64.decode(encryptedString, Base64.DEFAULT);
        byte[] iv = new byte[16];
        byte[] encryptedBytes = new byte[encryptedPayload.length - iv.length];

        //populate iv with bytes:
        System.arraycopy(encryptedPayload, 0, iv, 0, 16);

        //populate encryptedBytes with bytes:
        System.arraycopy(encryptedPayload, iv.length, encryptedBytes, 0, encryptedBytes.length);

        Cipher decryptCipher = Cipher.getInstance(AES_TRANSFORMATION_MODE);
        decryptCipher.init(Cipher.DECRYPT_MODE, secretAes256Key, new IvParameterSpec(iv));

        byte[] decryptedBytes = decryptCipher.doFinal(encryptedBytes);
        decryptedText = new String(decryptedBytes);

    } catch (NoSuchAlgorithmException | BadPaddingException | NoSuchPaddingException | IllegalBlockSizeException | InvalidAlgorithmParameterException | InvalidKeyException e) {
        e.printStackTrace();
    }

    return decryptedText;
}