使用静态密钥进行Java AES加密和解密

时间:2015-07-06 05:33:49

标签: java encryption aes password-encryption

我有一个应用程序需要在配置文件中存储一些秘密密码,例如数据库和ftp密码/详细信息。我环顾四周,发现了许多使用AES的加密/解密解决方案,但我似乎无法弄清楚如何在不更改密钥的情况下使其工作。这意味着我可以加密和解密(使用相同的SecretKey),但是在重启等时保持持久性。我似乎无法使SecretKey保持不变。以下示例显示了我的方法:

String secret = Encryptor.encrpytString("This is secret");
String test = Encryptor.decrpytString(secret);
System.out.println(test); //This is secret is printed

到目前为止一切顺利。但是,如果我运行它,我可能会得到'2Vhht / L80UlQ184S3rlAWw =='的值作为我的秘密,下次是'MeC4zCf9S5wUUKAu8rvpCQ =='时,可能关键是正在改变。我假设我正在对这个问题运用一些反直觉的逻辑,如果有人可以解释a)我做错了什么,或者b)允许我存储加密的密码信息的解决方案,我会很感激并可以通过提供的信息进行检索。

我的方法如下:

private static final String salt = "SaltySalt";

private static byte [] ivBytes = null;

private static byte[] getSaltBytes() throws Exception {
    return salt.getBytes("UTF-8");
}

private static char[] getMasterPassword() {
    return "SuperSecretPassword".toCharArray();
}

private static byte[] getIvBytes() throws Exception {
    if (ivBytes == null) {
        //I don't have the parameters, so I'll generate a dummy encryption to create them
        encrpytString("test");
    }
    return ivBytes;
}

public static String encrpytString (String input) throws Exception {
    SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
    PBEKeySpec spec = new PBEKeySpec(getMasterPassword(), getSaltBytes(), 65536,256);
    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);
    ivBytes = cipher.getParameters().getParameterSpec(IvParameterSpec.class).getIV();
    byte[] encryptedTextBytes = cipher.doFinal(input.getBytes("UTF-8"));
    return DatatypeConverter.printBase64Binary(encryptedTextBytes);        
}

public static String decrpytString (String input) throws Exception {
    byte[] encryptedTextBytes = DatatypeConverter.parseBase64Binary(input);
    SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
    PBEKeySpec spec = new PBEKeySpec(getMasterPassword(), getSaltBytes(), 65536, 256);
    SecretKey secretKey = factory.generateSecret(spec);
    SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(getIvBytes()));
    byte[] decryptedTextBytes = cipher.doFinal(encryptedTextBytes);
    return new String(decryptedTextBytes);
}

感谢您的帮助!

3 个答案:

答案 0 :(得分:3)

好的,好像我找到了问题的答案。我从this Stackoverflow post获取了我的信息。 据我所知,IV(初始化向量)用于将熵添加到加密过程中。每次创建新密码时,Java都会创建一个略有不同的IV。因此有两种解决方案:

  1. 使用固定的IV或
  2. 将IV与加密数据一起存储。
  3. 从我所读到的,选项1不是很好的做法;所以选项2就是。我知道应该可以简单地将IV附加到加密的字符串(因为仍然需要秘密),因此当解密时可以重建IV。

    这是几乎完整的解决方案。我在解密时仍然会遇到一些填充错误(请参阅我的评论)。我现在没有时间花在它上面,所以作为临时措施,我立即尝试解密加密的字符串并继续尝试(迭代)直到它工作。它似乎有大约50%的命中率+我不经常加密,因为它是一个性能问题。如果有人可以建议修复(仅仅是为了完整起见),那会很好。

    private static final String salt = "SaltySalt";
    
    private static final int IV_LENGTH = 16;
    
    private static byte[] getSaltBytes() throws Exception {
        return salt.getBytes("UTF-8");
    }
    
    private static char[] getMasterPassword() {
        return "SuperSecretPassword".toCharArray();
    }
    
    public static String encrpytString (String input) throws Exception {
        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
        PBEKeySpec spec = new PBEKeySpec(getMasterPassword(), getSaltBytes(), 65536,256);
        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);
        byte[] ivBytes = cipher.getParameters().getParameterSpec(IvParameterSpec.class).getIV();
        byte[] encryptedTextBytes = cipher.doFinal(input.getBytes("UTF-8"));
        byte[] finalByteArray = new byte[ivBytes.length + encryptedTextBytes.length]; 
        System.arraycopy(ivBytes, 0, finalByteArray, 0, ivBytes.length);
        System.arraycopy(encryptedTextBytes, 0, finalByteArray, ivBytes.length, encryptedTextBytes.length);
        return DatatypeConverter.printBase64Binary(finalByteArray);        
    }
    
    public static String decrpytString (String input) throws Exception {
        if (input.length() <= IV_LENGTH) {
            throw new Exception("The input string is not long enough to contain the initialisation bytes and data.");
        }
        byte[] byteArray = DatatypeConverter.parseBase64Binary(input);
        byte[] ivBytes = new byte[IV_LENGTH];
        System.arraycopy(byteArray, 0, ivBytes, 0, 16);
        byte[] encryptedTextBytes = new byte[byteArray.length - ivBytes.length];
        System.arraycopy(byteArray, IV_LENGTH, encryptedTextBytes, 0, encryptedTextBytes.length);
        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
        PBEKeySpec spec = new PBEKeySpec(getMasterPassword(), getSaltBytes(), 65536, 256);
        SecretKey secretKey = factory.generateSecret(spec);
        SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(ivBytes));
        byte[] decryptedTextBytes = cipher.doFinal(encryptedTextBytes);
        return new String(decryptedTextBytes);
    }
    

答案 1 :(得分:1)

使用静态初始化向量,例如零IV:

cipher.init(Cipher.ENCRYPT_MODE, secret, new IvParameterSpec(new byte[16]));

cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(new byte[16]));

由于您要存储密码,您可能希望使用随机的IV和/或随机盐并将其与密文一起存储,以便相同的密码不会加密到相同的密文。

答案 2 :(得分:0)

您需要先setSeed()

class Encryptor {

    static final String salt = "SaltSalt";

    public static byte[] encryptString(String input) throws Exception {
        byte[] bytes = input.getBytes("UTF-8");

        Cipher cipher = Cipher.getInstance("AES");
        KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
        SecureRandom secureRandom = new SecureRandom();
        secureRandom.setSeed(salt.getBytes("UTF-8"));
        keyGenerator.init(256, secureRandom);
        Key key = keyGenerator.generateKey();
        cipher.init(Cipher.ENCRYPT_MODE, key);
        byte[] a = cipher.doFinal(bytes);
        return a;
    }

    public static String decryptString(byte[] input) throws Exception {
        Cipher cipher = Cipher.getInstance("AES");
        KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
        SecureRandom secureRandom = new SecureRandom();
        secureRandom.setSeed(salt.getBytes("UTF-8"));
        keyGenerator.init(256, secureRandom);
        Key key = keyGenerator.generateKey();
        cipher.init(Cipher.DECRYPT_MODE, key);
        byte[] decrypted = cipher.doFinal(input);
        String result = new String(decrypted, "UTF-8");
        return result;
    }
}