2012-04-07 104 views
0

我有下面的代碼幾乎工作:AES與鹽和靜態密碼

AES.java

import java.io.UnsupportedEncodingException; 
import java.security.AlgorithmParameters; 
import java.security.InvalidAlgorithmParameterException; 
import java.security.InvalidKeyException; 
import java.security.NoSuchAlgorithmException; 
import java.security.spec.AlgorithmParameterSpec; 
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.KeyGenerator; 
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; 

public class AES { 

    private static final int KEY_LENGTH    = 128; 
    private static final int ITERATIONS    = 100; 

    private static final String ALGORITHM    = "AES"; 
    private static final String SECRET_KEY_ALGORITHM = "PBKDF2WithHmacSHA1"; 
    private static final String TRANSFORMATION   = "AES/CBC/PKCS5Padding"; 

    private final Cipher  m_enc_cipher; 
    private final Cipher  m_dec_cipher; 

    private final byte[]  m_iv; 

    public AES(final char[] password, final byte[] salt) 
      throws NoSuchAlgorithmException, InvalidKeySpecException, 
      NoSuchPaddingException, InvalidKeyException, 
      InvalidParameterSpecException, IllegalBlockSizeException, 
      BadPaddingException, UnsupportedEncodingException, 
      InvalidAlgorithmParameterException { 

     // Derive the key, given password and salt 
     final SecretKeyFactory factory = SecretKeyFactory 
       .getInstance(SECRET_KEY_ALGORITHM); 
     final KeySpec spec = new PBEKeySpec(password, salt, ITERATIONS, 
       KEY_LENGTH); 
     final SecretKey tmp = factory.generateSecret(spec); 
     final SecretKey secret = new SecretKeySpec(tmp.getEncoded(), ALGORITHM); 

     // Build encryptor and get IV 
     final Cipher enc_cipher = Cipher.getInstance(TRANSFORMATION); 
     enc_cipher.init(Cipher.ENCRYPT_MODE, secret); 
     final AlgorithmParameters params = enc_cipher.getParameters(); 
     final byte[] iv = params.getParameterSpec(IvParameterSpec.class) 
       .getIV(); 

     // Build decryptor 
     final Cipher dec_cipher = Cipher.getInstance(TRANSFORMATION); 
     dec_cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv)); 

     this.m_enc_cipher = enc_cipher; 
     this.m_dec_cipher = dec_cipher; 

     this.m_iv = iv; 
    } 

    public AES(final byte[] iv) throws NoSuchAlgorithmException, 
      InvalidKeySpecException, NoSuchPaddingException, 
      InvalidKeyException, InvalidParameterSpecException, 
      IllegalBlockSizeException, BadPaddingException, 
      UnsupportedEncodingException, InvalidAlgorithmParameterException { 

     final AlgorithmParameterSpec aps = new IvParameterSpec(iv); 

     final KeyGenerator keygen = KeyGenerator.getInstance(ALGORITHM); 
     keygen.init(KEY_LENGTH); 
     final SecretKey secret = keygen.generateKey(); 

     // Build encryptor 
     final Cipher enc_cipher = Cipher.getInstance(TRANSFORMATION); 
     enc_cipher.init(Cipher.ENCRYPT_MODE, secret, aps); 

     // Build decryptor 
     final Cipher dec_cipher = Cipher.getInstance(TRANSFORMATION); 
     dec_cipher.init(Cipher.DECRYPT_MODE, secret, aps); 

     this.m_enc_cipher = enc_cipher; 
     this.m_dec_cipher = dec_cipher; 

     this.m_iv = iv; 
    } 

    public byte[] get_iv() { 
     return this.m_iv; 
    } 

    public byte[] encrypt(final byte[] data) throws NoSuchAlgorithmException, 
      InvalidKeySpecException, NoSuchPaddingException, 
      InvalidKeyException, InvalidParameterSpecException, 
      IllegalBlockSizeException, BadPaddingException, 
      UnsupportedEncodingException { 
     return this.m_enc_cipher.doFinal(data); 
    } 

    public byte[] decrypt(final byte[] data) throws IllegalBlockSizeException, 
      BadPaddingException { 
     return this.m_dec_cipher.doFinal(data); 
    } 
} 

AESTest.java

import java.io.UnsupportedEncodingException; 
import java.security.InvalidAlgorithmParameterException; 
import java.security.InvalidKeyException; 
import java.security.NoSuchAlgorithmException; 
import java.security.spec.InvalidKeySpecException; 
import java.security.spec.InvalidParameterSpecException; 
import java.util.Arrays; 

