Java AES加解密静态秘钥

标签 java encryption aes password-encryption

我有一个应用程序需要在配置文件中存储一些 secret 密码,例如数据库和 ftp 密码/详细信息。我环顾四周,发现了很多使用 AES 的加密/解密解决方案,但我似乎无法弄清楚如何在不更改 key 的情况下使其工作。这意味着我可以加密和解密(使用相同的 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==' 的值作为我的 secret ,下一次它是 'MeC4zCf9S5wUUKAu8rvpCQ==',所以大概 key 正在改变。我假设我正在对这个问题应用一些反直觉的逻辑,如果有人能阐明 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);
}

感谢您的帮助!

最佳答案

好的,看来我已经找到问题的答案了。我的信息来自 this Stackoverflow post . 据我了解,IV(初始化 vector )用于将熵添加到加密过程中。每次创建新密码时,Java 都会创建略有不同的 IV。因此有两种解决方案:

  1. 使用固定的 IV,或者
  2. 将 IV 与加密数据一起存储。

根据我的阅读,选项 1 不是很好的做法;所以选项2是。我知道应该可以简单地将 IV 附加到加密字符串(因为仍然需要 secret ),因此在解密时可以重建 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);
}

关于Java AES加解密静态秘钥,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31238352/

相关文章:

java - 如何从 quarkus 应用程序中正确地将逻辑删除消息发布到压缩的 kafka 主题?

Java 改变对话框的标题

mysql - 将方法名称绑定(bind)到 mysql where 子句?

AES算法输入输出限制

使用 AES : Accepts only certain keys 的 Java 字符串加密

java - 如何使用 itextsharp 将长文本定位并换行到下一行?

java - 如何检查对象是否为 null,如果为 NULL 则继续?

ssl - 使用 SSL 的 IBM i DB2 JDBC 加密

android - 使用 ChaCha20 加密和解密字符串

c++ - 如何发送 EVP_PKEY 给对方?