2017-03-12 114 views
1

我對某些數據傳輸使用了加密類。 我不斷收到此錯誤:由於填充導致的加密問題?

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:2131) 
at com.casadelgato.util.Encryption.decrypt(Encryption.java:138) 
at com.casadelgato.util.Encryption.decryptBase64(Encryption.java:124) 
at com.casadelgato.util.Encryption.decryptBase64ToString(Encryption.java:109) 
at com.casadelgato.util.Encryption.main(Encryption.java:156) 

奇怪的是,我不,如果我正在使用相同的加密對象我加密解密得到它。當新的加密對象嘗試解密時,我只會收到錯誤消息。 我已經在下面的代碼中使用main()重現了這一點。

第二次解密呼叫失敗。 顯然密碼之間持有會話狀態?

我該如何解決這個問題,以便另一個程序可以解密其他地方加密的內容?

package com.casadelgato.util; 

import java.io.UnsupportedEncodingException; 
import java.security.AlgorithmParameters; 
import java.security.InvalidAlgorithmParameterException; 
import java.security.InvalidKeyException; 
import java.security.NoSuchAlgorithmException; 
import java.security.spec.InvalidKeySpecException; 
import java.security.spec.InvalidParameterSpecException; 
import java.security.spec.KeySpec; 
import java.util.Arrays; 
import javax.crypto.BadPaddingException; 
import javax.crypto.Cipher; 
import javax.crypto.IllegalBlockSizeException; 
import javax.crypto.NoSuchPaddingException; 
import javax.crypto.SecretKey; 
import javax.crypto.SecretKeyFactory; 
import javax.crypto.spec.IvParameterSpec; 
import javax.crypto.spec.PBEKeySpec; 
import javax.crypto.spec.SecretKeySpec; 
import org.apache.commons.codec.binary.Base64; 

public class Encryption { 
    private final byte[]  SALT = { 
           (byte) 0x26, (byte) 0xe4, (byte) 0x11, (byte) 0xa3, 
           (byte) 0x07, (byte) 0xc6, (byte) 0x55, (byte) 0x42 
    }; 

    private Cipher   ecipher; 
    private Cipher   dcipher; 

    public Encryption(String password) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, InvalidParameterSpecException, 
          InvalidAlgorithmParameterException { 
     SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); 

     KeySpec spec = new PBEKeySpec(password.toCharArray(), SALT, 65536, 128); 
     SecretKey tmp = factory.generateSecret(spec); 
     System.out.println("Encryption: " + Arrays.toString(tmp.getEncoded())); 

     SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES"); 

     ecipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 
     ecipher.init(Cipher.ENCRYPT_MODE, secret); 

     AlgorithmParameters params = ecipher.getParameters(); 
     byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV(); 

     dcipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 
     dcipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv)); 
    } 

    /** 
    * Encrypt the string and return the data encoded in base64 
    * 
    * @param encrypt String to encrypt 
    * @return base64 coded encrypted string 
    * @throws UnsupportedEncodingException 
    * @throws BadPaddingException 
    * @throws IllegalBlockSizeException 
    * @throws Exception 
    */ 
    public byte[] encryptStringToBase64(String encrypt) throws UnsupportedEncodingException, IllegalBlockSizeException, BadPaddingException { 
     byte[] bytes = encrypt.getBytes("UTF8"); 
     return encryptToBase64(bytes); 
    } 

    /** 
    * Encrypt a block of data and encode to Base64 
    * 
    * @param bytes 
    * @return base64 encoded encrypted data 
    * @throws BadPaddingException 
    * @throws IllegalBlockSizeException 
    * @throws Exception 
    */ 
    public byte[] encryptToBase64(byte[] bytes) throws IllegalBlockSizeException, BadPaddingException { 
     byte[] encrypted = encrypt(bytes); 
     return Base64.encodeBase64(encrypted); 
    } 

    /** 
    * Encrypt a block of data 
    * 
    * @param plain 
    * @return encryped data 
    * @throws BadPaddingException 
    * @throws IllegalBlockSizeException 
    * @throws Exception 
    */ 
    public byte[] encrypt(byte[] plain) throws IllegalBlockSizeException, BadPaddingException { 
     return ecipher.doFinal(plain); 
    } 

    /** 
    * Decrypt a string that was encrypted and coded in base64 
    * 
    * @param base64 
    * @return 
    * @throws BadPaddingException 
    * @throws IllegalBlockSizeException 
    * @throws UnsupportedEncodingException 
    * @throws Exception 
    */ 
    public String decryptBase64ToString(byte[] base64) throws IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException { 
     byte[] decrypted = decryptBase64(base64); 
     return new String(decrypted, "UTF8"); 
    } 

    /** 
    * Decrypt a Base64 encoded block 
    * 
    * @param base64 
    * @return 
    * @throws BadPaddingException 
    * @throws IllegalBlockSizeException 
    * @throws Exception 
    */ 
    public byte[] decryptBase64(byte[] base64) throws IllegalBlockSizeException, BadPaddingException { 
     byte[] decodedData = Base64.decodeBase64(base64); 
     byte[] decrypted = decrypt(decodedData); 
     return decrypted; 
    } 

    /** 
    * Decrypt a binary array. 
    * 
    * @param encrypt 
    * @return 
    * @throws BadPaddingException 
    * @throws IllegalBlockSizeException 
    * @throws Exception 
    */ 
    public byte[] decrypt(byte[] encrypt) throws IllegalBlockSizeException, BadPaddingException { 
     return dcipher.doFinal(encrypt); 
    } 

    public static void main(String[] args) throws Exception { 
     String messages[] = { "GETP", "Testing stuff that is longer" }; 
     String password = "SanityLost"; 

     try { 
      Encryption app = new Encryption(password); 
      Encryption app1 = new Encryption(password); 

      for (String message : messages) { 
       byte[] encrypted = app.encryptStringToBase64(message); 
       System.out.println("Encrypted string is: " + new String(encrypted, "UTF-8") + ", " + encrypted.length); 

       String decrypted = app.decryptBase64ToString(encrypted); 
       System.out.println("Decrypted string is: " + decrypted); 

       decrypted = app1.decryptBase64ToString(encrypted); 
       System.out.println("App1 Decrypted string is: " + decrypted); 
      } 

     } catch (Exception e1) { 
      e1.printStackTrace(); 
     } 

     return; 
    } 
} 

回答

3

dcipher對象獲取從ecipher對象其四。問題在於IV不依賴於密碼或密鑰。如果你沒有自己設置它,它會隨機生成。如果您創建第二個Encryption實例,您將得到不同於解密的IV。

IV不應該是祕密的,所以你可以簡單地將它與密文一起發送。由於IV總是具有相同的長度(AES/CBC爲16字節),所以您可以簡單地將它寫在密文前並在解密期間將其讀回。你需要爲此重做你的全班。請注意,隨機IV對於語義安全性非常重要。

雖然我們在這。您還應該爲每個加密隨機化salt。如果我們假設該鹽始終是8字節,IV始終是16個字節長,可以很容易地設計出以下格式的加密的消息:

salt | IV | ciphertext 

這裏|意味着串聯。

您不需要兩個Cipher實例。只是使用一個和init它用於加密或解密之前使用,而不是在構造函數內。

+0

感謝,這正是我需要知道! 我編輯了問題以包含我更新的課程。 – CasaDelGato

+0

好的,做到了!謝謝! – CasaDelGato

1

由於Arjom的回答,我已經固定的代碼爲:

