java - 给定最终 block 未正确填充

标签 java exception encryption cryptography javax.crypto

我正在尝试实现基于密码的加密算法,但我得到了这个异常:

javax.crypto.BadPaddingException: Given final block not properly padded

可能是什么问题?

这是我的代码:

public class PasswordCrypter {

    private Key key;

    public PasswordCrypter(String password)  {
        try{
            KeyGenerator generator;
            generator = KeyGenerator.getInstance("DES");
            SecureRandom sec = new SecureRandom(password.getBytes());
            generator.init(sec);
            key = generator.generateKey();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    public byte[] encrypt(byte[] array) throws CrypterException {
        try{
            Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, key);

            return cipher.doFinal(array);
        } catch (Exception e) { 
            e.printStackTrace();
        }
        return null;
    }

    public byte[] decrypt(byte[] array) throws CrypterException{
        try{
            Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, key);

            return cipher.doFinal(array);
        } catch(Exception e ){
            e.printStackTrace();
        }
        return null;
    }
}

(JUnit 测试)

public class PasswordCrypterTest {

    private static final byte[] MESSAGE = "Alpacas are awesome!".getBytes();
    private PasswordCrypter[] passwordCrypters;
    private byte[][] encryptedMessages;

    @Before
    public void setUp() {
        passwordCrypters = new PasswordCrypter[] {
            new PasswordCrypter("passwd"),
            new PasswordCrypter("passwd"),
            new PasswordCrypter("otherPasswd")
        };

        encryptedMessages = new byte[passwordCrypters.length][];
        for (int i = 0; i < passwordCrypters.length; i++) {
            encryptedMessages[i] = passwordCrypters[i].encrypt(MESSAGE);
        }
    }

    @Test
    public void testEncrypt() {
        for (byte[] encryptedMessage : encryptedMessages) {
            assertFalse(Arrays.equals(MESSAGE, encryptedMessage));
        }

        assertFalse(Arrays.equals(encryptedMessages[0], encryptedMessages[2]));
        assertFalse(Arrays.equals(encryptedMessages[1], encryptedMessages[2]));
    }

    @Test
    public void testDecrypt() {
        for (int i = 0; i < passwordCrypters.length; i++) {
            assertArrayEquals(MESSAGE, passwordCrypters[i].decrypt(encryptedMessages[i]));
        }

        assertArrayEquals(MESSAGE, passwordCrypters[0].decrypt(encryptedMessages[1]));
        assertArrayEquals(MESSAGE, passwordCrypters[1].decrypt(encryptedMessages[0]));

        try {
            assertFalse(Arrays.equals(MESSAGE, passwordCrypters[0].decrypt(encryptedMessages[2])));
        } catch (CrypterException e) {
            // Anything goes as long as the above statement is not true.
        }

        try {
            assertFalse(Arrays.equals(MESSAGE, passwordCrypters[2].decrypt(encryptedMessages[1])));
        } catch (CrypterException e) {
            // Anything goes as long as the above statement is not true.
        }
    }
}

最佳答案

如果您尝试使用错误的 key 解密 PKCS5 填充数据,然后将其取消填充(由 Cipher 类自动完成),您很可能会收到 BadPaddingException(可能略小于 255/256,大约 99.61%),因为填充具有特殊的结构,在 unpad 期间会被验证,并且很少有键会产生有效的填充。

因此,如果您遇到此异常,请将其捕获并将其视为“错误 key ”。

当您提供错误的密码时也会发生这种情况,然后使用该密码从 keystore 中获取 key ,或者使用 key 生成函数将其转换为 key 。

当然,如果您的数据在传输过程中损坏,也会发生错误填充。

也就是说,有一些关于您的方案的安全说明:

  • 对于基于密码的加密,您应该使用 SecretKeyFactory 和 PBEKeySpec 而不是使用带有 KeyGenerator 的 SecureRandom。原因是 SecureRandom 可能是每个 Java 实现上的不同算法,为您提供不同的 key 。 SecretKeyFactory 以定义的方式执行 key 派生(如果您选择正确的算法,这种方式被认为是安全的)。

  • 不要使用 ECB 模式。它独立加密每个 block ,这意味着相同的纯文本 block 也总是给出相同的密文 block 。

    最好使用安全的 mode of operation ,如 CBC(密码 block 链接)或 CTR(计数器)。或者,使用还包括身份验证的模式,例如 GCM(Galois-Counter 模式)或 CCM(带 CBC-MAC 的计数器),请参阅下一点。

  • 您通常不仅需要保密,还需要身份验证,以确保消息不被篡改。 (这也可以防止对您的密码进行选择密文攻击,即有助于保密。)因此,在您的消息中添加一个 MAC(消息身份验证代码),或使用包含身份验证的密码模式(请参阅前一点)。

  • DES 的有效 key 大小仅为 56 位。这个 key 空间非常小,可以在几个小时内被专门的攻击者暴力破解。如果您通过密码生成 key ,这将变得更快。 此外,DES 的 block 大小仅为 64 位,这增加了链接模式的一些弱点。 改用像 AES 这样的现代算法,它的 block 大小为 128 位, key 大小为 128 位(对于最常见的变体,也存在 196 和 256 的变体)。

关于java - 给定最终 block 未正确填充,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8049872/

相关文章:

c# - Silverlight C# 中的 MD5 或其他加密

java - Java 中 PHP 加密的替代方案

php - mcrypt 字符导致 mysql 错误

java - 制作静态数据源

c# - 通过 COM 互操作从 C++ 抛出到 C# 时丢失异常详细信息?

python - Windows 上的 os.rename 引发 WindowsError 而不是 OSError

安卓java.net.SocketException :socket failed: EACCES (Permission denied)

java - 重新加载后台进程的配置文件

java - java中通过FTP异步下载文件

java - 将 java.util.Date 转换为 yyyy-MM-dd 格式的字符串,而无需创建大量对象