Java AES再次保存和解密后解密不同的结果

时间:2014-07-26 13:58:36

标签: java encryption aes

当我运行scrypt时,我的第一个解密结果是正确的:

  兄弟群众意味着古老的需求社会在完美眩光之前   愤怒的确定

当我保存encryptedText,salt(例如数据库)并想再次解密时,我得到了这个。

  

Ŝ ,&6 ; m an在完美眩光愤怒之前的古代需求社会

*更新 我认为问题是我处理数据的方式(盐和静脉注射)。现在我能够加密并保存-encryptText,randomIV,randomSalt到数据库并使用masterPassword解密它没有问题。

感谢大家的帮助!

*编辑过的代码,我最终得到了这个解决方案

import java.io.UnsupportedEncodingException;
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.util.Random;
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 Aes { 
private static int pswdIterations = 65536;
private static int keySize =  256;

public static String encrypt(String plainText, String password, String salt, String initializationVector) throws 
    NoSuchAlgorithmException, 
    InvalidKeySpecException, 
    NoSuchPaddingException, 
    InvalidParameterSpecException, 
    IllegalBlockSizeException, 
    BadPaddingException, 
    UnsupportedEncodingException, 
    InvalidKeyException, 
    InvalidAlgorithmParameterException 
{   
    byte[] saltBytes = salt.getBytes("UTF-8");
    byte[] ivBytes = initializationVector.getBytes("UTF-8");

    // Derive the key, given password and salt.
    SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
    PBEKeySpec spec = new PBEKeySpec(
            password.toCharArray(), 
            saltBytes, 
            pswdIterations, 
            keySize
    );

    SecretKey secretKey = factory.generateSecret(spec);
    SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");

    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.ENCRYPT_MODE, secret, new IvParameterSpec(ivBytes));

    byte[] encryptedTextBytes = cipher.doFinal(plainText.getBytes("UTF-8"));
    return new Base64().encodeAsString(encryptedTextBytes);
}

    public static String decrypt(String encryptedText, String password, String salt, String initializationVector ) throws 
    NoSuchAlgorithmException, 
    InvalidKeySpecException, 
    NoSuchPaddingException, 
    InvalidKeyException, 
    InvalidAlgorithmParameterException,
    UnsupportedEncodingException
{
    byte[] saltBytes = salt.getBytes("UTF-8");
    byte[] ivBytes = initializationVector.getBytes("UTF-8");
    byte[] encryptedTextBytes = new Base64().decodeBase64(encryptedText);

    // Derive the key, given password and salt.
    SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
    PBEKeySpec spec = new PBEKeySpec(
            password.toCharArray(), 
            saltBytes, 
            pswdIterations, 
            keySize
    );

    SecretKey secretKey = factory.generateSecret(spec);
    SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");

    // Decrypt the message, given derived key and initialization vector.
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(ivBytes));

    byte[] decryptedTextBytes = null;
    try {
        decryptedTextBytes = cipher.doFinal(encryptedTextBytes);
    } catch (IllegalBlockSizeException e) {
        e.printStackTrace();
    } catch (BadPaddingException e) {
        e.printStackTrace();
    }

    return new String(decryptedTextBytes);
   }  

    public String generateSalt() {
        SecureRandom random = new SecureRandom();
        byte bytes[] = new byte[16];
        random.nextBytes(bytes);
        String s = new String(bytes);
        return s;
    }

    public String generateIV(String chars, int length) {
    Random rand = new Random();
    StringBuilder buf = new StringBuilder();
    for (int i=0; i<length; i++) {
      buf.append(chars.charAt(rand.nextInt(chars.length())));
    }
    return buf.toString();
    }

    }

* Edited.Run Test

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

//Passphrase and masterPassword
    String passPhrase = "stackoverflow is great";
    String masterPassword = "password";

    //-Aes
    Aes crypt = new Aes();

    // Aes generate random salt
    String genSalt = crypt.generateSalt();
    String tmpSalt = genSalt;
    // Aes generate random Iv
    String genIV =   crypt.generateIV("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", 16);
    String tmpIV = genIV;

// Aes encrypt phrase
    String cipherPassPhrase = crypt.encrypt(passPhrase, masterPassword, tmpSalt, tmpIV);

    System.out.println(cipherPassPhrase);

// save cipherPassPhrase, tmpSalt, tmpIV to database ....decrypt with not stored masterPassword
 }

2 个答案:

答案 0 :(得分:0)

您的代码存在一些不同的问题。首先是你如何产生盐......

SecureRandom random = new SecureRandom();
byte bytes[] = new byte[20];
random.nextBytes(bytes);
String s = new String(bytes);
return s;

您正在使用随机数据并尝试将其转换为字符串,但字符串构造函数不期望任何随机数据,它需要一些编码的文本字符串(UTF-8,UTF-16,或者其他。)如果对二进制数据进行编码,则只能将其转换为字符串,我只需返回bytes数组。

public byte[] generateSalt(int length) {
    SecureRandom random = new SecureRandom();
    byte bytes[] = new byte[length];
    random.nextBytes(bytes);
    return bytes;
}

接下来,很难说为什么你的输出正在解密你所声明的特定方式(只有第一个块被破坏),但我猜赌它与你错误编码IV有关将其保存到数据库中。更好的解决方案是将salt和IV添加到加密数据中,然后在需要解密时将其删除。

