In some projects, we exposed our username and password of mySql in file .properties, which is not safe. To solve this issue, in this post we will introduct, 1) how to use DES to encode and decode information, 2) how to customized the resolution of ${...} placeholders against local properties based on class PropertyPlaceholderConfigurer.
1. DES
The Data Encryption Standard (DES) is a symmetric-key algorithm for the encryption of data. To encrypt our important information, we need to write a DESUtil at first, which is responsible to encrypt and decrypt strings. In DESUtil we first need to generate a key for encrption. Then we can write getEncryptString(...) and getDecryptString(...) methods. The code in DESUtil is shown as follows.
CODE
/**
* DES algorithm for Encoding and Decoding configuration properties in our project
* It is a symmetric Security algorithm,
*/
public class DESUtil {
private static Key key;
//setting key
private static String KEY_STR = "mykey";
private static String CHARSETNAME = "UTF-8";
private static String ALGORITHM = "DES";
/**
* generate key for encoding
*/
static {
try {
// create a generator instance for generating code using DES algorithm
KeyGenerator generator = KeyGenerator.getInstance(ALGORITHM);
// Using SHA1 security strategy
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
// Setting seed
secureRandom.setSeed(KEY_STR.getBytes());
generator.init(secureRandom);
key = generator.generateKey();
generator = null;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* To Encrypt a String
*
* @param str:String need to Encrypt
* @return
*/
public static String getEncryptString(String str) {
//BASE64 Encoding, receiving byte[] array and change it to String
BASE64Encoder base64Decoder = new BASE64Encoder();
try {
//Encoding way:UTF-8
byte[] bytes = str.getBytes(CHARSETNAME);
// create a cipher based on DES algorithm
Cipher cipher = Cipher.getInstance(ALGORITHM);
// using generated key to init the cipher
cipher.init(Cipher.ENCRYPT_MODE, key);
// get the encrypted bytes
byte[] doFinal = cipher.doFinal(bytes);
// return the encrypted String
return base64Decoder.encode(doFinal);
} catch (Exception e) {
//TODO:handle exception
throw new RuntimeException(e);
}
}
public static String getDecryptString(String str){
BASE64Decoder base64Decoder = new BASE64Decoder();
try{
//Encoding way:UTF-8
byte[] bytes = base64Decoder.decodeBuffer(str);
//get he cipher instance
Cipher cipher = Cipher.getInstance(ALGORITHM);
// using the generated key
cipher.init(Cipher.DECRYPT_MODE,key);
// get the decoded byte stream
byte[] doFinal = cipher.doFinal(bytes);
// change it to string using UTF_8 charset
return new String(doFinal,CHARSETNAME);
}catch (Exception e){
//TODO:handle exception
throw new RuntimeException(e);
}
}
public static void main(String[] args){
System.out.println(getEncryptString("username"));
System.out.println(getEncryptString("password"));
}
}
2. PropertyPlaceholderConfigurer
PlaceholderConfigurer(org.springframework.beans.factory.config) subclass that resolves ${...} placeholders against local properties and/or system properties and environment variables.
To enable encryption and decryption of placeholders in our configuration files with DES, first we create a class EncryptPropertyPlaceholderConfigurer which extends PlaceholderConfigurer. In this class, we override method convertProperty to realizing the convertion using DES.
CODE
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
public class EncryptPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer {
// all the String need to decryptStirng
private String[] encryptPropNames = {"jdbc.username","jdbc.password"};
/**
* return the property value of the corresponding property value
* @param propertyName
* @param propertyValue
* @return
*/
@Override
protected String convertProperty(String propertyName, String propertyValue) {
if(isEncryptProp(propertyName)){
String decryptValue = DESUtil.getDecryptString(propertyValue);
return decryptValue;
}
return propertyValue;
}
/**
* verify if that property has encrypted
* @param propertyName
* @return
*/
private boolean isEncryptProp(String propertyName) {
for(String encryptPropName:encryptPropNames){
if(propertyName.equals(encryptPropName)) return true;
}
return false;
}
}
Then we need to create a bean to tell spring use created EncryptPropertyPlaceholderConfigurer to reolve system properties. Previous, we only appoint the location where to find ${...} placeholders like
<context:property-holder location="classpath:jdbc.properties"/>
Now we define a bean like this.
<bean class="com.example.o2o.util.EncryptPropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:jdbc.properties</value>
<value>classpath:redis.properties</value>
</list>
</property>
<property name="fileEncoding" value="UTF-8" />
</bean>