给定的最终块没有正确填充

我试图实现基于密码的加密算法,但是我得到了这个例外:

异常: 给定的最终块没有正确填充

有什么问题吗?

这是我的代码:

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.
}
}
}
406157 次浏览

根据所使用的加密算法,在对字节数组进行加密之前,可能需要在末尾添加一些填充字节,这样字节数组的长度就是块大小的倍数:

特别是在您的情况下,您选择的填充模式是 PKCS5,如下所述: Http://www.rsa.com/products/bsafe/documentation/cryptoj35html/doc/dev_guide/group_abc0_sym__pad.html

(我假设您在尝试加密时遇到了这个问题)

可以在实例化 Cipher 对象时选择填充架构。支持的值取决于您正在使用的安全提供程序。

顺便问一下,您确定要使用对称加密机制来加密密码吗?单向大麻不是更好吗?如果您真的需要能够解密密码,DES 是一个相当弱的解决方案,如果您需要使用对称算法,您可能有兴趣使用更强大的东西,如 AES。

如果你试图用错误的密钥解密 PKCS5加密的数据,然后取消加密(这是由 Cipher 类自动完成的) ,你很可能会得到 BadPaddingException (可能略低于255/256,约99.61%) ,因为填充有一个特殊的结构,在取消加密期间验证,很少的密钥会产生一个有效的填充。

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

如果提供了错误的密码,然后使用该密码从密钥存储库获取密钥,或者使用密钥生成函数将其转换为密钥,也会发生这种情况。

当然,如果数据在传输过程中损坏,也可能会出现糟糕的填充。

也就是说,有一些关于你的计划的安全评论:

  • 对于基于密码的加密,应该使用 SecureRandom 和 KeyGenerator,而不是 SecureRandom。原因是 SecureRandom 可能是每个 Java 实现上的不同算法,给您一个不同的密钥。Secretariat KeyFactory 以一种定义的方式(如果选择了正确的算法,则以一种被认为是安全的方式)进行密钥派生。

  • 不要使用欧洲央行模式。它独立地加密每个块,这意味着相同的纯文本块也总是提供相同的加密文本块。

    最好使用安全的 运作模式,如 CBC (密码块链接)或 CTR (计数器)。或者,使用一种也包含身份验证的模式,如 GCM (Galois-Counter 模式)或 CCM (CBC-MAC 计数器) ,参见下一点。

  • 您通常不仅需要保密性,还需要身份验证,以确保消息没有被篡改。(这也可以防止选择密文攻击您的密码,即帮助保密。)因此,添加一个 MAC (讯息鑑别码)到您的消息,或使用包括认证的密码模式(见前一点)。

  • DES 的有效密钥大小只有56位。这个密钥空间非常小,它可以在几个小时内被专门的攻击者强迫使用。如果您使用密码生成密钥,那么这将变得更快。 此外,DES 的块大小只有64位,这增加了链接模式的一些弱点。 使用像 AES 这样的现代算法,它的块大小为128位,密钥大小为128位(对于最常见的变体,还存在196和256的变体)。

由于操作系统本人遇到了这个问题,简单到不同的平台关于 JRE 的实现。

new SecureRandom(key.getBytes())

将在 Windows 中得到相同的值,而在 Linux 中不同。因此在 Linux 中需要改为

SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
secureRandom.setSeed(key.getBytes());
kgen.init(128, secureRandom);

“ SHA1PRNG”是使用的算法,你可以参考 给你了解更多关于算法的信息。

当您为您的签名密钥输入错误的密码时,这也可能是一个问题。

例外: 给定的最终块没有正确填充。

如果在解密过程中使用了错误的密钥,就会出现这样的问题。

对于我自己来说,当我使用错误的密钥进行解密时,就会发生这种情况。 它总是区分大小写的。因此,在加密... . : 时,请确保使用了相同的密钥

如果您确定所有配置都正确,那么可能会因为发送一个空值作为加密文本或平面文本来加密而导致此异常。