2012-04-07 73 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