【问题标题】:Check encryption key correctness in JAVA在 JAVA 中检查加密密钥的正确性
【发布时间】:2012-08-31 21:51:22
【问题描述】:

我正在使用 BouncyCastle 在 CBC 模式下使用 AES 和 PKCS5 填充来加密/解密一些文件:

Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding", "BC");

现在两个问题:

  • 如何检查提供的数据解密密钥是否正确?
  • 如何检查加密的输入是否未被触及(例如,用户使用 HEX 编辑器未更改)?

谢谢

【问题讨论】:

    标签: java cryptography bouncycastle jce integrity


    【解决方案1】:

    您可以使用AEAD mode,(如 CCM 或 GCM)代替 CBC。这些模式对加密消息进行身份验证,因此如果使用了错误的密钥,或者密文已被更改,您可以检测到它。但是,您将无法区分这些情况。

    Java 7 的 GCM 加密 API 支持,但 Oracle 的 Java 实现附带的 SunJCE 提供程序尚不支持它。您可以通过 BouncyCastle 等第三方提供商获得支持。

    如果您使用额外的加密服务,例如数字签名或消息验证码,您也可以实现同样的目的。

    【讨论】:

    • 这可能是最简单的方法。他只需要确保他永远不会重复使用静脉注射。在这种情况下,GCM 会严重失败。
    【解决方案2】:

    加密不仅仅与算法和加密密钥有关,还与很多有关 系统组织。

    通常,您无法确定密钥是否正确。任何密钥都可以用来解密 应该被解密的数据,但它取决于其他一些机制来告诉你是否 是“正确”的结果。

    一般来说,你无法确定要解密的数据是否未被触及,除非通过一些 外部检查。这是大多数加密系统的属性,可以更改任何加密的 数据会极大地改变解密的输出,可能会变成你可以解释的东西 作为垃圾。

    【讨论】:

      【解决方案3】:

      您应该添加一个首先验证消息完整性的 MAC,然后才能解密它。 MAC 的一个常见选择是 HMAC 与您喜欢的任何散列函数,例如 SHA-2。

      与其自己动手,不如使用经过身份验证的密码。 AES-GCM 是常见的选择。但在这种情况下,您需要非常小心,切勿重复使用 IV。

      【讨论】:

        【解决方案4】:

        JCE 密码通常非常基础。如果您需要包括完整性和密钥测试在内的全功能保护,则需要将它们结合起来。和往常一样,最好不要自己设备。所以最好选择更高级的格式,如 PKCS7/12 或 PGP。

        根据使用的填充,当您尝试使用错误的密钥对其进行解密时,某些密码会给您一个 PaddingException。为了更强大的完整性检查,我将使用由 HMAC 字节组成的填充。

        JCE 中包含一个相当完整的方法,它是 AESWrap 算法。它需要填充数据,但会确保完整性。它最好与 RFC 3537 中描述的长度字节结合使用。请注意,这仅适用于较小数量的机密(如对称密钥)。 RFC3537 填充限制为 255 个字节。

        要将其与密码派生密钥一起使用,您可以使用:

        char[] pass = ... // your password
        byte[] codeBytes = ... // up to 255 bytes you want to protect
        
        // generate wrapping key from password
        SecretKeyFactory f = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
        SecureRandom rand = SecureRandom.getInstance("SHA1PRNG");
        byte[] salt = new byte[16]; rand.nextBytes(salt);
        SecretKey kek = f.generateSecret(new PBEKeySpec(pass, salt, 1000, 128));
        kek = new SecretKeySpec(password.getEncoded(), "AES"); // convert into AES
        
        // RFC3537 padding (lengthbyte)
        byte[] wrappedCodeBytes = new byte[codeBytes + 1 % 8];
        System.arraycopy(codeBytes,0,wrappedCodeBytes,1,wrappedCodeBytes.length);
        paddedCodeBytes[0]=(byte)codeBytes.length;
        byte[] pad = new byte[paddedCodeBytes.length - codeBytes.length -1]; rand.nextBytes(pad);
        System.arraycopy(pad,0,paddedCodeBytes,codeBytes.length+1,pad.length);
        // AESWrap is WRAP_MODE:needs a SecretKey 
        SecretKey paddedCodeKey = new SecretKeySpec(paddedCodeBytes, "RAW");
        
        // now wrap the password with AESWrap kek is 128 bit
        Cipher c = Cipher.getInstance("AESWrap"); // default IV
        c.init(Cipher.WRAP_MODE, kek);
        byte[] result = c.warp(paddedCodeKey);
        

        解包留给读者作为练习 :) 示例代码使用 128 位密钥大小,因为无论如何不能从 PBKDF2 获得更多熵。

        请注意,这很可能会检测到错误的密码,一些批评者会认为这是 AESWrap 的一个弱点。

        【讨论】:

          【解决方案5】:

          查看 this tutorial on BC encryption,特别是 InitCiphers 方法,并详细查看指定实际密码类型的第二个代码块。

          如何检查提供的数据解密密钥是否正确?

          根据JCE Javadocs,具体是Class SecretKeySpec的构造函数:

          此构造函数不检查给定字节是否确实指定了指定算法的密钥。例如,如果算法是 DES,则此构造函数不检查密钥是否为 8 字节长,也不检查弱或半弱密钥。为了执行这些检查,应使用特定于算法的密钥规范类(在本例中:DESKeySpec)。

          请注意,Interface KeySpec 列出了所有实现类,基本上是一个验证选项列表

          我如何检查加密输入是否未被触及(例如,用户使用 HEX 编辑器未更改)?

          确实如此。这是一个很好的。 “输入”非常通用。您是指要解密的实际内容吗?好吧,如果它被修改了,我相信它不会正确解密。那有意义吗?

          如果您正在谈论更改奇偶校验位的密钥的情况,如Bouncy Castle FAQ 第 (6) 项中所述,您将必须对钥匙。仅密钥的前 56 个字节用于加密操作,后 8 个字节保留用于奇偶校验。因此,本质上,“密钥”的最后一部分可以更改,而第一部分仍然有用。要检测奇偶校验或密钥是否已更改,您将运行奇偶校验。 我发现 this little ditty 在进行奇偶校验。 而且,有关如何在这些键中设置奇偶校验的更多信息,请参阅 JDK7 Crypto Provider source for Class DESKeyGenerator by Jan Luehe 中的 cmets(接近底部)其中讨论了奇偶校验设置。

          我最近与 BC 进行了一些互动,希望这些信息对我有所帮助。

          【讨论】:

          • 您的大部分帖子都特定于 DES 处理密钥的特殊方式。那些关键奇偶校验位比无用更糟糕,它们甚至不存在像 AES 这样的体面密码。 OP 需要某种形式的真正 MAC。
          猜你喜欢
          • 2012-07-19
          • 1970-01-01
          • 2012-08-27
          • 1970-01-01
          • 1970-01-01
          • 2014-12-24
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多