import javax.crypto.BadPaddingException; 
import javax.crypto.IllegalBlockSizeException; 
import javax.crypto.NoSuchPaddingException; 

import org.junit.Test; 
import static org.junit.Assert.*; 

public class AESTest { 
    @Test 
    public void test() throws InvalidKeyException, NoSuchAlgorithmException, 
      InvalidKeySpecException, NoSuchPaddingException, 
      InvalidParameterSpecException, IllegalBlockSizeException, 
      BadPaddingException, UnsupportedEncodingException, 
      InvalidAlgorithmParameterException { 

     final char[] password = "my_password".toCharArray(); 
     final byte[] salt = new byte[] { 22, 11 }; 

     final byte[] original_data = "Hello, World!".getBytes("UTF-8"); 
     final AES aesA = new AES(password, salt); 
     final byte[] encrypted_data = aesA.encrypt(original_data); 
     System.out.println("Encrypted:"); 
     System.out.println(javax.xml.bind.DatatypeConverter 
       .printBase64Binary(encrypted_data)); 

     final AES aesB = new AES(aesA.get_iv()); 
     final byte[] decrypted_data_B = aesB.decrypt(encrypted_data); 
     System.out.println("Decrypted B:"); 
     System.out.println(javax.xml.bind.DatatypeConverter 
       .printBase64Binary(decrypted_data_B)); 
     assertTrue(Arrays.equals(original_data, decrypted_data_B)); 
    } 
} 

在測試(AESTest.java),它給了我這個錯誤:

javax.crypto.BadPaddingException: Given final block not properly padded 

我的最終目的是能夠發送加密的數據塊,並在以後拿回來。
術語「稍後」可以指一天/一週/年。
然後,使用相同的密碼我想解密它。

因爲「稍後」可能是一個月,我需要創建一個新的AES對象。

現在,這個新的AES對象必須能夠使用相同的固定密碼/鹽來解密數據。
就是這樣。

我試過使用相同的IV,但它不起作用。

我在這裏做錯了什麼?

編輯#1: 請注意ITERATIONS以及。

回答

2

如前所述,第二個構造函數創建一個新的AES密鑰,因此它不會與第一個構造函數中創建的密鑰相匹配。我沒有真正意識到擁有兩個不同的構造函數,每個構造函數在哪一個中創建適合加密的AES實例以及另一個用於解密的實例?根據您想要加密還是解密,重新考慮該設計並僅使用一個單一實例可能是有意義的。

儘管在第一個構造函數中生成的密鑰是保密的,但生成的IV是公開信息,可以公開安全地公開,但這不會損害安全性。但是,您應該爲每條加密消息創建獨特的IV,否則就會出現針對您正在使用的CBC模式的攻擊。

因此,這裏就是我會建議你做:

加密:

基本上第一個構造函數,而忽略了第二個實例進行解密。檢索已創建的IV。

解密:

再次基本上在第一構造函數,與另外的IV參數。使用完全相同的參數(鹽,密碼,迭代)重新創建密鑰,並且這次以解密模式初始化Cipher,另外傳遞IV參數。

這應該做到這一點!

+0

第二個ctor應該看起來像http://pastebin.com/741SuBtv?它實際上工作,但我想知道,如果它仍然是靜態的,那麼這兩個'SecretKey'對象('tmp'&'secret')是做什麼的?不知道我明白這一點。無論如何,你對發佈的代碼有什麼看法? – Poni 2012-04-08 19:56:12

+0

謝謝! ............. – Poni 2012-04-19 17:58:44

3

當您通過iv初始化AES時,您將創建一個不同的secret。 Isnt錯了嗎?

您沒有使用相同的密碼和鹽。

+0

我不知道它是否是錯誤的 - 這就是爲什麼我問,希望得到一個具體的答案:)編輯:使用相同的構造函數,我得到同樣的錯誤。 – Poni 2012-04-07 23:46:23

+0

當您創建'aesB'並且初始化'SecretKeySpec'時,就像'iv'一樣使用'aesA'的'secret.getEncoded()'。 – dash1e 2012-04-07 23:57:00

+0

那麼,爲了得到一個合適的解密器,我應該在aesB中使用什麼?編碼密鑰? IV?都? – Poni 2012-04-08 00:07:35