Encrypt Password in Configuration Files?

218,542

Solution 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.

Solution 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.

Solution 3

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)

Solution 4

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!

Solution 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...

Share:
218,542
Petey B
Author by

Petey B

I do security shenanigans at GNB. SOreadytohelp

Updated on July 08, 2022

Comments

  • Petey B
    Petey B almost 2 years

    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.

  • ninesided
    ninesided almost 15 years
    +1 for using a KeyStore! It's nothing more than obfuscation if you're storing the key in the Jar file.
  • matbrgz
    matbrgz almost 15 years
    If all that is needed is not to have the password stored in clear text, then keystores are overkill.
  • Petey B
    Petey B almost 15 years
    Thanks for the code example, it's pretty much how i ended up doing it. In regards to the password that protects the passwords i ran into that same problem, i went witht the obfuscate it method for now but havnt come up with an acceptable solution yet, thanks for your suggestions.
  • Petey B
    Petey B over 12 years
    Yeah, this is from 3 years ago. To avoid the master key, I ended up using RSA keys issued from our internal CA. Access to the private key is protected by being encrypted with a finger print of the machine hardware.
  • user1007231
    user1007231 over 12 years
    I see, sounds pretty solid. nice.
  • Spencer Kormos
    Spencer Kormos over 11 years
    Theoretically, you could store that password as a system property, and read it at runtime. Assuming you only have access to that account / server, that would be the ultimate way to do it. Not to mention you could have different files, for different environments, and different property passwords for each.
  • Ztyx
    Ztyx about 11 years
    "Alternatively, you can give it as a system property when you start the Java process (-DpropertyProtectionPassword=...)". Note that this would make it possible to extract the password using "ps fax" on (GNU/Linux)/UNIX.
  • wbd
    wbd almost 11 years
    Why do you encode to Base64?
  • Maurício
    Maurício over 10 years
    @Ben It's common practice to encode to Base64 to allow you to store the resultant value in a text-file or string-based database column, or similar.
  • nanosoft
    nanosoft about 9 years
    @user1007231 - Where to keep - "Just make sure to keep the originals in your own secure place..." ?
  • nanosoft
    nanosoft about 9 years
    @PeteyB - Didn't understood ? Can you point me to some links that can enlighten me. Thanks
  • dwjohnston
    dwjohnston almost 9 years
    Does this solution mean that if an attacker has access to your source code, they can decrypt the passwords?
  • jwilleke
    jwilleke almost 9 years
    Base64 is not encryption.
  • user1007231
    user1007231 almost 9 years
    @nanosoft - Get a "Aegis Secure Key USB" and store in a text doc there or on paper in your wallet
  • Jeryl Cook
    Jeryl Cook over 8 years
    yes @dwjohnston , access to your code they can decrypt. but you could save the secret key (SecretKey) to a file that lives outside of your source code.
  • Faither
    Faither about 8 years
    Use MD5 or other hash algorythm. That will give you the best security.
  • Johannes Brodwall
    Johannes Brodwall about 8 years
    Hashing is better if you need to verify a secret. But when you need to store a secret you will need to submit to another system, hashing doesn't help.
  • Kimball Robinson
    Kimball Robinson almost 8 years
    @V.7 nope. MD5 is absolutely not secure for password hashing and was never designed for that purpose. Never use it for that. These days, Argon2 is best. See owasp.org/index.php/Password_Storage_Cheat_Sheet and paragonie.com/blog/2016/02/how-safely-store-password-in-2016
  • Johannes Brodwall
    Johannes Brodwall about 7 years
    @MaartenBodewes Thank you for pointing me in the direection of PBKDF2. I learned stuff both about the algorithm and the theory around slow hashes. Good stuff to learn. I've updated the code - I hope you like the new version better.
  • Maarten Bodewes
    Maarten Bodewes about 7 years
    Much better this way. Of course a secure random salt and an iteration count of a (conservative, low end) of 40K would be nicer, but at least you've indicated these things in the comments and PBKDF2 and AES/CBC are definite improvements. I think it is great how you handled this, by updating the answer; I'll at remove the warning. Voted up your comment so that people aren't surprised to find the updated code (they can look at the edits to find the old code I suppose). Might be a good idea to clean up your old comments as well.
  • Johannes Brodwall
    Johannes Brodwall about 7 years
    Thanks for the vote! I'm happy to learn - that's why we're here. :-) I updated the iteration count to 40k and changed the comment to reflect this. I've also deleted an earlier comment. Regarding the salt: Is it correct that the salt can be stored in the prop files with the encrypted properties? I also wonder if you have any insights into why other key-lengths than 128 (and 132, it turns out) throw exception.
  • Johannes Brodwall
    Johannes Brodwall about 7 years
    @dwjohnston - I've updated the code example to avoid the possible confusion with hardcoding the password. Hope this makes it clearer.
  • robob
    robob about 7 years
    Base64 is not ancryption and it's the worst example you can provide...a lot of people believe that base64 is an encryption algortihm, so better to not confuse them...
  • atom88
    atom88 over 6 years
    notice that the decode() method in the top of the source file does the actual encryption. However, this base64 encoding and decoding is needed to convert from a string the bytes in order to pass this function something that it can use (a byte array byte[])
  • atom88
    atom88 over 6 years
    or store them in an HSM (Hardware Security Module) en.wikipedia.org/wiki/Hardware_security_module
  • atom88
    atom88 over 6 years
    The real solution is to store your private key somewhere else, like a card or an HSM: en.wikipedia.org/wiki/Hardware_security_module
  • atom88
    atom88 over 6 years
    using an HSM is the ideal way: en.wikipedia.org/wiki/Hardware_security_module
  • ifly6
    ifly6 over 6 years
    This doesn't work anymore.
  • Johannes Brodwall
    Johannes Brodwall about 6 years
    @ifly6 What error/problem do you encounter?
  • ifly6
    ifly6 about 6 years
    I’d have to run it and check my computer, but iirc, it has to do with some libraries no longer existing in Java 9’s JDK.
  • Johannes Brodwall
    Johannes Brodwall about 6 years
    @ifly6 i tried to run this with Oracle JDK 9.0.4 and get the expected behavior. Please provide more information if I am going to help. :-)
  • Stefan
    Stefan about 6 years
    This was simply great! Thanks!
  • atom88
    atom88 over 5 years
    The most secure way to store the secret key would be in an HSM (Hardware Security Module) en.wikipedia.org/wiki/Hardware_security_module
  • atom88
    atom88 over 5 years
    You could also store your secret key on a USB drive with a PIN "Padlock" type of device like this one: CORSAIR Flash Padlock 3 16GB Secure USB 3.0 Flash Drive with Keypad 256bit AES Encryption Model CMFPLA3B-16GB newegg.com/Product/…-9SIAH9B7YN2197--Product
  • benez
    benez over 2 years
    @atom88 how do you plugin the HSM to your cloud server?
  • atom88
    atom88 over 2 years
    There are what they call "Network HSM's" that can be done over a network. Presumably you could somehow connect your physical HSM to a cloud provider. AWS for example can generate keys or you can BYOK (Bring your own keys) with you to use for things like encryption/decryption. It's all about whom you trust (cloud provider), admins at the cloud provider, or your network layer, etc.