现有需求,需要对配置文件里,敏感的字段进行加密,参考了properties文件加密的方法。使用时发现好处是,每次加密得到的密文都不一样,这样同样的value可以加密成不同的密码。为了支持在properties文件中,同时能读取明文的配置值和密文的配置值,采用了扩展Properties类的方法
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 java.util.Properties;
import java.util.Scanner;
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 EncryptProperties extends Properties {
/**
*
*/
private static final long serialVersionUID = 1L;
private static SecretKeySpec secKey;
private static final String HEADER = "ENC_";
static {
final String password = "sasflc";
final int iterationCount = 40000;
final int keyLength = 128;
final byte[] salt = new String("fafszxcvxv1fadscxv").getBytes();
try {
secKey = createSecretKey(password.toCharArray(),
salt, iterationCount, keyLength);
}
catch(Exception e) {
e.printStackTrace();
}
}
@Override
public String getProperty(String key) {
String res = super.getProperty(key);
return EncryptProperties.getDecryptValue(res);
}
public static String getEncryptValue(String value) {
if(value != null) {
try {
value = HEADER + encrypt(value);
}
catch(Exception e) {
e.printStackTrace();
}
}
return value;
}
public static String getDecryptValue(String value) {
if(value != null && value.startsWith(HEADER)) {
try {
String enc = value.substring(4);
try {
value = decrypt(enc);
}
catch(Exception e) {
e.printStackTrace();
}
}
catch(Exception e) {
e.printStackTrace();
}
}
return value;
}
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) throws GeneralSecurityException, UnsupportedEncodingException {
Cipher pbeCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
pbeCipher.init(Cipher.ENCRYPT_MODE, secKey);
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) 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, secKey, 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);
}
在配置文件里,所有值以ENC_开头的,按照加密值进行读取和解密;其它的值仍然采用getProperty()的方法获取明文的值。