在配置文件中加密密码? ?

我有一个从配置文件中读取服务器信息的程序,并希望加密该配置中的密码,以便我的程序能够读取并解密。

要求:

  • 加密存储在文件中的明文密码
  • 解密从我的程序中读取的文件中的加密密码

你有什么建议吗?我想写自己的算法,但我觉得它会非常不安全。

226672 次浏览

是的,绝对不要编写自己的算法。 Java 有很多加密 API。

如果您正在安装的操作系统上有一个密钥存储库,那么您可以使用它来存储您的加密密钥,您将需要加密和解密配置或其他文件中的敏感数据。

看看 Jasypt,它是一个提供基本加密功能的库,只需要最少的努力。

我认为最好的方法是确保您的配置文件(包含您的密码)是 只有特定的用户帐户才能访问。例如,您可能有一个特定于应用程序的用户 appuser,该用户只有受信任的人才有密码(而且他们的 su也有密码)。

这样,就不会有烦人的加密开销,而且您仍然有一个安全的密码。

编辑: 我假设您没有将应用程序配置导出到受信任的环境之外(考虑到这个问题,我不确定这样做是否有意义)

查看 Jetty 中有哪些可用于在配置文件中存储密码(或哈希)的内容,并考虑 OBF 编码是否对您有用。然后在源代码中查看它是如何完成的。

Http://www.eclipse.org/jetty/documentation/current/configuring-security-secure-passwords.html

一个简单的方法是在 Java 中使用基于密码的加密。这允许您使用密码对文本进行加密和解密。

这基本上意味着使用算法 "AES/CBC/PKCS5Padding"初始化 javax.crypto.Cipher,并使用 "PBKDF2WithHmacSHA512"算法从 javax.crypto.SecretKeyFactory获得一个密钥。

下面是一个代码示例(更新为替换安全性较差的基于 MD5的变体) :

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.AlgorithmParameters;
import java.security.GeneralSecurityException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.Base64;
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;


public class ProtectedConfigFile {


public static void main(String[] args) throws Exception {
String password = System.getProperty("password");
if (password == null) {
throw new IllegalArgumentException("Run with -Dpassword=<password>");
}


// The salt (probably) can be stored along with the encrypted data
byte[] salt = new String("12345678").getBytes();


// Decreasing this speeds down startup time and can be useful during testing, but it also makes it easier for brute force attackers
int iterationCount = 40000;
// Other values give me java.security.InvalidKeyException: Illegal key size or default parameters
int keyLength = 128;
SecretKeySpec key = createSecretKey(password.toCharArray(),
salt, iterationCount, keyLength);


String originalPassword = "secret";
System.out.println("Original password: " + originalPassword);
String encryptedPassword = encrypt(originalPassword, key);
System.out.println("Encrypted password: " + encryptedPassword);
String decryptedPassword = decrypt(encryptedPassword, key);
System.out.println("Decrypted password: " + decryptedPassword);
}


private static SecretKeySpec createSecretKey(char[] password, byte[] salt, int iterationCount, int keyLength) throws NoSuchAlgorithmException, InvalidKeySpecException {
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
PBEKeySpec keySpec = new PBEKeySpec(password, salt, iterationCount, keyLength);
SecretKey keyTmp = keyFactory.generateSecret(keySpec);
return new SecretKeySpec(keyTmp.getEncoded(), "AES");
}


private static String encrypt(String property, SecretKeySpec key) throws GeneralSecurityException, UnsupportedEncodingException {
Cipher pbeCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
pbeCipher.init(Cipher.ENCRYPT_MODE, key);
AlgorithmParameters parameters = pbeCipher.getParameters();
IvParameterSpec ivParameterSpec = parameters.getParameterSpec(IvParameterSpec.class);
byte[] cryptoText = pbeCipher.doFinal(property.getBytes("UTF-8"));
byte[] iv = ivParameterSpec.getIV();
return base64Encode(iv) + ":" + base64Encode(cryptoText);
}


private static String base64Encode(byte[] bytes) {
return Base64.getEncoder().encodeToString(bytes);
}


private static String decrypt(String string, SecretKeySpec key) throws GeneralSecurityException, IOException {
String iv = string.split(":")[0];
String property = string.split(":")[1];
Cipher pbeCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
pbeCipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(base64Decode(iv)));
return new String(pbeCipher.doFinal(base64Decode(property)), "UTF-8");
}


private static byte[] base64Decode(String property) throws IOException {
return Base64.getDecoder().decode(property);
}
}

一个问题仍然存在: 应该将用于加密密码的密码存储在哪里?您可以将它存储在源文件中并混淆它,但是再次找到它并不太困难。或者,您可以在启动 Java 进程(-DpropertyProtectionPassword=...)时将其作为系统属性提供。

如果使用 KeyStore,则仍然存在同样的问题,KeyStore 也受到密码的保护。基本上,您将需要有一个主密码的地方,这是非常难以保护。

为了很好地解决主密码的问题——最好的方法是不要将密码存储在任何地方,应用程序应该为自己加密密码——这样只有它才能解密密码。所以如果我用一个。配置文件我将执行以下操作,配置文件:

加密这些密钥 = 保密密钥,另一个秘密

Secret Key = unprotected tedPasswordThatIputHere

另一个秘密 = 另一个通行证

关于

这样我就可以读出 加密 TheseKeys,将上面的 Brodwall 示例应用于它们,然后 用某种标记(比方说 地窖:)将它们写回文件,让应用程序知道不要再这样做,输出如下:

加密这些密钥 = 保密密钥,另一个秘密

Secret Key = 地下室: ii4jfj304fjhfj934fuh938

另一个秘密 = 地窖: jd48jofh48h

关于

只要确保把原件放在你自己的安全地方。

尝试使用 ESAPI 加密方法。它很容易配置,你也可以很容易地改变你的密钥。

Http://owasp-esapi-java.googlecode.com/svn/trunk_doc/latest/org/owasp/esapi/encryptor.html

1)加密 2)解密 3)签署 4)取消签署 5)散列 6)基于时间的签名和更多的只有一个库。

最重要的一点,房间里的大象和所有这一切,是 如果你的应用程序可以得到的密码,那么黑客可以访问的盒子也可以得到它!

解决这个问题的唯一方法是,应用程序使用 Standard Input 在控制台上请求“主密码”,然后使用该方法对存储在文件中的密码进行解密。当然,这完全使得应用程序在启动时不可能与操作系统一起启动。

然而,即使有这种程度的烦恼,如果一个黑客设法获得 root 访问权限(甚至只是作为运行应用程序的用户进行访问) ,他可以转储内存并在那里找到密码。

要确保的是,不要让整个公司都访问生产服务器(从而访问密码) ,并确保不可能破解这个盒子!

取决于您需要配置文件的安全性或应用程序的可靠性,http://activemq.apache.org/encrypted-passwords.html可能是一个很好的解决方案。

如果您不太担心密码被解密,并且可以非常简单地配置使用 bean 来存储密码密钥。然而,如果你需要更多的安全性,你可以设置一个环境变量,并在启动后删除它。因此,您必须担心应用程序/服务器出现故障,而不是应用程序无法自动重新启动。

如果使用 Java 8,可以通过替换来避免使用内部 Base64编码器和解码器

return new BASE64Encoder().encode(bytes);

return Base64.getEncoder().encodeToString(bytes);

还有

return new BASE64Decoder().decodeBuffer(property);

return Base64.getDecoder().decode(property);

注意,此解决方案不保护数据,因为解密方法存储在相同的位置。这只会让它更难被打破。主要是避免打印它,并显示给大家错误。