对doFinal的一些调用有badPaddingException。 不是全部。 相同的输入

我使用javax.crypto.cipher来加密和解密一些数据。 它运作良好。 但有时,解密会出现badPaddingException。 如果我将一个成功的呼叫与一个有效的呼叫进行比较,则给予密码的输入是相同的,并且密码以相同的方式初始化。

我如何实现我的密码

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); 

我怎么用它

 dec = Base64.decode(str) ; byte[] utf8 = dcipher.doFinal(dec); 

在doFinal上引发了exception。

任何的想法 ?

谢谢 !

哦,顺便说一句,我使用bouncyCastle作为提供者,并将其添加到列表的顶部

 Security.insertProviderAt(new org.bouncycastle.jce.provider.BouncyCastleProvider(), 1); 

为了完整,并且因为问题仍然不时出现。 这是完整的课程。

 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 String 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 String 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(); } } 

编辑:我想强调这2点:

  • 相同的输入有时会失败/有时会被成功解密。 (我知道这要归功于BadPaddingException中的日志)
  • 这主要调用解密方法1 000 000 000次doest再现issus。

 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() ; } } 

也许它可能来自我使用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) 

只是一个想法:也许你在不同的提供商之间跳跃 (Sun / BC / etc)

问候。

一些想法:

堆栈跟踪表明BouncyCastle提供程序可能未被提取。 您可以尝试将提供程序显式传递给Cipher和KeyFactory。

您提供的代码是唯一正在运行的代码,对吧? 这个过程中没有其他线程吗?

我想知道你是否可能通过在KeySpec和ParameterSpec上指定salt和iterationCount来混淆密码。 我看到的例子( 在j2me中加密PBEWithMD5AndDES , http: //cs.saddleback.edu/rwatkins/CS4B/Crypto/FileEncryptor.html ),在创建KeySpec时不指定它们。

也许它与这个bug有关: http : //bugs.sun.com/bugdatabase/view_bug.do?video_id = 4414138

底线:不要使用String来存储加密文本!

也许这可以解决它:

 dec = Base64.decode(str.getBytes("UTF-8")); 

这在黑暗中有点像,但尝试实例化你的密码:

 dcipher = Cipher.getInstance("PBEWithMD5AndDES/CBC/PKCS5Padding"); 

badPaddingException通常表示密码错误。 使用“wrong_password”作为密码重新创建错误:

 EncryptDecryptUtil encryptDecript = new EncryptDecryptUtil("wrong_password"); encryptDecript.decrypt("Q3qWLKo6yJY="); 

结果:

 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 

我打算走出困境,并建议这是一个线程安全问题。 尝试在ecipher/dcipher#doFinal调用周围添加synchronized块,您可以轻松地使用关联的Cipher对象作为对象进行同步。

 byte[] enc; synchronized (ecipher) { enc = ecipher.doFinal(utf8); }