java 某些调用 doFinal 时出现 badPaddingException。不是全部。相同的输入
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4895773/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
badPaddingException on some call of the doFinal. Not all. Same input
提问by Antoine Claval
I use a javax.crypto.cipher for encrypt and decrypt some data. It's working well. But sometimes, the decryption faill with a badPaddingException. If i compare a succefull call with a failling call, the input given to the cipher are the same, and the cipher is initialised the same way.
我使用 javax.crypto.cipher 来加密和解密一些数据。它运作良好。但有时,解密失败并出现 badPaddingException。如果我将成功调用与失败调用进行比较,则提供给密码的输入是相同的,并且密码的初始化方式相同。
that how i instanciate my cipher
那我是如何实例化我的密码的
dcipher = Cipher.getInstance("PBEWithMD5AndDES");
KeySpec keySpec = new PBEKeySpec(passPhrase.toCharArray(), salt, iterationCount);
SecretKey key = SecretKeyFactory.getInstance("PBEWithMD5AndDES").generateSecret(keySpec);
dcipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
That how i use it
那我如何使用它
dec = Base64.decode(str) ;
byte[] utf8 = dcipher.doFinal(dec);
The exception is raised on the doFinal.
在 doFinal 上引发异常。
Any idea ?
任何的想法 ?
Thanks !
谢谢 !
Oh, btw, i use bouncyCastle as a provider, and add it on top on the list with
哦,顺便说一句,我使用 bouncyCastle 作为提供者,并将其添加到列表的顶部
Security.insertProviderAt(new org.bouncycastle.jce.provider.BouncyCastleProvider(), 1);
For the sake of the completeness, and because the problem still appeart from time to time. Here is the complete class.
为了完整起见,也因为问题仍然不时出现。这是完整的课程。
public class EncryptDecryptUtil {
/** Encryption Cipher */
private static Cipher ecipher;
/** Decription Cipher */
private static Cipher dcipher;
private static Logger logger = Logger.getLogger(EncryptDecryptUtil.class);
/**
* Constructor used to create this object. Responsible for setting and initializing this object's encrypter and
* decrypter Cipher instances given a Secret Key and algorithm.
*
* @param key Secret Key used to initialize both the encrypter and decrypter instances.
* @param algorithm Which algorithm to use for creating the encrypter and decrypter instances.
*/
public EncryptDecryptUtil(SecretKey key, String algorithm) {
Security.insertProviderAt(new org.bouncycastle.jce.provider.BouncyCastleProvider(), 1);
try {
ecipher = Cipher.getInstance(algorithm);
dcipher = Cipher.getInstance(algorithm);
ecipher.init(Cipher.ENCRYPT_MODE, key);
dcipher.init(Cipher.DECRYPT_MODE, key);
} catch (NoSuchPaddingException e) {
System.out.println("EXCEPTION: NoSuchPaddingException");
} catch (NoSuchAlgorithmException e) {
System.out.println("EXCEPTION: NoSuchAlgorithmException");
} catch (InvalidKeyException e) {
System.out.println("EXCEPTION: InvalidKeyException");
}
}
/**
* Constructor used to create this object. Responsible for setting and initializing this object's encrypter and
* decrypter Chipher instances given a Pass Phrase and algorithm.
*
* @param passPhrase Pass Phrase used to initialize both the encrypter and decrypter instances.
*/
public EncryptDecryptUtil(String passPhrase) {
Security.insertProviderAt(new org.bouncycastle.jce.provider.BouncyCastleProvider(), 1);
// 8-bytes Salt
byte[] salt = { (byte) 0xB9, (byte) 0x8B, (byte) 0xD8, (byte) 0x31, (byte) 0x55, (byte) 0x24, (byte) 0xF3, (byte) 0x13 };
// Iteration count
int iterationCount = 19;
try {
// Generate the secret key associated to the passphrase.
KeySpec keySpec = new PBEKeySpec(passPhrase.toCharArray(), salt, iterationCount);
SecretKey key = SecretKeyFactory.getInstance("PBEWithMD5AndDES").generateSecret(keySpec);
// Get instance of the cipher
ecipher = Cipher.getInstance("PBEWithMD5AndDES");
dcipher = Cipher.getInstance("PBEWithMD5AndDES");
// Prepare the parameters to the cipthers
AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, iterationCount);
ecipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
dcipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
} catch (InvalidAlgorithmParameterException e) {
logger.error("during encrypter instantiation",e);
} catch (InvalidKeySpecException e) {
logger.error("during encrypter instantiation",e);
} catch (NoSuchPaddingException e) {
logger.error("during encrypter instantiation",e);
} catch (NoSuchAlgorithmException e) {
logger.error("during encrypter instantiation",e);
} catch (InvalidKeyException e) {
logger.error("during encrypter instantiation",e);
}
}
/**
* Takes a single String as an argument and returns an Encrypted version of that String.
*
* @param str String to be encrypted
* @return <code>String</code> Encrypted version of the provided String
*/
public String encrypt(String str) {
try {
// Encode the string into bytes using utf-8
byte[] utf8 = str.getBytes("UTF8");
// Encrypt
byte[] enc = ecipher.doFinal(utf8);
// Encode bytes to base64 to get a string
return new String( Base64.encode(enc), "UTF8");
} catch (BadPaddingException e) {
logger.error("during encryption : ",e);
} catch (IllegalBlockSizeException e) {
logger.error("during encryption : ",e);
} catch (UnsupportedEncodingException e) {
logger.error("during encryption : ",e);
}
return new String();
}
/**
* Takes a encrypted String as an argument, decrypts and returns the decrypted String.
*
* @param str Encrypted String to be decrypted
* @return <code>String</code> Decrypted version of the provided String
*/
public String decrypt(String str) {
byte[] dec = new byte[0];
try {
// Decode base64 to get bytes. Not sure to understand why.
dec = Base64.decode(str) ;
// Decrypt
byte[] utf8 = dcipher.doFinal(dec);
// Decode using utf-8
return new String(utf8, "UTF8");
} catch (BadPaddingException e) {
logger.error("error during decryption. String to decode was : "+str + " byte array to decode was : "+ Arrays.toString(dec) ,e);
} catch (IllegalBlockSizeException e) {
logger.error("during decryption : ",e);
} catch (UnsupportedEncodingException e) {
logger.error("during decryption : ",e);
}
return new String();
}
}
Edit :I would like to stress this 2 points :
编辑:我想强调这 2 点:
- the same input will sometime fail / sometime be sucessfully decrypted. ( i know that thanks to the logs in the BadPaddingException )
- this main calling the decrypt method 1 000 000 000 times doest reproduce the issus.
- 相同的输入有时会失败/有时会成功解密。(我知道这要归功于 BadPaddingException 中的日志)
- 这个主要调用解密方法 1 000 000 000 次并没有重现问题。
...
...
for( int i = 0 ; i<1000000000 ; i++){
EncryptDecryptUtil encryptDecript = new EncryptDecryptUtil("pass");
if ( !"YES".equals(encryptDecript.decrypt("Q3qWLKo6yJY="))){
System.out.println("Fail at call " + i);
throw new InvalidParameterException() ;
}
}
So maybe it could come from the way i use the EncryptDecryptUtils class ? It's a field of a Spring bean, instancied one time.
所以也许它可能来自我使用 EncryptDecryptUtils 类的方式?它是 Spring bean 的一个字段,实例化一次。
com.myStuff.dlm.cryptography.EncryptDecryptUtil error during decryption. String to decode was : Q3qWLKo6yJY= byte array to decode was : [114, 52, -52, -54, 82, 87, 124, 84]
javax.crypto.BadPaddingException: Given final block not properly padded
at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
at com.sun.crypto.provider.SunJCE_ab.b(DashoA13*..)
at com.sun.crypto.provider.PBEWithMD5AndDESCipher.engineDoFinal(DashoA13*..)
at javax.crypto.Cipher.doFinal(DashoA13*..)
at com.dvidea.dlm.cryptography.EncryptDecryptUtil.decrypt(EncryptDecryptUtil.java:166)
回答by ATorras
Just an idea: Maybe you are jumpingamong different providers (Sun/BC/etc)
只是一个想法:也许你在不同的供应商之间跳跃(Sun/BC/etc)
Regards.
问候。
回答by ykaganovich
Some thoughts:
一些想法:
The stack trace indicates that the BouncyCastle provider might not be getting picked up. You can try to pass the provider explicitly to the Cipher and the KeyFactory.
堆栈跟踪表明 BouncyCastle 提供程序可能未被选中。您可以尝试将提供程序显式传递给 Cipher 和 KeyFactory。
The code you provided is the only code that's running, right? No other threads in the process?
您提供的代码是唯一正在运行的代码,对吗?进程中没有其他线程?
I wonder if you might be confusing the cipher by specifying the salt and the iterationCount both on the KeySpec, and ParameterSpec. The examples I see ( encrypt PBEWithMD5AndDES in j2me, http://cs.saddleback.edu/rwatkins/CS4B/Crypto/FileEncryptor.html), do not specify those when creating the KeySpec.
我想知道您是否会通过在 KeySpec 和 ParameterSpec 上指定 salt 和迭代计数来混淆密码。我看到的示例(在 j2me 中加密 PBEWithMD5AndDES,http: //cs.saddleback.edu/rwatkins/CS4B/Crypto/FileEncryptor.html),在创建 KeySpec 时没有指定这些示例。
回答by Andrew
I'm going to go out on a limb and suggest that it's a thread-safety issue.
Try adding synchronized
blocks around the ecipher/dcipher#doFinal
calls, you can easily use the associated Cipher object as an object to synchronize with.
我打算出去,并建议这是一个线程安全问题。尝试synchronized
在ecipher/dcipher#doFinal
调用周围添加块,您可以轻松地将关联的 Cipher 对象用作要与之同步的对象。
byte[] enc;
synchronized (ecipher) {
enc = ecipher.doFinal(utf8);
}
回答by Luzifer42
Maybe it is related to this bug: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4414138
也许它与这个错误有关:http: //bugs.sun.com/bugdatabase/view_bug.do?bug_id=4414138
Bottom line: Don't use String to store encrypted text!
底线:不要使用 String 来存储加密文本!
Maybe this could fix it:
也许这可以解决它:
dec = Base64.decode(str.getBytes("UTF-8"));
回答by Julius Musseau
badPaddingException usually means the password was wrong. Using "wrong_password" as the password recreates the error:
badPaddingException 通常意味着密码错误。使用“wrong_password”作为密码会重新创建错误:
EncryptDecryptUtil encryptDecript = new EncryptDecryptUtil("wrong_password");
encryptDecript.decrypt("Q3qWLKo6yJY=");
Results:
结果:
error during decryption. String to decode was : Q3qWLKo6yJY=
byte array to decode was : [67, 122, -106, 44, -86, 58, -56, 106]
javax.crypto.BadPaddingException: Given final block not properly padded
回答by ZoFreX
This is a bit of a shot in the dark, but try instantiating your cipher like this:
这有点像在黑暗中拍摄,但请尝试像这样实例化您的密码:
dcipher = Cipher.getInstance("PBEWithMD5AndDES/CBC/PKCS5Padding");