给定最终块未正确填充
Posted
技术标签:
【中文标题】给定最终块未正确填充【英文标题】:Given final block not properly padded 【发布时间】:2011-12-24 09:25:55 【问题描述】:我正在尝试实现基于密码的加密算法,但我得到了这个异常:
javax.crypto.BadPaddingException:
可能是什么问题?
这是我的代码:
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.
【问题讨论】:
【参考方案1】:如果您尝试使用错误的密钥解密 PKCS5 填充数据,然后将其取消填充(由 Cipher 类自动完成),您很可能会收到 BadPaddingException(可能略小于 255/256,大约 99.61%),因为填充具有特殊的结构,在 unpad 期间会被验证,并且很少有键会产生有效的填充。
因此,如果您遇到此异常,请将其捕获并将其视为“错误密钥”。
当您提供错误的密码时也会发生这种情况,然后使用该密码从密钥库中获取密钥,或者使用密钥生成函数将其转换为密钥。
当然,如果您的数据在传输过程中损坏,也会发生错误填充。
也就是说,有一些关于您的方案的安全说明:
对于基于密码的加密,您应该使用 SecretKeyFactory 和 PBEKeySpec 而不是使用带有 KeyGenerator 的 SecureRandom。原因是 SecureRandom 可能是每个 Java 实现上的不同算法,为您提供不同的密钥。 SecretKeyFactory 以定义的方式执行密钥派生(如果您选择正确的算法,这种方式被认为是安全的)。
不要使用 ECB 模式。它独立加密每个块,这意味着相同的纯文本块也总是给出相同的密文块。
最好使用安全的mode of operation,例如 CBC(密码块链接)或 CTR(计数器)。或者,使用还包括身份验证的模式,例如 GCM(Galois-Counter 模式)或 CCM(带 CBC-MAC 的计数器),请参阅下一点。
您通常不仅需要保密,还需要身份验证,以确保消息不被篡改。 (这也可以防止对您的密码进行选择密文攻击,即有助于保密。)因此,在您的消息中添加一个 MAC(消息身份验证代码),或使用包含身份验证的密码模式(请参阅前一点)。
DES 的有效密钥大小仅为 56 位。这个密钥空间非常小,可以在几个小时内被专门的攻击者暴力破解。如果您通过密码生成密钥,这将变得更快。 此外,DES 的块大小仅为 64 位,这增加了链接模式的一些弱点。 改用像 AES 这样的现代算法,它的块大小为 128 位,密钥大小为 128 位(对于最常见的变体,也存在 196 和 256 的变体)。
【讨论】:
我只是想确认一下。我是加密新手,这是我的场景,我正在使用 AES 加密。在我的加密/解密函数中,我使用了一个加密密钥。我在解密时使用了错误的加密密钥,我得到了这个javax.crypto.BadPaddingException: Given final block not properly padded
。我应该把它当作一个错误的键吗?
需要明确的是,当为密钥存储文件(例如 .p12 文件)提供错误密码时也会发生这种情况,这就是我刚刚发生的情况。
@WarrenDew “密钥库文件的密码错误”只是“错误密钥”的一个特例。
@kenicky 抱歉,我刚才看到了你的评论……是的,错误的键几乎总是会导致这种效果。 (当然,损坏的数据是另一种可能性。)
@PaŭloEbermann 我同意,但我认为这不一定是显而易见的,因为它与原始帖子中程序员控制密钥和解密的情况不同。不过,我确实发现您的回答足够有用,可以投票。【参考方案2】:
当您为签名密钥输入错误的密码时,这也可能是一个问题。
【讨论】:
这真的是评论,而不是答案。多一点代表,you will be able to post comments. 这不是答案 剧透警报:这就是答案。我在其中一个密码中打错了。【参考方案3】:根据您使用的加密算法,您可能需要在加密字节数组之前在末尾添加一些填充字节,以便字节数组的长度是块大小的倍数:
具体而言,您选择的填充模式是 PKCS5,如下所述: http://www.rsa.com/products/bsafe/documentation/cryptoj35html/doc/dev_guide/group_CJ_SYM__PAD.html
(我假设您在尝试加密时遇到了问题)
您可以在实例化 Cipher 对象时选择填充模式。支持的值取决于您使用的安全提供程序。
顺便问一下,您确定要使用对称加密机制来加密密码吗?单向哈希不是更好吗?如果你真的需要能够解密密码,DES 是一个相当弱的解决方案,如果你需要保持对称算法,你可能有兴趣使用更强大的东西,比如 AES。
【讨论】:
那么您能否发布尝试加密/解密的代码? (并检查您尝试解密的字节数组是否大于块大小) 我对 Java 和密码学都很陌生,所以我仍然不知道更好的加密方法。我只是想完成这个,而不是寻找更好的方法来实现它。 你能更新链接吗,因为它不起作用@fpacifici,我更新了我的帖子我包括了测试加密和解密的 JUnit 测试 已更正(抱歉复制粘贴错误)。无论如何,确实发生了您的问题,因为您使用的密钥与 Paulo 解释的用于加密的密钥不同。发生这种情况是因为在 junit 中使用 @Before 注释的方法在每个测试方法之前执行,因此每次都重新生成密钥。由于密钥是随机初始化的,因此每次都会有所不同。【参考方案4】:由于操作系统的原因,我遇到了这个问题,对于不同平台的 JRE 实现很简单。
new SecureRandom(key.getBytes())
在 Windows 中将获得相同的值,而在 Linux 中则不同。所以在Linux下需要改成
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
secureRandom.setSeed(key.getBytes());
kgen.init(128, secureRandom);
“SHA1PRNG”是使用的算法,你可以参考here了解更多关于算法的信息。
【讨论】:
【参考方案5】:javax.crypto.BadPaddingException: 鉴于最终块未正确填充。
如果在解密过程中使用了错误的密钥,则可能会出现此类问题。
对于我自己来说,当我使用错误的密钥进行解密时会发生这种情况。 它始终区分大小写。因此,请确保您在加密时使用了相同的密钥...... :)
【讨论】:
以上是关于给定最终块未正确填充的主要内容,如果未能解决你的问题,请参考以下文章
加密/解密 - iphone 到 java - BadPaddingException:给定最终块未正确填充
java.io.IOException:无法解密安全内容条目:javax.crypto.BadPaddingException:给定最终块未正确填充