鉴于最后的块没有正确填充
我想实现基于密码的encryptionalgorithm,但我得到这个exception:
javax.crypto.BadPaddingException:给定的最终块未正确填充
可能是什么问题? (我是Java新手。)
这是我的代码:
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; } }
(JUnittesting)
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. } } }
如果尝试使用错误的密钥解密PKCS5填充的数据,然后取消压缩(由Cipher类自动完成),则最有可能得到BadPaddingException(可能略小于255/256,大约为99.61% ),因为填充有一个特殊的结构,在unpad中有效,很less的键会产生一个有效的填充。
所以,如果你得到这个exception,赶上它,把它视为“错误的关键”。
当您提供一个错误的密码,然后用于从密钥库中获取密钥,或者使用密钥生成function将其转换为密钥时,也会发生这种情况。
当然,如果你的数据在传输中被破坏,也可能发生错误的填充。
这就是说,你的计划有一些安全的评论:
-
对于基于密码的encryption,您应该使用SecretKeyFactory和PBEKeySpec,而不是与KeyGenerator一起使用SecureRandom。 原因是SecureRandom在每个Java实现上可能是不同的algorithm,给你一个不同的关键。 SecretKeyFactory以确定的方式进行密钥派生(如果select正确的algorithm,则认为安全)。
-
不要使用ECB模式。 它独立地encryption每个块,这意味着相同的明文块也总是给出相同的密文块。
最好使用安全的操作模式 ,如CBC(密码块链接)或CTR(计数器)。 或者,也可以使用包含authentication的模式,如GCM(Galois-Counter模式)或CCM(使用CBC-MAC的计数器),请参阅下一点。
-
通常情况下,您不仅需要保密,还需要身份validation,以确保消息不被篡改。 (这也可以防止select密文攻击你的密码,即有助于保密)。因此,添加一个MAC(消息authentication码)到你的消息,或者使用一个包含authentication的密码模式(见前面的点)。
-
DES的有效密钥大小只有56位。 这个关键空间是相当小的,它可以被一个专门的攻击者在几个小时内暴力破解。 如果您通过密码生成密钥,则会更快。 而且,DES的块大小只有64位,这在链接模式中增加了一些弱点。 使用像AES这样的现代algorithm,其块大小为128位,密钥大小为128位(对于标准variables)。
根据您使用的encryptionalgorithm,您可能需要在encryption字节数组之前添加一些填充字节,以使字节数组的长度为块大小的倍数:
特别是在你的情况下,你select的填充模式是PKCS5,这里描述: http : //www.rsa.com/products/bsafe/documentation/cryptoj35html/doc/dev_guide/group_ CJ _SYM__PAD.html
(我假设你尝试encryption时遇到问题)
实例化Cipher对象时,可以select填充模式。 支持的值取决于您正在使用的安全提供程序。
顺便问一下,您确定要使用对称encryption机制来encryption密码吗? 不会是单向散列更好? 如果你真的需要能够解密密码,DES是一个相当薄弱的解决scheme,如果你需要使用对称algorithm,你可能有兴趣使用像AES这样更强大的东西。