【问题标题】:Double encryption / decryption fails but single does not - AES 256 bit双重加密/解密失败,但单一没有 - AES 256 位
【发布时间】:2014-12-18 05:10:07
【问题描述】:

所以这个特殊的异常很常见,但我的问题与通常问的略有不同。

我有一个 AES 解密和加密函数,定义如下:

    public static byte[] encrypt(byte[] ivBytes, byte[] keyBytes, byte[] textBytes)
            throws java.io.UnsupportedEncodingException,
            NoSuchAlgorithmException,
            NoSuchPaddingException,
            InvalidKeyException,
            InvalidAlgorithmParameterException,
            IllegalBlockSizeException,
            BadPaddingException {

        AlgorithmParameterSpec ivSpec = new IvParameterSpec(ivBytes);
        SecretKeySpec newKey = new SecretKeySpec(keyBytes, "AES");
        Cipher cipher;
        cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, newKey, ivSpec);
        return cipher.doFinal(textBytes);
    }

    public static byte[] decrypt(byte[] ivBytes, byte[] keyBytes, byte[] textBytes)
            throws java.io.UnsupportedEncodingException,
            NoSuchAlgorithmException,
            NoSuchPaddingException,
            InvalidKeyException,
            InvalidAlgorithmParameterException,
            IllegalBlockSizeException,
            BadPaddingException {

        AlgorithmParameterSpec ivSpec = new IvParameterSpec(ivBytes);
        SecretKeySpec newKey = new SecretKeySpec(keyBytes, "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, newKey, ivSpec);
        return cipher.doFinal(textBytes);
    }

现在,如果我像这样执行一次解密:

System.out.println(Arrays.toString(
                AES256Cipher.decrypt(ivBytes, HexBytePlainWriter.hexStringToByteArray(aKeys[a]),
                        AES256Cipher.encrypt(ivBytes, HexBytePlainWriter.hexStringToByteArray(aKeys[a]),
                                AES256Cipher.encrypt(ivBytes, HexBytePlainWriter.hexStringToByteArray(bKeys[b]), HexBytePlainWriter.hexStringToByteArray(zkeys[a^b]))
                                )
                        )));

字节数组输出就好了。而如果我执行双重加密/解密:

        System.out.println("dec: " + HexBytePlainWriter.ByteToHexString(
            AES256Cipher.decrypt(ivBytes, HexBytePlainWriter.hexStringToByteArray(aKeys[a]),
                    AES256Cipher.decrypt(ivBytes, HexBytePlainWriter.hexStringToByteArray(bKeys[b]),
                            AES256Cipher.encrypt(ivBytes, HexBytePlainWriter.hexStringToByteArray(aKeys[a]),
                                    AES256Cipher.encrypt(ivBytes, HexBytePlainWriter.hexStringToByteArray(bKeys[b]), HexBytePlainWriter.hexStringToByteArray(zkeys[a^b]))
                                    )
                            ))));

我得到了著名的javax.crypto.BadPaddingException: Given final block not properly padded 异常。请注意ab 只是整数(假设它们都是0)。目前,IVBytes 只是一个大小为 16 的空字节数组,用new byte[16] 声明。 aKeysbKeys 都是带有 AES 加密(随机)字符串(长度 32 字节)的字符串数组。

这些是我使用的辅助函数(将 byte[] 转换为十六进制字符串,反之亦然):

public static  String ByteToHexString (byte[] data) {
        StringBuilder buf = new StringBuilder();
        for (byte b : data) {
            int halfbyte = (b >>> 4) & 0x0F;
            int two_halfs = 0;
            do {
                buf.append((0 <= halfbyte) && (halfbyte <= 9) ? (char) ('0' + halfbyte) : (char) ('a' + (halfbyte - 10)));
                halfbyte = b & 0x0F;
            } while (two_halfs++ < 1);
        }
        return buf.toString();
    }

    public static byte[] hexStringToByteArray(String s) {
        int len = s.length();
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                                 + Character.digit(s.charAt(i+1), 16));
        }
        return data;
    }

我怀疑第一次解密的​​输出使密文格式错误,导致外部抛出异常。我检查了大小,外部输出 32 个字节,所以应该没问题。这不符合 PKC5Padding 吗?

非常感谢任何帮助。

编辑:

看起来我的最小示例是错误的:我首先使用密钥 B 而不是 A。但 Jon Skeet 确实给了我一个想法。如果我有任何新内容,我会进行编辑。

编辑2:

这个想法是正确的。我正在遍历一个乱码真值表(对于那些感兴趣的人,请查看此Wikipedia article)并检查所有可能的密文(CT)。问题是如果你选择了一个不正确的 CT 并对其使用双重解密,它会抛出异常,因为第一次解密返回垃圾。对表中的键进行简单检查即可解决此问题。

【问题讨论】:

    标签: java encryption cryptography aes padding


    【解决方案1】:

    您使用了错误的密钥进行解密。您使用密钥 B 加密,然后使用密钥 A 加密结果。然后您尝试使用密钥 B 解密 结果,并使用密钥 A 解密 final 结果.

    每次解密时,您都应该指定用于执行“最近”加密操作的相同密钥。所以:

             Encrypt         Encrypt        Decrypt        Decrypt
             with B          with A         with A         with B
    Plain text -> encrypted I -> encrypted II -> encrypted I -> plain text
    

    我还建议一次执行一个语句来执行这些操作,而不是因为在单个语句中完成所有事情而必须从 底部 读取,这样会更容易了解发生了什么。

    【讨论】:

    • 感谢您的回答。我确实在那里交换了它们。不幸的是,我在不同的地方运行了相同的代码(我以为我找到了一个最小的崩溃示例),但我确实应用了正确的顺序。尽管我可能会在那里检索到错误的密文,但您确实给了我一个想法。我会再看一遍,然后再回来。
    • 看我的edit2,我的预感是正确的。感谢您的回答导致了这个想法;)
    猜你喜欢
    • 2023-04-06
    • 1970-01-01
    • 1970-01-01
    • 2017-02-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多