Encrypt Password in Configuration Files? [closed]

2018-12-31 23:59发布

问题:

I have a program that reads server information from a configuration file and would like to encrypt the password in that configuration that can be read by my program and decrypted.

Requirments:

  • Encrypt plaintext password to be stored in the file
  • Decrypt the encrypted password read in from the file from my program

Any reccomendations on how i would go about doing this? I was thinking of writing my own algorithm but i feel it would be terribly insecure.

回答1:

A simple way of doing this is to use Password Based Encryption in Java. This allows you to encrypt and decrypt a text by using a password.

This basically means initializing a javax.crypto.Cipher with algorithm \"AES/CBC/PKCS5Padding\" and getting a key from javax.crypto.SecretKeyFactory with the \"PBKDF2WithHmacSHA512\" algorithm.

Here is a code example (updated to replace the less secure MD5-based variant):

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);
    }
}

One problem remains: Where should you store the password that you use to encrypt the passwords? You can store it in the source file and obfuscate it, but it\'s not too hard to find it again. Alternatively, you can give it as a system property when you start the Java process (-DpropertyProtectionPassword=...).

The same issue remains if you use the KeyStore, which also is protected by a password. Basically, you will need to have one master password somewhere, and it\'s pretty hard to protect.



回答2:

Yes, definitely don\'t write your own algorithm. Java has lots of cryptography APIs.

If the OS you are installing upon has a keystore, then you could use that to store your crypto keys that you will need to encrypt and decrypt the sensitive data in your configuration or other files.



回答3:

Check out jasypt, which is a library offering basic encryption capabilities with minimum effort.



回答4:

I think that the best approach is to ensure that your config file (containing your password) is only accessible to a specific user account. For example, you might have an application specific user appuser to which only trusted people have the password (and to which they su to).

That way, there\'s no annoying cryptography overhead and you still have a password which is secure.

EDIT: I am assuming that you are not exporting your application configuration outside of a trusted environment (which I\'m not sure would make any sense, given the question)



回答5:

Well to solve the problems of master password - the best approach is not to store the password anywhere, the application should encrypt passwords for itself - so that only it can decrypt them. So if I was using a .config file I would do the following, mySettings.config:

encryptTheseKeys=secretKey,anotherSecret

secretKey=unprotectedPasswordThatIputHere

anotherSecret=anotherPass

someKey=unprotectedSettingIdontCareAbout

so I would read in the keys that are mentioned in the encryptTheseKeys, apply the Brodwalls example from above on them and write them back to the file with a marker of some sort (lets say crypt:) to let the application know not to do it again, the output would look like this:

encryptTheseKeys=secretKey,anotherSecret

secretKey=crypt:ii4jfj304fjhfj934fouh938

anotherSecret=crypt:jd48jofh48h

someKey=unprotectedSettingIdontCareAbout

Just make sure to keep the originals in your own secure place...



回答6:

The big point, and the elephant in the room and all that, is that if your application can get hold of the password, then a hacker with access to the box can get hold of it too!

The only way somewhat around this, is that the application asks for the \"master password\" on the console using Standard Input, and then uses this to decrypt the passwords stored on file. Of course, this completely makes is impossible to have the application start up unattended along with the OS when it boots.

However, even with this level of annoyance, if a hacker manages to get root access (or even just access as the user running your application), he could dump the memory and find the password there.

The thing to ensure, is to not let the entire company have access to the production server (and thereby to the passwords), and make sure that it is impossible to crack this box!



回答7:

Try using ESAPIs Encryption methods. Its easy to configure and you can also easily change your keys.

http://owasp-esapi-java.googlecode.com/svn/trunk_doc/latest/org/owasp/esapi/Encryptor.html

You

1)encrypt 2)decrypt 3)sign 4)unsign 5)hashing 6)time based signatures and much more with just one library.



回答8:

See what is available in Jetty for storing password (or hashes) in configuration files, and consider if the OBF encoding might be useful for you. Then see in the source how it is done.

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



回答9:

Depending on how secure you need the configuration files or how reliable your application is, http://activemq.apache.org/encrypted-passwords.html may be a good solution for you.

If you are not too afraid of the password being decrypted and it can be really simple to configure using a bean to store the password key. However, if you need more security you can set an environment variable with the secret and remove it after launch. With this you have to worry about the application / server going down and not application not automatically relaunching.



回答10:

If you are using java 8 the use of the internal Base64 encoder and decoder can be avoided by replacing

return new BASE64Encoder().encode(bytes);

with

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

and

return new BASE64Decoder().decodeBuffer(property);

with

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

Note that this solution doesn\'t protect your data as the methods for decrypting are stored in the same place. It just makes it more difficult to break. Mainly it avoids to print it and show it to everybody by mistake.