user234194
user234194

Reputation: 1723

encrypt and decrypt property file value in java

I am looking for a way to encrypt a password in a configuration file that is being read by a Java program. Currently, I read-in the password from the text file, but that leaves the password sitting right out in the open if someone were to look at the config file.

I was thinking of building a simple class where user could type in their desired password, get an encrypted version of the password, then paste the encrypted version into the configuration text file. Then the application would read encrypted password, decrypt the password back into a string, and move on.

I am having trouble with the string-->encrytped bytes-->string conversions.

I am using the built-in java security classes to implement this code. Here is some sample test code:

    // Reads password from config file
String password = ScriptConfig.getString( "password" );

// Generate Key
KeyGenerator kg = KeyGenerator.getInstance("DES");
Key key = kg.generateKey();

// Create Encryption cipher
Cipher cipher = Cipher.getInstance( "DES" );
cipher.init( Cipher.ENCRYPT_MODE, key );

// Encrypt password
byte[] encrypted = cipher.doFinal( password.getBytes() );

// Create decryption cipher
cipher.init( Cipher.DECRYPT_MODE, key );
byte[] decrypted = cipher.doFinal( encrypted );

// Convert byte[] to String
String decryptedString = new String(decrypted);

System.out.println("password: " + password);
System.out.println("encrypted: " + encrypted);
System.out.println("decrypted: " + decryptedString);

// Read encrypted string from config file
String encryptedPassword = ScriptConfig.getString( "encryptedPassword"
);

// Convert encryptedPassword string into byte[]
byte[] encryptedPasswordBytes = new byte[1024];
encryptedPasswordBytes = encryptedPassword.getBytes();

// Decrypt encrypted password from config file
byte[] decryptedPassword = cipher.doFinal( encryptedPasswordBytes );//error here

System.out.println("encryptedPassword: " + encryptedPassword);
System.out.println("decryptedPassword: " + decryptedPassword);


