2015-12-10 97 views
1

我發現這篇寫得很好的關於如何使用AES加密的例子,我承認一些更高級的方面超出了我。如果我使用同一個實例對象,該類可以正常工作。如果我創建另一個對象,使用相同的確切passPhrase - 該對象不能再正確解碼任何類型的字符串或前一個對象創建的數據。我只能得出結論,因爲這段代碼採用了相當弱的passPhrase字符串,將SALT混合在一起,並且構建了一個更強大的128位密鑰 - 這個密鑰構建過程每次都以某種方式隨機化。存在的意義:每次執行的AES加密密鑰生成是否一致?

new AESEncrypter("MyPassword") <> new AESEncrypter("MyPassword") 

有人能幫助我修改下面的類來獲得所需的行爲:

AESEncrypter a = new AESEncrypter("MyPassword") 
String encoded = a.encrypt("my message") 

AESEncrypter b = new AESEncrypter("MyPassword") 
b.decrypt(encoded) == "my message" 
import java.security.spec.KeySpec; 
import javax.crypto.Cipher; 
import javax.crypto.SecretKey; 
import javax.crypto.SecretKeyFactory; 
import javax.crypto.spec.IvParameterSpec; 
import javax.crypto.spec.PBEKeySpec; 
import javax.crypto.spec.SecretKeySpec; 
import sun.misc.BASE64Decoder; 
import sun.misc.BASE64Encoder; 

public class AESEncrypter { 

    private static final byte[] SALT = { 
      (byte) 0xA9, (byte) 0x9B, (byte) 0xC8, (byte) 0x32, 
      (byte) 0x56, (byte) 0x35, (byte) 0xE3, (byte) 0x03 
    }; 
    private static final int ITERATION_COUNT = 65536; 
    private static final int KEY_LENGTH = 128; 
    public Cipher ecipher; 
    public Cipher dcipher; 

    AESEncrypter(String passPhrase) throws Exception { 

    SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); 
     KeySpec spec = new PBEKeySpec(passPhrase.toCharArray(), SALT, ITERATION_COUNT, KEY_LENGTH); 
     SecretKey tmp = factory.generateSecret(spec); 

     // I Think the problem is here??? 

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

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

     dcipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 
     byte[] iv = ecipher.getParameters().getParameterSpec(IvParameterSpec.class).getIV(); 
     dcipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv)); 
    } 

    public String encrypt(String encrypt) throws Exception { 
     byte[] bytes = encrypt.getBytes("UTF8"); 
     byte[] encrypted = encrypt(bytes); 
     return new BASE64Encoder().encode(encrypted); 
    } 

    public byte[] encrypt(byte[] plain) throws Exception { 
     return ecipher.doFinal(plain); 
    } 

    public String decrypt(String encrypt) throws Exception { 
     byte[] bytes = new BASE64Decoder().decodeBuffer(encrypt); 
     byte[] decrypted = decrypt(bytes); 
     return new String(decrypted, "UTF8"); 
    } 

    public byte[] decrypt(byte[] encrypt) throws Exception { 
     return dcipher.doFinal(encrypt); 
    } 

    public static void main(String[] args) throws Exception { 

     String message = "MESSAGE"; 
     String password = "PASSWORD"; 

     AESEncrypter encrypter1 = new AESEncrypter(password); 
     AESEncrypter encrypter2 = new AESEncrypter(password); 

     String encrypted1 = encrypter1.encrypt(message); 
     String encrypted2 = encrypter2.encrypt(message); 

     System.out.println("Display Encrypted from object 1 and 2..why do they differ?"); 

     System.out.println(encrypted1) ; 
     System.out.println(encrypted2) ; 

     System.out.println("Display Each object decrypting its own encrypted msg. Works as expected"); 

     System.out.println(encrypter1.decrypt(encrypted1)) ; 
     System.out.println(encrypter2.decrypt(encrypted2)) ; 

     System.out.println("Attempt to decrypt the each others msg.. will fail"); 

     System.out.println(encrypter1.decrypt(encrypted2)) ; 
     System.out.println(encrypter2.decrypt(encrypted1)) ; 

    } 

} 

顯示從對象1和2..why加密它們有什麼不同?

 
drGy+BNSHPy34NWkkcNqLQ== 

9p06VfBgTuh7TizZSbvKjw== 

顯示每個對象解密自己的加密信息。按預期工作

 
MESSAGE 

MESSAGE 

嘗試解密每個人的味精..將失敗

錯誤:

Exception in thread "main" javax.crypto.BadPaddingException: Given final block not properly padded 
    at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:966) 
    at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:824) 
    at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:436) 
    at javax.crypto.Cipher.doFinal(Cipher.java:2165) 
+0

如果SALT和密碼每次都是相同的值,結果應該是一樣的。 –

+0

@ScaryWombat感謝您的檢查,也許我的代碼中有一些其他錯誤導致數據不對齊正在生成 – LaloInDublin

+0

我已更新上面的示例以顯示已證實的問題 – LaloInDublin

回答

1

的問題是,當你初始化CBC模式中的新Cipher,它產生一個新鮮和隨機的IV給你。這個初始化向量不一定是保密的,但它必須是不可預知的,才能提供語義安全性。您可以簡單地將IV放在密文前面並將其用於解密。

public byte[] encrypt(byte[] plain) throws Exception { 
    byte[] iv = ecipher.getParameters().getParameterSpec(IvParameterSpec.class).getIV(); 
    byte[] ct = ecipher.doFinal(plain); 
    byte[] result = new byte[ct.length + iv.length]; 
    System.arraycopy(iv, 0, result, 0, iv.length); 
    System.arraycopy(ct, 0, result, iv.length, ct.length); 
    return result; 
} 

public byte[] decrypt(byte[] encrypt) throws Exception { 
    byte[] iv = new byte[dcipher.getBlockSize()]; 
    byte[] ct = new byte[encrypt.length - dcipher.getBlockSize()]; 
    System.arraycopy(encrypt, 0, iv, 0, dcipher.getBlockSize()); 
    System.arraycopy(encrypt, dcipher.getBlockSize(), ct, 0, ct.length); 

    dcipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv)); 
    return dcipher.doFinal(ct); 
} 

則需要在一個私有變量的解密工作的初始化步驟來存儲secret


請記住,PBDKF2的鹽也應該是隨機的,長度爲16個字節。你可以將它存儲在IV的旁邊。

+0

感謝您的意見和代碼幫助我更深入地研究了AES瞭解這段代碼實際上在做什麼,以及CBC模式是什麼。比方說,我製作了一個使用上面代碼的消息傳遞應用程序,其中每個人都有相同的AES密碼。以這種方式傳輸每條消息的IV是否有意義?也許我選擇了AES模式的錯誤形式...... – LaloInDublin

+1

IV只有一個目的,即使您使用完全相同的密鑰對完全相同的明文進行加密,也會隨機化密文。它被稱爲[語義安全](https://en.wikipedia.org/wiki/Semantic_security)。否則,被動攻擊者(觀察者)可能會確定您再次發送了相同的消息並從中推斷出某些內容。如果您使用隨機鹽從密碼派生密鑰,那麼您不需要隨機IV,但是您可能不希望每次加密某個消息時派生密鑰,所以隨機IV有意義。 –

+0

優秀的學習信息和上面的代碼很好,謝謝! – LaloInDublin