public String encrypt(String plainText) throws Exception {   
    //get salt    
    byte[] saltBytes = generateSalt(saltLength);

    // Derive the key
    SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
    PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), saltBytes, pswdIterations, keySize);

    SecretKey secretKey = factory.generateSecret(spec);
    SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");

    //encrypt the message
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.ENCRYPT_MODE, secret);
    AlgorithmParameters params = cipher.getParameters();
    byte[] ivBytes = params.getParameterSpec(IvParameterSpec.class).getIV();
    byte[] encryptedTextBytes = cipher.doFinal(plainText.getBytes("UTF-8"));

    //prepend the salt and IV
    byte[] buffer = new byte[saltBytes.length + ivBytes.length + encryptedTextBytes.length];
    System.arraycopy(saltBytes, 0, buffer, 0, saltBytes.length);
    System.arraycopy(ivBytes, 0, buffer, saltBytes.length, ivBytes.length);
    System.arraycopy(encryptedTextBytes, 0, buffer, saltBytes.length + ivBytes.length, encryptedTextBytes.length);
    return new Base64().encodeToString(buffer);
}

public String decrypt(String encryptedText) throws Exception {

    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

    //strip off the salt and IV
    ByteBuffer buffer = ByteBuffer.wrap(new Base64().decode(encryptedText));
    byte[] saltBytes = new byte[saltLength];
    buffer.get(saltBytes, 0, saltBytes.length);
    byte[] ivBytes = new byte[cipher.getBlockSize()];
    buffer.get(ivBytes, 0, ivBytes.length);
    byte[] encryptedTextBytes = new byte[buffer.capacity() - saltBytes.length - ivBytes.length];
    buffer.get(encryptedTextBytes);

    // Derive the key
    SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
    PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), saltBytes, pswdIterations, keySize);

    SecretKey secretKey = factory.generateSecret(spec);
    SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");
    cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(ivBytes));

    byte[] decryptedTextBytes = null;
    try {
        decryptedTextBytes = cipher.doFinal(encryptedTextBytes);
    } catch (IllegalBlockSizeException e) {
        e.printStackTrace();
    } catch (BadPaddingException e) {
        e.printStackTrace();
    }

    return new String(decryptedTextBytes);
}

每次加密一段数据时使用新的IV都非常重要,因为它helps to avoid known text attacks

答案 1 :(得分:-1)

您的代码可以加密A并解密A,但不加密A,加密B并解密A. 因为加密A时加密A的salt和初始化向量被覆盖。 我不知道你要归档什么。我留下了一个可用的代码示例。

import java.security.AlgorithmParameters;
import java.security.SecureRandom;
import java.util.HashMap;
import java.util.Map;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
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 AESDemo {

    private static final String password = "test";
    private static String salt;
    private static int pswdIterations = 65536;
    private static int keySize = 256;
    //read from DB
    private byte[] ivBytes = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

    public String encrypt(String plainText) throws Exception {

        // get salt
        if (salt == null)
            salt = generateSalt();
        byte[] saltBytes = salt.getBytes("UTF-8");

        // Derive the key
        SecretKeyFactory factory = SecretKeyFactory
                .getInstance("PBKDF2WithHmacSHA1");
        PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), saltBytes,
                pswdIterations, keySize);

        SecretKey secretKey = factory.generateSecret(spec);
        SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");

        // encrypt the message
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, secret, new IvParameterSpec(ivBytes));
        byte[] encryptedTextBytes = cipher.doFinal(plainText.getBytes("UTF-8"));

        return new Base64().encodeAsString(encryptedTextBytes);
    }

    @SuppressWarnings("static-access")
    public String decrypt(String encryptedText) throws Exception {

        byte[] saltBytes = salt.getBytes("UTF-8");
        byte[] encryptedTextBytes = new Base64().decodeBase64(encryptedText);

        // Derive the key
        SecretKeyFactory factory = SecretKeyFactory
                .getInstance("PBKDF2WithHmacSHA1");
        PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), saltBytes,
                pswdIterations, keySize);

        SecretKey secretKey = factory.generateSecret(spec);
        SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");

        // Decrypt the message
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, secret,
                new IvParameterSpec(ivBytes));

        byte[] decryptedTextBytes = null;
        try {
            decryptedTextBytes = cipher.doFinal(encryptedTextBytes);
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        }

        return new String(decryptedTextBytes);
    }

    public String generateSalt() {
        //get the salt from DB
        return "I hate salt";
    }
}

public class stackoverflow_test {

    public static void main(String[] ag) throws Exception{
        AESDemo d = new AESDemo();

        System.out.println("Encrypted string:" + d.encrypt("brother crowd mean guy ancient demand society before perfection glare anger certain"));           
        String encryptedText = d.encrypt("brother crowd mean guy ancient demand society before perfection glare anger certain");
        String encryptedText2 = d.encrypt("Hello World");
        System.out.println("Decrypted string:" + d.decrypt(encryptedText2));        
        System.out.println("Decrypted string:" + d.decrypt(encryptedText));         

    }
}

结果

Encrypted string:c7NXCBiq7tqzon39iPtkQLJ0chuXudS5oVDPdAr5S3q1245d3uJUVcyNUY77rpGeNvOp9hOldhiOM8mp2C/aOqqNyXx82zJt2V2EFtQkauCl/oY2EMENh1jCR6Nqf1lJ
Decrypted string:Hello World
Decrypted string:brother crowd mean guy ancient demand society before perfection glare anger certain