2015-02-08 92 views
0

我一直在盯着這段代碼幾個小時,而且我必須錯過一些愚蠢的東西。因此,非常感謝有人的幫助。首選項和javax.crypto.BadPaddingException:給定最後的塊沒有正確填充

下面是示例代碼,最簡單的我可以得到它repro這個問題。代碼在第一次運行時工作正常,但是在爲IV創建了首選項之後,它會在之後失敗。

我的真實代碼存儲在偏好比這更多,我簡化了很多麻煩拍攝,以便更容易隔離。

我的目標是創建一個類,允許我存儲使用AES 128加密加密的首選項,並使用從用戶提供的密碼派生的密鑰。例如確保首選項。我拿出所有用戶提供的密碼來排除故障。

package com.test; 

import java.io.UnsupportedEncodingException; 
import java.security.AlgorithmParameters; 
import java.security.InvalidAlgorithmParameterException; 
import java.security.InvalidKeyException; 
import java.security.NoSuchAlgorithmException; 
import java.security.SecureRandom; 
import java.security.spec.InvalidParameterSpecException; 
import java.util.Random; 
import java.util.logging.Level; 
import java.util.logging.Logger; 
import java.util.prefs.Preferences; 
import javax.crypto.BadPaddingException; 
import javax.crypto.Cipher; 
import javax.crypto.IllegalBlockSizeException; 
import javax.crypto.KeyGenerator; 
import javax.crypto.SecretKey; 
import javax.crypto.spec.IvParameterSpec; 
import org.apache.commons.codec.binary.Base64; 

public class Settings { 
    private Preferences prefs = null; 
    private byte[] iv = null; 
    private SecretKey secret = null; 
    Cipher cipher = null; 

    public static void main(String[] args){ 
     Settings t = new Settings(); 
     String encText = t.encryptText("HELLO");//Encrypt a value 
     String output = t.decryptText(encText);//Decrypt the value 
     System.out.println(output); //Display the decrypted value. 
    } 

    public Settings(){ 
     try { 
      String parentClass = new Exception().getStackTrace()[1].getClassName();//Really only controls where the prefs go, shouldn't matter. 
      this.prefs = Preferences.userNodeForPackage(Class.forName(parentClass)); 
      Random r = new SecureRandom(); 
      KeyGenerator keyGen = KeyGenerator.getInstance("AES"); 
      keyGen.init(128); // 128 bit key 
      this.secret = keyGen.generateKey(); 

      cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 
     } catch (NoSuchAlgorithmException ex) { 
      Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, ex); 
     } catch (Exception ex) { 
      Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, ex); 
     } 
    } 
     private String encryptText(String plainText){ 
     try { 
      cipher.init(Cipher.ENCRYPT_MODE, this.secret); 
      AlgorithmParameters params = cipher.getParameters(); 

      this.iv = prefs.getByteArray("IV", null); 
      if(this.iv == null){ 
       this.iv = params.getParameterSpec(IvParameterSpec.class).getIV(); 
       prefs.putByteArray("IV", this.iv); 
      } 
      byte[] ciphertext = cipher.doFinal(plainText.getBytes("UTF-8")); 
      String ret = new String(Base64.encodeBase64(ciphertext)); 
      return ret; 
     } catch (InvalidParameterSpecException | IllegalBlockSizeException | BadPaddingException | UnsupportedEncodingException | InvalidKeyException ex) { 
      Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, ex); 
     } 
     return ""; 
    } 
    private String decryptText(String cipherText){ 
     try { 
      this.iv = prefs.getByteArray("IV", null); 
      byte[] cText = Base64.decodeBase64(cipherText); 
      cipher.init(Cipher.DECRYPT_MODE, this.secret, new IvParameterSpec(this.iv)); 
      String ret = new String(cipher.doFinal(cText), "UTF-8"); 
      return ret; 
     } catch (IllegalBlockSizeException | BadPaddingException | UnsupportedEncodingException | InvalidKeyException | InvalidAlgorithmParameterException ex) { 
      Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, ex); 
     } 
     return ""; 
    } 
} 

