javax.crypto.BadPaddingException:填充块有时会损坏

时间:2019-04-18 18:18:36

标签: java encryption cryptography sharedpreferences

我有以下用于加密的代码

public static String encrypt(String value, char[] secret) {
        try {
            final byte[] bytes = value != null ? value.getBytes(StandardCharsets.UTF_8) : new byte[0];
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
            SecretKey key = keyFactory.generateSecret(new PBEKeySpec(secret));
            Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
            pbeCipher.init(Cipher.ENCRYPT_MODE, key, new PBEParameterSpec(IsoGame.$().crossPlatformManager.getCrossPlatformUtilsInstance().getDeviceUniqueIdentifier().getBytes(StandardCharsets.UTF_8), 20));
            return new String(Base64.encodeBase64(pbeCipher.doFinal(bytes)), StandardCharsets.UTF_8);

        } catch (Exception e) {
            e.printStackTrace();
        }
        return value;

    }

以及以下用于解密的代码。

public static String decrypt(String value, char[] secret) {
        try {
            final byte[] bytes = value != null ? Base64.decodeBase64(value.getBytes(StandardCharsets.UTF_8)) : new byte[0];
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
            SecretKey key = keyFactory.generateSecret(new PBEKeySpec(secret));
            Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
            pbeCipher.init(Cipher.DECRYPT_MODE, key, new PBEParameterSpec(IsoGame.$().crossPlatformManager.getCrossPlatformUtilsInstance().getDeviceUniqueIdentifier().getBytes(StandardCharsets.UTF_8), 20));
            return new String(pbeCipher.doFinal(bytes), StandardCharsets.UTF_8);

        } catch (Exception e) {
            e.printStackTrace();
        }
        return value;

    }

但是,有时会在

处引发异常
pbeCipher.doFinal(bytes)

在解密方法中。

例外是javax.crypto.BadPaddingException: pad block corrupted

这很奇怪,因为有时会以相同的值获取此异常。

有什么想法吗? 谢谢。

2 个答案:

答案 0 :(得分:2)

最可能的原因就是提供的密码错误。如果提供了错误的密码,则得出错误的密钥。然后,密文将被解密为垃圾明文。仅当抛出填充异常时才会注意到这一点:随机填充字节很可能会失败。

您可以例如首先通过使用派生密钥对已知数据执行HMAC来验证派生密钥正确。另外,使用某种经过身份验证的加密是一个好主意,这样,如果密钥或数据 错误或被破坏,解密确实会失败。如果您不走运,那么-此时-数据将解密,填充将成功,并且您将得到垃圾明文。

当然,您最好升级到PBKDF2进行密钥派生,并将AES升级到例如AES-GCM代替DES。当前,即使您使用强密码,您的加密也是完全不安全的。

答案 1 :(得分:1)

您的问题是

IsoGame.$().crossPlatformManager.getCrossPlatformUtilsInstance().getDeviceUniqueIdentifier().getBytes(StandardCharsets.UTF_8)

我已经多次运行以下代码,并且没有发生异常,并且解密的数据等于“ Hello there!”:

public static void main(String[] args)
    {
        new CryptographyError();
    }

    private CryptographyError()
    {
        char[] secret = "MySecret".toCharArray();
        String mesasge = "Hello there!";
        EncryptedData encryptedData = encrypt(mesasge, secret);
        System.out.println("ENCRYPTED " + encryptedData.encryptedString);
        String decrypted = decrypt(encryptedData, secret);
        System.out.println("DECRYPTED " + decrypted);
    }

    private static final SecureRandom RANDOM = new SecureRandom();

    public static EncryptedData encrypt(String value, char[] secret) {
        try {
            final byte[] bytes = value != null ? value.getBytes(StandardCharsets.UTF_8) : new byte[0];
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
            SecretKey key = keyFactory.generateSecret(new PBEKeySpec(secret));
            Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
            byte[] salt = new byte[8];
            RANDOM.nextBytes(salt);
            pbeCipher.init(Cipher.ENCRYPT_MODE, key, new PBEParameterSpec(salt, 20));
            return new EncryptedData(salt, new String(Base64.getEncoder().encode(pbeCipher.doFinal(bytes)), StandardCharsets.UTF_8));

        } catch (Exception e) {
            e.printStackTrace();
            System.out.println(value);
        }
        return null;

    }

    public static String decrypt(EncryptedData encryptedData, char[] secret) {
        try {
            String value = encryptedData.encryptedString;
            byte[] salt = encryptedData.salt;
            final byte[] bytes = value != null ? Base64.getDecoder().decode(value.getBytes(StandardCharsets.UTF_8)) : new byte[0];
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
            SecretKey key = keyFactory.generateSecret(new PBEKeySpec(secret));
            Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
            pbeCipher.init(Cipher.DECRYPT_MODE, key, new PBEParameterSpec(salt, 20));
            return new String(pbeCipher.doFinal(bytes), StandardCharsets.UTF_8);

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

    }

    private static class EncryptedData
    {
        private final byte[] salt;
        private final String encryptedString;

        private EncryptedData(byte[] salt, String encryptedString)
        {
            this.salt = salt;
            this.encryptedString = encryptedString;
        }
    }

我的代码和您的代码之间的唯一主要区别是

IsoGame.$().crossPlatformManager.getCrossPlatformUtilsInstance().getDeviceUniqueIdentifier().getBytes(StandardCharsets.UTF_8)

表示在加密和解密时不得返回相同的值。 另外,如果您想测试一下,可以只改变它们之间的盐,并注意再次抛出异常。

Maarten Bodewes还为您提供了有关如何改进代码的一些好的说明。