2015-12-10 60 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