Java 给定的最终块未正确填充
我正在尝试实现基于密码的加密算法,但遇到以下例外情况: javax.crypto.BadPaddingException:给定的最后一个块未正确填充 有什么问题吗 这是我的密码:Java 给定的最终块未正确填充,java,exception,encryption,cryptography,javax.crypto,Java,Exception,Encryption,Cryptography,Javax.crypto,我正在尝试实现基于密码的加密算法,但遇到以下例外情况: javax.crypto.BadPaddingException:给定的最后一个块未正确填充 有什么问题吗 这是我的密码: public class PasswordCrypter { private Key key; public PasswordCrypter(String password) { try{ KeyGenerator generator;
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测试)
公共类密码密码测试{
private static final byte[]MESSAGE=“羊驼太棒了!”.getBytes();
私有密码加密程序[]密码加密程序;
专用字节[][]加密消息;
@以前
公共作废设置(){
passwordCrypters=新的passwordCrypters[]{
新密码密码器(“passwd”),
新密码密码器(“passwd”),
新密码密码器(“其他密码”)
};
encryptedMessages=新字节[passwordCrypters.length][];
for(int i=0;i
根据您使用的加密算法,在加密字节数组之前,您可能必须在末尾添加一些填充字节,以便字节数组的长度是块大小的倍数:
具体来说,在您的案例中,您选择的填充模式是PKCS5,如下所述:
(我假设您在尝试加密时遇到问题)
您可以在实例化密码对象时选择填充模式。支持的值取决于您使用的安全提供程序
顺便问一下,您确定要使用对称加密机制来加密密码吗?单向散列不是更好吗?如果您真的需要能够解密密码,DES是一个相当弱的解决方案,如果您需要使用对称算法,您可能会对使用AES之类的更强大的解决方案感兴趣。如果您试图用错误的密钥解密PKCS5填充的数据,然后取消其填充(这由Cipher类自动完成),您很可能会得到BadPaddingException(可能略小于255/256,约为99.61%),因为填充有一个特殊的结构,在取消填充期间会得到验证,并且很少有键会产生有效的填充 因此,如果出现此异常,请捕获它并将其视为“错误的键”。 当您提供错误的密码时,也可能发生这种情况,然后使用该密码从密钥库获取密钥,或者使用密钥生成函数将其转换为密钥 当然,如果您的数据在传输过程中损坏,也可能发生错误填充 也就是说,您的计划中有一些安全问题:
- 对于基于密码的加密,应该使用SecretKeyFactory和PBEKeySpec,而不是使用SecureRandom with KeyGenerator。原因是SecureRandom可以是每个Java实现上的不同算法,为您提供不同的密钥。SecretKeyFactory以定义的方式进行密钥派生(如果选择正确的算法,这种方式被认为是安全的)
- 不要使用ECB模式。它独立地加密每个块,这意味着相同的纯文本块也总是提供相同的密文块 最好使用安全的,如CBC(密码块链接)或CTR(计数器)。或者,使用也包括身份验证的模式,如GCM(Galois计数器模式)或CCM(带CBC-MAC的计数器),请参阅下一点
- 通常,您不仅需要保密,还需要身份验证,以确保消息不被篡改。(这还可以防止对密码的选定密文攻击,即有助于保密。)因此,请在消息中添加MAC(消息身份验证码),或使用包含身份验证的密码模式(请参阅上一点)
- DES的有效密钥大小只有56位。该密钥空间非常小,可以在几个小时内由专用攻击者强制执行。如果您通过密码生成密钥,这将更快。 此外,DES的块大小只有64位,这在链接模式中增加了一些弱点。 改用AES等现代算法,其块大小为128位,密钥大小为 128位(对于标准变体)
new SecureRandom(key.getBytes())
将在Windows中获得相同的值,而在Linux中则不同。所以在Linux中需要改为
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
secureRandom.setSeed(key.getBytes());
kgen.init(128, secureRandom);
“SHA1PRNG”是使用的算法,您可以参考有关算法的更多信息。当您为签名密钥输入错误的密码时,这也可能是一个问题。因此,您可以发布尝试加密/解密的代码吗?(并检查您尝试解密的字节数组是否不大于块大小)我对Java和密码学非常陌生
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
secureRandom.setSeed(key.getBytes());
kgen.init(128, secureRandom);