堆棧跟蹤的是僅在運行2+收到:

Feb 07, 2015 9:02:46 PM com.test.Settings decryptText 

SEVERE: null 
javax.crypto.BadPaddingException: Given final block not properly padded 
    at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:811) 
    at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:676) 
    at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:313) 
    at javax.crypto.Cipher.doFinal(Cipher.java:2087) 
    at com.test.Settings.decryptText(Settings.java:77) 
    at com.test.Settings.main(Settings.java:34) 

------------編輯正確答案----------- - 正如GregS指出的那樣,當偏好存在時,我沒有將IV加載到加密例程中,因此導致不匹配。下面是更新後的問題修復後的加密功能。

private String encryptText(String plainText){ 
     try { 
      this.iv = prefs.getByteArray("IV", null); 
      if(this.iv == null) { //If not set, set the IV 
       cipher.init(Cipher.ENCRYPT_MODE, this.secret); 
       AlgorithmParameters params = cipher.getParameters(); 
       this.iv = params.getParameterSpec(IvParameterSpec.class).getIV(); 
       prefs.putByteArray("IV", this.iv); 
      } else { 
       cipher.init(Cipher.ENCRYPT_MODE, this.secret, new IvParameterSpec(this.iv)); 
      } 

      byte[] ciphertext = cipher.doFinal(plainText.getBytes("UTF-8")); 
      String ret = new String(Base64.encodeBase64(ciphertext)); 
      return ret; 
     } catch (InvalidParameterSpecException | IllegalBlockSizeException | BadPaddingException | UnsupportedEncodingException | InvalidKeyException ex) { 
      Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, ex); 
     } catch (InvalidAlgorithmParameterException ex) { 
      Logger.getLogger(Settings.class.getName()).log(Level.SEVERE, null, ex); 
     } 
     return ""; 
    } 
+0

當您刪除IV參數解密(並完全刪除IV處理)會發生什麼?這將對加密和解密使用默認的IV。也許問題是首選項不是空的。 – 2015-02-08 12:58:14

+0

作爲@ArtjomB。注意,如果'encryptText'中的首選項不爲空,那麼你最終將從首選項中檢索iv,但從不在密碼實例中設置iv。但是,在'decryptText'中,您確實在密碼實例中設置了iv。結果,加密端使用默認的IV(可能全是零),而解密端使用從偏好中獲得的IV。 – 2015-02-08 15:15:28

+0

GregS,你是絕對正確的。如果pref未設置,則使用IV,但如果未設置IV,則我從未加載要使用的IV。這正是我的問題。 現在你指出了這一點,似乎很明顯,但我盯着那些代碼幾個小時。感謝您的幫助! 如果你想發佈這個答案,我會很高興接受它。 – Doug 2015-02-08 23:21:20

回答

0

由於GregS指出,我沒有對IV當偏好存在加載到加密程序,結果出現了不匹配。下面是更新後的問題修復後的加密功能。

private String encryptText(String plainText){ 
    try { 
     this.iv = prefs.getByteArray("IV", null); 
     if(this.iv == null) { //If not set, set the IV 
      cipher.init(Cipher.ENCRYPT_MODE, this.secret); 
      AlgorithmParameters params = cipher.getParameters(); 
      this.iv = params.getParameterSpec(IvParameterSpec.class).getIV(); 
      prefs.putByteArray("IV", this.iv); 
     } else { 
      cipher.init(Cipher.ENCRYPT_MODE, this.secret, new IvParameterSpec(this.iv)); 
     } 

     byte[] ciphertext = cipher.doFinal(plainText.getBytes("UTF-8")); 
     String ret = new String(Base64.encodeBase64(ciphertext)); 
     return ret; 
    } catch (InvalidParameterSpecException | IllegalBlockSizeException | BadPaddingException | UnsupportedEncodingException | InvalidKeyException ex) { 
     Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, ex); 
    } catch (InvalidAlgorithmParameterException ex) { 
     Logger.getLogger(Settings.class.getName()).log(Level.SEVERE, null, ex); 
    } 
    return ""; 
    } 
相關問題