The config file has the following variables:
password=password
encryptedPassword=[B@2a4983


When I run the code, I get the following output:
password: passwd
encrypted: [B@2a4983
decrypted: passwd
javax.crypto.IllegalBlockSizeException: Input length must be multiple
of 8 when decrypting with padded cipher
at com.sun.crypto.provider.SunJCE_h.b(DashoA12275)
at com.sun.crypto.provider.SunJCE_h.b(DashoA12275)
at com.sun.crypto.provider.DESCipher.engineDoFinal(Da shoA12275)
at javax.crypto.Cipher.doFinal(DashoA12275)
at com.sapient.fbi.uid.TestEncryption.main(TestEncryp tion.java:4

Any help on the error, structure, or process I am using to do this would be great. Thanks.

Upvotes: 15

Views: 53065

Answers (4)

Cheesle
Cheesle

Reputation: 124

A really simple solution would be to use Base64 encoding, see the code snippet below :-

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

...

private String encode(String str) {
    BASE64Encoder encoder = new BASE64Encoder();
    str = new String(encoder.encodeBuffer(str.getBytes()));
    return str;
}

private String decode(String str) {
    BASE64Decoder decoder = new BASE64Decoder();
    try {
        str = new String(decoder.decodeBuffer(str));
    } catch (IOException e) {
        e.printStackTrace();
    }       
    return str;
}

...

Upvotes: -12

sujesh
sujesh

Reputation: 61

Here are some helpers to encrypt or decrypt using AES in Java:

public static final String AES = "AES";

/**
 * Encrypt a value and generate a keyfile.
 * If the keyfile is not found, then a new one will be created.
 * 
 * @throws GeneralSecurityException
 * @throws IOException if an I/O error occurs
 */
public static String encrypt(String value, File keyFile)
        throws GeneralSecurityException, IOException {
    if (!keyFile.exists()) {
        KeyGenerator keyGen = KeyGenerator.getInstance(CryptoUtils.AES);
        keyGen.init(128);
        SecretKey sk = keyGen.generateKey();
        FileWriter fw = new FileWriter(keyFile);
        fw.write(byteArrayToHexString(sk.getEncoded()));
        fw.flush();
        fw.close();
    }

    SecretKeySpec sks = getSecretKeySpec(keyFile);
    Cipher cipher = Cipher.getInstance(CryptoUtils.AES);
    cipher.init(Cipher.ENCRYPT_MODE, sks, cipher.getParameters());
    byte[] encrypted = cipher.doFinal(value.getBytes());
    return byteArrayToHexString(encrypted);
}

/**
 * Decrypt a value.
 * 
 * @throws GeneralSecurityException
 * @throws IOException if an I/O error occurs
 */
public static String decrypt(String message, File keyFile)
        throws GeneralSecurityException, IOException {
    SecretKeySpec sks = getSecretKeySpec(keyFile);
    Cipher cipher = Cipher.getInstance(CryptoUtils.AES);
    cipher.init(Cipher.DECRYPT_MODE, sks);
    byte[] decrypted = cipher.doFinal(hexStringToByteArray(message));
    return new String(decrypted);
}

private static SecretKeySpec getSecretKeySpec(File keyFile)
        throws NoSuchAlgorithmException, IOException {
    byte[] key = readKeyFile(keyFile);
    SecretKeySpec sks = new SecretKeySpec(key, CryptoUtils.AES);
    return sks;
}

private static byte[] readKeyFile(File keyFile)
        throws FileNotFoundException {
    Scanner scanner = new Scanner(keyFile).useDelimiter("\\Z");
    String keyValue = scanner.next();
    scanner.close();
    return hexStringToByteArray(keyValue);
}

private static String byteArrayToHexString(byte[] b) {
    StringBuffer sb = new StringBuffer(b.length * 2);
    for (int i = 0; i < b.length; i++) {
        int v = b[i] & 0xff;
        if (v < 16) {
            sb.append('0');
        }
        sb.append(Integer.toHexString(v));
    }
    return sb.toString().toUpperCase();
}

private static byte[] hexStringToByteArray(String s) {
    byte[] b = new byte[s.length() / 2];
    for (int i = 0; i < b.length; i++) {
        int index = i * 2;
        int v = Integer.parseInt(s.substring(index, index + 2), 16);
        b[i] = (byte) v;
    }
    return b;
}

Just call the appropiate method.

Upvotes: 6

AngerClown
AngerClown

Reputation: 6229

Take a look at Jasypt. It has already done the heavy lifting for you. Specifically, the org.jasypt.encryption.pbe.StandardPBEStringEncryptor and org.jasypt.properties.PropertyValueEncryptionUtils classes.

Create an encryptor:

SimplePBEConfig config = new SimplePBEConfig(); 
config.setAlgorithm("PBEWithMD5AndTripleDES");
config.setKeyObtentionIterations(1000);
config.setPassword("propertiesFilePassword");

StandardPBEStringEncryptor encryptor = new org.jasypt.encryption.pbe.StandardPBEStringEncryptor();
encryptor.setConfig(config);
encryptor.initialize();

Then use PropertyValueEncryptionUtils to encrypt / decrypt values:

PropertyValueEncryptionUtils.encrypt(value, encryptor);
PropertyValueEncryptionUtils.decrypt(encodedValue, encryptor)

Note the encoded value will start with ENC( and end with ), so it's easy to tell if a property from a file is encrypted.

Also, note that the password used for config.setPassword() is not the password you are encoding to store in the properties file. Instead, it is the password to encrypt / decrypt the value you are storing. What this password is and how to set it is up to you. I default to the fully qualified classname of whatever is reading the Properties file.

Finally, if you are using Spring, Jasypt has an EncryptablePropertyPlaceholderConfigurer class that you can use to load the properties file and use ${foo} syntax in your Spring XML files to do variable substitution for things like DB passwords.

Upvotes: 15

Waldheinz
Waldheinz

Reputation: 10487

I am having trouble with the string-->encrytped bytes-->string conversions.

I'd stuff the byte array through a base64 en/decoder, this way you'll have to persist strings that contain nothing but chars within (a subset of) ASCII, which should limit your trouble. Have a look e.g. at commons codecs and replace your new String(decrypted) with a call to one of the static methods in the org.apache.commons.codec.binary.Base64 class.

Besides that I think what you ultimately want to do is not strictly to "encrypt" the password but rather to store only a hash of the password, which was already discussed on SO.

Upvotes: 2

Related Questions