2017-03-12 3 views
1

Ich verwende eine Verschlüsselungsklasse für einige Datenübertragungen. Ich erhalte diese Fehlermeldung:Verschlüsselungsproblem aufgrund von Auffüllen?

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) 

Das Seltsame ist, bekomme ich es nicht, wenn ich die Entschlüsselung mit dem gleichen Encryption Objekt renne ich mit verschlüsselt. Ich erhalte nur den Fehler, wenn ein neues Verschlüsselungsobjekt versucht, zu entschlüsseln. Ich habe dies mit dem Haupt() im folgenden Code reproduziert.

Der zweite Entschlüsselungsanruf schlägt fehl. Offenbar halten die Chiffren Zustand zwischen den Sitzungen ???

Wie behebe ich das, damit ein anderes Programm entschlüsseln kann, was an anderer Stelle verschlüsselt wurde?

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

Antwort

3

Ihr dcipher Objekt erhält seine IV aus dem ecipher Objekt. Das Problem ist, dass die IV nicht von dem Passwort oder Schlüssel abhängig ist. Es wird zufällig generiert, wenn Sie es nicht selbst einstellen. Sie erhalten eine andere IV für die Entschlüsselung als für die Verschlüsselung, wenn Sie eine zweite Encryption Instanz erstellen.

Die IV soll nicht geheim sein, also können Sie sie einfach zusammen mit dem Chiffretext senden. Da die IV immer die gleiche Länge hat (16 Byte für AES/CBC), können Sie sie einfach vor den Chiffretext schreiben und während der Entschlüsselung zurücklesen. Sie müssen Ihre gesamte Klasse dafür überarbeiten. Beachten Sie, dass eine zufällige IV für die semantische Sicherheit wichtig ist.

Während wir dabei sind. Sie sollten auch das Salz für jede Verschlüsselung randomisieren. Wenn wir davon ausgehen, dass das Salz immer 8 Byte und die IV ist immer 16 Byte lang, ist es einfach, das folgende Format für eine verschlüsselte Nachricht zu entwickeln:

salt | IV | ciphertext 

Hier | bedeutet Verkettung.

Sie benötigen keine zwei Cipher Instanzen. Verwenden Sie einfach einen und init es für die Verschlüsselung oder Entschlüsselung kurz vor der Verwendung und nicht innerhalb des Konstruktors.

+0

Danke, das ist genau das, was ich wissen musste! Ich habe die Frage bearbeitet, um meine aktualisierte Klasse aufzunehmen. – CasaDelGato

+0

Ok, hat es geschafft! Vielen Dank! – CasaDelGato

1

Dank Arjom Antwort, habe ich den Code festgelegt werden:

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; 
    } 
} 
Verwandte Themen