package com.casadelgato.util; 

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.InvalidKeySpecException; 
import java.security.spec.InvalidParameterSpecException; 
import java.security.spec.KeySpec; 
import javax.crypto.BadPaddingException; 
import javax.crypto.Cipher; 
import javax.crypto.IllegalBlockSizeException; 
import javax.crypto.NoSuchPaddingException; 
import javax.crypto.SecretKey; 
import javax.crypto.SecretKeyFactory; 
import javax.crypto.spec.IvParameterSpec; 
import javax.crypto.spec.PBEKeySpec; 
import javax.crypto.spec.SecretKeySpec; 
import org.apache.commons.codec.binary.Base64; 

/** 
* General data encryption/decryption handling. Can do Strings or byte[]. 
* 
* @author John Lussmyer 
*/ 
public class Encryption { 
    private String   password; 
    private SecretKeyFactory factory; 

    public Encryption(String password) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, InvalidParameterSpecException, 
          InvalidAlgorithmParameterException { 
     this.password = password; 
     factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); 

     return; 
    } 

    /** 
    * Encrypt the string and return the data encoded in base64 
    * 
    * @param encrypt String to encrypt 
    * @return base64 coded encrypted string 
    * @throws UnsupportedEncodingException 
    * @throws BadPaddingException 
    * @throws IllegalBlockSizeException 
    * @throws InvalidParameterSpecException 
    * @throws NoSuchPaddingException 
    * @throws NoSuchAlgorithmException 
    * @throws InvalidKeySpecException 
    * @throws InvalidKeyException 
    * @throws Exception 
    */ 
    public byte[] encryptStringToBase64(String encrypt) throws UnsupportedEncodingException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException, InvalidKeySpecException, 
             NoSuchAlgorithmException, NoSuchPaddingException, InvalidParameterSpecException { 
     byte[] bytes = encrypt.getBytes("UTF8"); 
     return encryptToBase64(bytes); 
    } 

    /** 
    * Encrypt a block of data and encode to Base64 
    * 
    * @param bytes 
    * @return base64 encoded encrypted data 
    * @throws BadPaddingException 
    * @throws IllegalBlockSizeException 
    * @throws InvalidParameterSpecException 
    * @throws NoSuchPaddingException 
    * @throws NoSuchAlgorithmException 
    * @throws InvalidKeySpecException 
    * @throws InvalidKeyException 
    * @throws Exception 
    */ 
    public byte[] encryptToBase64(byte[] bytes) throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, InvalidKeySpecException, NoSuchAlgorithmException, 
            NoSuchPaddingException, InvalidParameterSpecException { 
     byte[] encrypted = encrypt(bytes); 
     return Base64.encodeBase64(encrypted); 
    } 

    /** 
    * Encrypt a block of data 
    * 
    * @param plain 
    * @return encryped data 
    * @throws BadPaddingException 
    * @throws IllegalBlockSizeException 
    * @throws InvalidParameterSpecException 
    * @throws NoSuchPaddingException 
    * @throws NoSuchAlgorithmException 
    * @throws InvalidKeySpecException 
    * @throws InvalidKeyException 
    * @throws Exception 
    */ 
    public byte[] encrypt(byte[] plain) throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, InvalidKeySpecException, NoSuchAlgorithmException, NoSuchPaddingException, 
           InvalidParameterSpecException { 
     byte[] salt = new byte[8]; 
     byte[] iv; 
     SecureRandom rand = new SecureRandom(); 
     rand.nextBytes(salt); 

     KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 65536, 128); 
     SecretKey tmp = factory.generateSecret(spec); 

     SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES"); 
     Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 
     cipher.init(Cipher.ENCRYPT_MODE, secret); 

     AlgorithmParameters params = cipher.getParameters(); 
     iv = params.getParameterSpec(IvParameterSpec.class).getIV(); 

     byte[] data = cipher.doFinal(plain); 
     byte[] result = new byte[data.length + salt.length + iv.length]; 
     System.arraycopy(salt, 0, result, 0, salt.length); 
     System.arraycopy(iv, 0, result, salt.length, iv.length); 
     System.arraycopy(data, 0, result, salt.length + iv.length, data.length); 

     return result; 
    } 

    /** 
    * Decrypt a string that was encrypted and coded in base64 
    * 
    * @param base64 
    * @return 
    * @throws BadPaddingException 
    * @throws IllegalBlockSizeException 
    * @throws UnsupportedEncodingException 
    * @throws InvalidKeySpecException 
    * @throws NoSuchPaddingException 
    * @throws NoSuchAlgorithmException 
    * @throws InvalidAlgorithmParameterException 
    * @throws InvalidKeyException 
    * @throws Exception 
    */ 
    public String decryptBase64ToString(byte[] base64) throws IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException, InvalidKeyException, InvalidAlgorithmParameterException, 
             NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException { 
     byte[] decrypted = decryptBase64(base64); 
     return new String(decrypted, "UTF8"); 
    } 

    /** 
    * Decrypt a Base64 encoded block 
    * 
    * @param base64 
    * @return 
    * @throws BadPaddingException 
    * @throws IllegalBlockSizeException 
    * @throws InvalidKeySpecException 
    * @throws NoSuchPaddingException 
    * @throws NoSuchAlgorithmException 
    * @throws InvalidAlgorithmParameterException 
    * @throws InvalidKeyException 
    * @throws Exception 
    */ 
    public byte[] decryptBase64(byte[] base64) throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, 
            NoSuchPaddingException, InvalidKeySpecException { 
     byte[] decodedData = Base64.decodeBase64(base64); 
     byte[] decrypted = decrypt(decodedData); 
     return decrypted; 
    } 

    /** 
    * Decrypt a binary array. 
    * 
    * @param encrypt 
    * @return 
    * @throws BadPaddingException 
    * @throws IllegalBlockSizeException 
    * @throws InvalidKeySpecException 
    * @throws NoSuchPaddingException 
    * @throws NoSuchAlgorithmException 
    * @throws InvalidAlgorithmParameterException 
    * @throws InvalidKeyException 
    * @throws Exception 
    */ 
    public byte[] decrypt(byte[] encrypt) throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, 
           NoSuchPaddingException, InvalidKeySpecException { 
     byte[] desalt = new byte[8]; 
     byte[] deiv = new byte[16]; 
     byte[] data = new byte[encrypt.length - 8 - 16]; 
     System.arraycopy(encrypt, 0, desalt, 0, desalt.length); 
     System.arraycopy(encrypt, desalt.length, deiv, 0, deiv.length); 
     System.arraycopy(encrypt, deiv.length + desalt.length, data, 0, encrypt.length - deiv.length - desalt.length); 

     KeySpec spec = new PBEKeySpec(password.toCharArray(), desalt, 65536, 128); 
     SecretKey tmp = factory.generateSecret(spec); 

     SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES"); 
     Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 
     cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(deiv)); 

     return cipher.doFinal(data); 
    } 

    /** 
    * Used to test the code. 
    * 
    * @param args ignored 
    * @throws Exception 
    */ 
    public static void main(String[] args) throws Exception { 
     String messages[] = { "GETP", "Testing stuff that is longer" }; 
     String password = "SanityLost"; 

     try { 
      Encryption app = new Encryption(password); 
      Encryption app1 = new Encryption(password); 

      for (String message : messages) { 
       byte[] encrypted = app.encryptStringToBase64(message); 
       System.out.println("Encrypted string is: " + new String(encrypted, "UTF-8") + ", " + encrypted.length); 

       String decrypted = app.decryptBase64ToString(encrypted); 
       System.out.println("Decrypted string is: " + decrypted); 

       decrypted = app1.decryptBase64ToString(encrypted); 
       System.out.println("App1 Decrypted string is: " + decrypted); 
      } 

     } catch (Exception e1) { 
      e1.printStackTrace(); 
     } 

     return; 
    } 
}