繁体   English   中英

AES-128加密不适用于Java <1.7

[英]AES-128 Encryption not working on Java < 1.7

我已经在学校任务中​​捣乱了3天,终于在今天完成了它,没有错误且工作正常! 除了,我在Java 1.7上测试它,学校服务器(教授将编译它)运行1.6。 所以,我在1.6上测试了我的代码,希望覆盖我的所有基础,并在解密时得到BadPaddingException

[编辑] 警告:此代码不遵循常见的安全实践,不应在生产代码中使用。

最初,我有这个,在1.7上工作正常(对不起,很多代码..所有相关..):

public static String aes128(String key, String data, final int direction) {
    SecureRandom rand = new SecureRandom(key.getBytes());
    byte[] randBytes = new byte[16];
    rand.nextBytes(randBytes);
    SecretKey encKey = new SecretKeySpec(randBytes, "AES");

    Cipher cipher = null;
    try {
        cipher = Cipher.getInstance("AES");
        cipher.init((direction == ENCRYPT ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE), encKey);
    } catch (InvalidKeyException e) {
        return null;
    } catch (NoSuchPaddingException e) {
        return null;
    } catch (NoSuchAlgorithmException e) {
        return null;
    }

    try {
        if (direction == ENCRYPT) {
            byte[] encVal = cipher.doFinal(data.getBytes());
            String encryptedValue = Base64.encode(encVal);
            return encryptedValue;
        } else {
            byte[] dataBytes = Base64.decode(data);
            byte[] encVal = cipher.doFinal(dataBytes);
            return new String(encVal);
        }
    } catch (NullPointerException e) {
        return null;
    } catch (BadPaddingException e) {
        return null;
    } catch (IllegalBlockSizeException e) {
        return null;
    }
}

但是,我的BadPaddingException catch块在解密时执行:

javax.crypto.BadPaddingException: Given final block not properly padded
        at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
        at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
        at com.sun.crypto.provider.AESCipher.engineDoFinal(DashoA13*..)
        at javax.crypto.Cipher.doFinal(DashoA13*..)
        at CipherUtils.aes128(CipherUtils.java:112)
        at CipherUtils.decryptFile(CipherUtils.java:44)
        at decryptFile.main(decryptFile.java:21)

这就是我试图解决的问题(基本上,我自己添加了所有填充/取消填充,并使用NoPadding ):

public static String aes128(String key, String data, final int direction) {
    // PADCHAR = (char)0x10 as String
    while (key.length() % 16 > 0)
        key = key + PADCHAR; // Added this loop

    SecureRandom rand = new SecureRandom(key.getBytes());
    byte[] randBytes = new byte[16];
    rand.nextBytes(randBytes);
    SecretKey encKey = new SecretKeySpec(randBytes, "AES");
    AlgorithmParameterSpec paramSpec = new IvParameterSpec(key.getBytes()); // Created this

    Cipher cipher = null;
    try {
        cipher = Cipher.getInstance("AES/CBC/NoPadding"); // Added CBC/NoPadding
        cipher.init((direction == ENCRYPT ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE), encKey, paramSpec); // Added paramSpec
    } catch (InvalidKeyException e) {
        return null;
    } catch (NoSuchPaddingException e) {
        return null;
    } catch (NoSuchAlgorithmException e) {
        return null;
    } catch (InvalidAlgorithmParameterException e) {
        return null; // Added this catch{}
    }

    try {
        if (direction == ENCRYPT) {
            while (data.length() % 16 > 0)
                data = data + PADCHAR; // Added this loop

            byte[] encVal = cipher.doFinal(data.getBytes());
            String encryptedValue = Base64.encode(encVal);
            return encryptedValue;
        } else {
            byte[] dataBytes = Base64.decode(data);
            byte[] encVal = cipher.doFinal(dataBytes);
            return new String(encVal);
        }
    } catch (NullPointerException e) {
        return null;
    } catch (BadPaddingException e) {
        return null;
    } catch (IllegalBlockSizeException e) {
        return null;
    }
}

使用它时,我只是乱搞乱哄哄:

Out: u¢;èÉ÷JRLòB±J°N°[9cRÐ{ªv=]I¯¿©:
´RLA©êí;R([¶Ü9¸ßv&%®µ^#û|Bá (80)
Unpadded: u¢;èÉ÷JRLòB±J°N°[9cRÐ{ªv=]I¯¿©:
´RLA©êí;R([¶Ü9¸ßv&%®µ^#û|Bá (79)

值得注意的是1.6和1.7产生不同的加密字符串。

例如,在1.7上,使用key hi加密xy (包括SHA-1哈希)会产生:

XLUVZBIJv1n/FV2MzaBK3FLPQRCQF2FY+ghyajdqCGsggAN4aac8bfwscrLaQT7BMHJgfnjJLn+/rwGv0UEW+dbRIMQkNAwkGeSjda3aEpk=

在1.6,同样的事情产生:

nqeahRnA0IuRn7HXUD1JnkhWB5uq/Ng+srUBYE3ycGHDC1QB6Xo7cPU6aEJxH7NKqe3kRN3rT/Ctl/OrhqVkyDDThbkY8LLP39ocC3oP/JE=

我没想到任务需要这么长时间,所以我的时间已经用完了,今晚确实需要完成。 如果那时候没有答案,我只会给老师留言。 它似乎是在1.7中修复的一些问题...虽然希望可以通过我的代码中的正确添加/修复来解决。

非常感谢每个人的时间!

首先:

对于几乎所有系统,两次加密相同的明文应该总是 (即非常非常高的概率)产生不同的密文。

传统的例子是,它允许CPA对手通过两个查询区分E(“黎明时的攻击”)和E(“黄昏时的攻击”)。 (有一些系统需要确定性加密,但正确的方法是“合成IV”或密码模式,如CMC和EME。)

最终,问题是SecureRandom()不是用于密钥派生的。

  • 如果输入“key”是密码,那么你应该使用类似PBKDF2(或scrypt()bcrypt() )的东西。
    • 此外,您应该使用显式字符集,例如String.getBytes("UTF-8")
  • 如果输入“key”是键,则最常见的字符串表示形式是hexdump。 Java不包括unhexing功能,但有几个在这里
    • 如果输入是“主密钥”并且您想要派生子密钥,那么您应该使用其他数据对其进行哈希处理 如果子键始终相同,则没有多大意义。

额外的挑剔:

  • 您的代码容易受到填充oracle攻击; 你真的应该在对数据做任何事情之前验证MAC(或者更好,使用经过验证的加密模式)。
  • 在第二个列表中,您明确重用了IV。 坏! 假设使用CBC模式,使用的IV应该是不可预测的; SecureRandom在这里很有用。

我一直在看,我不得不同意NullUserException。 问题是使用SecureRandom 这意味着你永远不会真正知道你的密钥是什么,因此它不一定是相同的密钥。

encKey来自SecureRandom,它由提供的密钥播种。 因此,如果键是相同的,种子是相同的,所以随机应该是相同的...

...当然,除非Oracle(或其他提供商)更改版本之间的实现。

好的,添加我研究的更多信息。 我认为这个答案最有帮助

从用户获取密码和明文,并将它们转换为字节数组。
生成安全的随机盐。
将salt附加到密码并计算其加密哈希值。 重复多次。
使用生成的哈希作为初始化向量和/或密钥加密明文。
保存盐和生成的密文。

对我来说,听起来像SecureRandom只使用一次来生成salt但是必须使用密码文本保存salt以撤消加密过程。 额外的安全性来自步骤的重复和变化(默默无闻)。

注意:我无法找到任何共识,即这些步骤是最佳做法。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM