文章目录
简介
配置文件中一些敏感信息通常需要加密,例如数据库密码等信息。
如何方便快捷的对这些配置加密呢?
比较常见的方式是使用jasypt。
很多时候其实我们并不关心jasypt如何加密配置的,但是有时候我们得看其他一些项目,找相关数据库信息,如果密码被jasypt加密了。
这时就需要我们多了解一下jasypt,才能得到原始的密码。
简单使用
在SpringBoot中使用jasypt非常简单。
添加依赖
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
选择和SpringBoot的适配的版本。
配置StringEncryptor
import org.jasypt.encryption.StringEncryptor;
import org.jasypt.encryption.pbe.PooledPBEStringEncryptor;
import org.jasypt.encryption.pbe.config.SimpleStringPBEConfig;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class JasyptConfig {
@Value("${jasypt.encryptor.pd}")
private String pd;
@Bean("jasyptStringEncryptor")
public StringEncryptor stringEncryptor(){
PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
SimpleStringPBEConfig config = new SimpleStringPBEConfig();
config.setPassword(pd);
config.setAlgorithm("PBEWithMD5AndDES");
config.setKeyObtentionIterations(1000);
config.setPoolSize(1);
config.setProviderName("SunJCE");
config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator");
config.setStringOutputType("base64");
encryptor.setConfig(config);
return encryptor;
}
}
上面配置了一个PooledPBEStringEncryptor是jasypt自己实现的类。
在application.properties配置文件中配置PooledPBEStringEncryptor的密码:
jasypt.encryptor.pd=123456abc
配置@EnableEncryptableProperties
启动类中配置@EnableEncryptableProperties,注意可能和Druid冲突。
@SpringBootApplication
@EnableEncryptableProperties
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
测试是否能解密
添加一个属性类:
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
@PropertySource("classpath:code.properties")
@ConfigurationProperties(prefix = "code")
@Component
@Data
public class CodeProperties {
private String code;
}
在对应配置文件code.properties中配置相应属性:
code.code=ENC(HNfBIwi7zVx5pUk0fN3Xow==)
ENC(加密之后的内容)
内容是通过配置的PooledPBEStringEncryptor加密之后的内容,可以通过PooledPBEStringEncryptor的encryp方法获取。
测试类:
import jakarta.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import vip.meet.App;
@SpringBootTest(classes = App.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class CodePropertiesTest {
@Resource
private CodeProperties codeProperties;
@Test
void getCode() {
System.out.println(codeProperties.getCode());
}
}
我们可以看到打印出了原始密码123456
是不是很简单,很容易找出密码了。
但是并不是所有项目都会使用jasypt实现的PooledPBEStringEncryptor。
有的项目会自定义StringEncryptor,下面我们来看怎样自定义StringEncryptor。
自定义StringEncryptor
比如我们要定义一个使用RSA加密方式的,我们只需要实现StringEncryptor接口就可以。
实现StringEncryptor
import org.jasypt.encryption.StringEncryptor;
import org.jetbrains.annotations.NotNull;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import vip.meet.common.encrypt.rsa.RSAHelper;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RsaStringEncryptor implements StringEncryptor, EnvironmentAware {
private Environment environment;
public String encrypt(String message) {
throw new RuntimeException("不支持加密");
}
public String decrypt(String encryptedMessage) {
return this.decryptPassword(encryptedMessage);
}
private String decryptPassword(String message) {
String match = "\\S+#\\{\\S*}";
boolean isMatch = Pattern.matches(match, message);
if (isMatch) {
String destRegex = "#\\{\\S*}";
Pattern pattern = Pattern.compile(destRegex);
Matcher matcher = pattern.matcher(message);
if (matcher.find()) {
String group = matcher.group();
String destStr = group.substring(2, group.length() - 1);
if (destStr.isEmpty()) {
throw new RuntimeException(String.format("秘钥不能为空,当前value值为%s", message));
} else {
String secretKey = environment.getProperty(destStr);
String cipherText = message.substring(0, message.lastIndexOf(group));
try {
return RSAHelper.decrypt(secretKey, cipherText);
} catch (Exception e) {
throw new RuntimeException(String.format("解密失败,请确认密文和秘钥,当前value值为%s,密文为%s,秘钥为%s", message, cipherText, secretKey),e);
}
}
} else {
throw new RuntimeException("需要解密的密文需要按照'密文#{秘钥}'的形式传进来");
}
} else {
throw new RuntimeException("需要解密的密文需要按照'密文#{秘钥}'的形式传进来");
}
}
public void setEnvironment(@NotNull Environment environment) {
this.environment = environment;
}
}
RSA工具类:RSAHelper
import lombok.extern.slf4j.Slf4j;
import javax.crypto.Cipher;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.security.spec.X509EncodedKeySpec;
@Slf4j
public class RSAHelper {
private static final String RSA = "RSA";
public static String decrypt(String cipherText) throws Exception {
return decrypt((String) null, cipherText);
}
public static String decrypt(String publicKeyText, String cipherText)
throws Exception {
PublicKey publicKey = getPublicKey(publicKeyText);
return decrypt(publicKey, cipherText);
}
public static PublicKey getPublicKey(String publicKeyText) {
if (publicKeyText == null || publicKeyText.isEmpty()) {
throw new RuntimeException("密钥不能为空");
}
try {
byte[] publicKeyBytes = base64ToByteArray(publicKeyText);
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(
publicKeyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(RSA, "SunRsaSign");
return keyFactory.generatePublic(x509KeySpec);
} catch (Exception e) {
throw new IllegalArgumentException("Failed to get public key", e);
}
}
public static String decrypt(PublicKey publicKey, String cipherText)
throws Exception {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
try {
cipher.init(Cipher.DECRYPT_MODE, publicKey);
} catch (InvalidKeyException e) {
RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey;
RSAPrivateKeySpec spec = new RSAPrivateKeySpec(rsaPublicKey.getModulus(), rsaPublicKey.getPublicExponent());
Key fakePrivateKey = KeyFactory.getInstance(RSA).generatePrivate(spec);
cipher = Cipher.getInstance(RSA);
cipher.init(Cipher.DECRYPT_MODE, fakePrivateKey);
}
if (cipherText == null || cipherText.isEmpty()) {
return cipherText;
}
byte[] cipherBytes = base64ToByteArray(cipherText);
byte[] plainBytes = cipher.doFinal(cipherBytes);
return new String(plainBytes);
}
public static String encrypt(String key, String plainText) throws Exception {
if (key == null || key.isEmpty()) {
throw new RuntimeException("密钥不能为空");
}
byte[] keyBytes = base64ToByteArray(key);
return encrypt(keyBytes, plainText);
}
public static String encrypt(byte[] keyBytes, String plainText)
throws Exception {
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory factory = KeyFactory.getInstance(RSA, "SunRsaSign");
PrivateKey privateKey = factory.generatePrivate(spec);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
try {
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
} catch (InvalidKeyException e) {
RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) privateKey;
RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec(rsaPrivateKey.getModulus(), rsaPrivateKey.getPrivateExponent());
Key fakePublicKey = KeyFactory.getInstance(RSA).generatePublic(publicKeySpec);
cipher = Cipher.getInstance(RSA);
cipher.init(Cipher.ENCRYPT_MODE, fakePublicKey);
}
byte[] encryptedBytes = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
return byteArrayToBase64(encryptedBytes);
}
public static byte[][] genKeyPairBytes(int keySize)
throws NoSuchAlgorithmException, NoSuchProviderException {
byte[][] keyPairBytes = new byte[2][];
KeyPairGenerator gen = KeyPairGenerator.getInstance(RSA, "SunRsaSign");
gen.initialize(keySize, new SecureRandom());
KeyPair pair = gen.generateKeyPair();
keyPairBytes[0] = pair.getPrivate().getEncoded();
keyPairBytes[1] = pair.getPublic().getEncoded();
return keyPairBytes;
}
public static String[] genKeyPair(int keySize)
throws NoSuchAlgorithmException, NoSuchProviderException {
byte[][] keyPairBytes = genKeyPairBytes(keySize);
String[] keyPairs = new String[2];
keyPairs[0] = byteArrayToBase64(keyPairBytes[0]);
keyPairs[1] = byteArrayToBase64(keyPairBytes[1]);
return keyPairs;
}
public static String byteArrayToBase64(byte[] a) {
return byteArrayToBase64(a, false);
}
/**
* Translates the specified byte array into an "alternate representation" Base64 string. This non-standard variant
* uses an alphabet that does not contain the uppercase alphabetic characters, which makes it suitable for use in
* situations where case-folding occurs.
*/
public static String byteArrayToAltBase64(byte[] a) {
return byteArrayToBase64(a, true);
}
private static String byteArrayToBase64(byte[] a, boolean alternate) {
int aLen = a.length;
int numFullGroups = aLen / 3;
int numBytesInPartialGroup = aLen - 3 * numFullGroups;
int resultLen = 4 * ((aLen + 2) / 3);
StringBuilder result = new StringBuilder(resultLen);
char[] intToAlpha = (alternate ? intToAltBase64 : intToBase64);
// Translate all full groups from byte array elements to Base64
int inCursor = 0;
for (int i = 0; i < numFullGroups; i++) {
int byte0 = a[inCursor++] & 0xff;
int byte1 = a[inCursor++] & 0xff;
int byte2 = a[inCursor++] & 0xff;
result.append(intToAlpha[byte0 >> 2]);
result.append(intToAlpha[(byte0 << 4) & 0x3f | (byte1 >> 4)]);
result.append(intToAlpha[(byte1 << 2) & 0x3f | (byte2 >> 6)]);
result.append(intToAlpha[byte2 & 0x3f]);
}
// Translate partial group if present
if (numBytesInPartialGroup != 0) {
int byte0 = a[inCursor++] & 0xff;
result.append(intToAlpha[byte0 >> 2]);
if (numBytesInPartialGroup == 1) {
result.append(intToAlpha[(byte0 << 4) & 0x3f]);
result.append("==");
} else {
// assert numBytesInPartialGroup == 2;
int byte1 = a[inCursor++] & 0xff;
result.append(intToAlpha[(byte0 << 4) & 0x3f | (byte1 >> 4)]);
result.append(intToAlpha[(byte1 << 2) & 0x3f]);
result.append('=');
}
}
// assert inCursor == a.length;
// assert result.length() == resultLen;
return result.toString();
}
/**
* This array is a lookup table that translates 6-bit positive integer index values into their "Base64 Alphabet"
* equivalents as specified in Table 1 of RFC 2045.
*/
private static final char[] intToBase64 = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2',
'3', '4', '5', '6', '7', '8', '9', '+', '/'};
/**
* This array is a lookup table that translates 6-bit positive integer index values into their
* "Alternate Base64 Alphabet" equivalents. This is NOT the real Base64 Alphabet as per in Table 1 of RFC 2045. This
* alternate alphabet does not use the capital letters. It is designed for use in environments where "case folding"
* occurs.
*/
private static final char[] intToAltBase64 = {'!', '"', '#', '$', '%', '&', '\'', '(', ')', ',', '-', '.', ':',
';', '<', '>', '@', '[', ']', '^', '`', '_', '{', '|', '}', '~', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2',
'3', '4', '5', '6', '7', '8', '9', '+', '?'};
/**
* Translates the specified Base64 string (as per Preferences.get(byte[])) into a byte array.
*/
public static byte[] base64ToByteArray(String s) {
return base64ToByteArray(s, false);
}
/**
* Translates the specified "alternate representation" Base64 string into a byte array.
*/
public static byte[] altBase64ToByteArray(String s) {
return base64ToByteArray(s, true);
}
private static byte[] base64ToByteArray(String s, boolean alternate) {
byte[] alphaToInt = (alternate ? altBase64ToInt : base64ToInt);
int sLen = s.length();
int numGroups = sLen / 4;
if (4 * numGroups != sLen) {
throw new IllegalArgumentException("String length must be a multiple of four.");
}
int missingBytesInLastGroup = 0;
int numFullGroups = numGroups;
if (sLen != 0) {
if (s.charAt(sLen - 1) == '=') {
missingBytesInLastGroup++;
numFullGroups--;
}
if (s.charAt(sLen - 2) == '=') {
missingBytesInLastGroup++;
}
}
byte[] result = new byte[3 * numGroups - missingBytesInLastGroup];
// Translate all full groups from base64 to byte array elements
int inCursor = 0, outCursor = 0;
for (int i = 0; i < numFullGroups; i++) {
int ch0 = base64toInt(s.charAt(inCursor++), alphaToInt);
int ch1 = base64toInt(s.charAt(inCursor++), alphaToInt);
int ch2 = base64toInt(s.charAt(inCursor++), alphaToInt);
int ch3 = base64toInt(s.charAt(inCursor++), alphaToInt);
result[outCursor++] = (byte) ((ch0 << 2) | (ch1 >> 4));
result[outCursor++] = (byte) ((ch1 << 4) | (ch2 >> 2));
result[outCursor++] = (byte) ((ch2 << 6) | ch3);
}
// Translate partial group, if present
if (missingBytesInLastGroup != 0) {
int ch0 = base64toInt(s.charAt(inCursor++), alphaToInt);
int ch1 = base64toInt(s.charAt(inCursor++), alphaToInt);
result[outCursor++] = (byte) ((ch0 << 2) | (ch1 >> 4));
if (missingBytesInLastGroup == 1) {
int ch2 = base64toInt(s.charAt(inCursor++), alphaToInt);
result[outCursor++] = (byte) ((ch1 << 4) | (ch2 >> 2));
}
}
// assert inCursor == s.length()-missingBytesInLastGroup;
// assert outCursor == result.length;
return result;
}
/**
* Translates the specified character, which is assumed to be in the "Base 64 Alphabet" into its equivalent 6-bit
* positive integer.
*
* @throw IllegalArgumentException or ArrayOutOfBoundsException if c is not in the Base64 Alphabet.
*/
private static int base64toInt(char c, byte[] alphaToInt) {
int result = alphaToInt[c];
if (result < 0) {
throw new IllegalArgumentException("Illegal character " + c);
}
return result;
}
/**
* This array is a lookup table that translates unicode characters drawn from the "Base64 Alphabet" (as specified in
* Table 1 of RFC 2045) into their 6-bit positive integer equivalents. Characters that are not in the Base64
* alphabet but fall within the bounds of the array are translated to -1.
*/
private static final byte[] base64ToInt = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62,
-1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7,
8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28,
29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51};
/**
* This array is the analogue of base64ToInt, but for the nonstandard variant that avoids the use of uppercase
* alphabetic characters.
*/
private static final byte[] altBase64ToInt = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, -1, 62, 9, 10,
11, -1, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 12, 13, 14, -1, 15, 63, 16, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 17, -1, 18, 19, 21, 20, 26, 27, 28,
29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 22, 23, 24, 25};
}
配置自定义
import org.jasypt.encryption.StringEncryptor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class JasyptConfig {
@Bean("jasyptStringEncryptor")
public StringEncryptor stringEncryptor(){
// 更好的方式是在RsaStringEncryptor直接指定@Component("jasyptStringEncryptor")
// new的方式不会直接处理EnvironmentAware
return new RsaStringEncryptor();
}
}
注意:bean name固定为jasyptStringEncryptor,否则jasypt回去创建一个默认的jasyptStringEncryptor。
属性类
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
@PropertySource("classpath:code.properties")
@ConfigurationProperties(prefix = "code")
@Component
@Data
public class CodeProperties {
private String code;
private String password;
}
配置加密项
code.password=ENC(aqEZbUMLgAQVY/nTkzxOfu4CUNr9Q0f00fQQzQAOsWsi6BPopRfkqxjoBJz3g7ddsO+H/GBfYQwYIuUjeNMekw==#{code.publicKey})
code.publicKey=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAMzghZ5VNqTqHgTlzKvL3UUAc3uqcg5cw+lXH3mmra01dOuNxm2fziWCS5sX5+8EIJ2GRKWR1WaCskcFWgnU8WkCAwEAAQ==
上面的配置是不是看起来很奇怪,那是因为我们自定义的RsaStringEncryptor就是这个逻辑。
当jasypt发现有需要解密的就会使用RsaStringEncryptor的decrypt来解密。
结果
我们可以看到,最终解密之后的结果123456
从配置和结果我们可以看到StringEncryptor是非常灵活的。
那StringEncryptor如何判断哪些项需要解密的呢?
答案是:去匹配:ENC(),匹配到,括号中的就是需要解密的内容。
为什么呢?
因为com.ulisesbocchio.jasyptspringboot.detector.DefaultPropertyDetector
【】
具体请看下一节
EncryptablePropertyDetector与EncryptablePropertyResolver
EncryptablePropertyResolver是用来处理属性值,他的默认实现类DefaultPropertyResolver,使用了默认DefaultPropertyDetector:
public DefaultPropertyResolver(StringEncryptor encryptor, Environment environment) {
this(encryptor, new DefaultPropertyDetector(), environment);
}
那我们可不可以自定义这2个接口呢?
当然可以。
自定义EncryptablePropertyDetector
isEncrypted是判断是否需要执行解密操作,unwrapEncryptedValue是获取需要解密的字符串。
import com.ulisesbocchio.jasyptspringboot.EncryptablePropertyDetector;
import org.springframework.stereotype.Component;
@Component("encryptablePropertyDetector")
//@Component
public class PDEncryptablePropertyDetector implements EncryptablePropertyDetector {
private static final String PREFIX = "pd::";
@Override
public boolean isEncrypted(String property) {
if (property == null) {
return false;
}
return property.startsWith(PREFIX);
}
@Override
public String unwrapEncryptedValue(String property) {
return property.substring(PREFIX.length());
}
}
表示如果属性值是以pd::开头,则需要解密。
自定义EncryptablePropertyResolver
重点是实现resolvePropertyValue,执行具体的加密逻辑。
注意需要指定名字encryptablePropertyResolver。
import com.ulisesbocchio.jasyptspringboot.EncryptablePropertyResolver;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Objects;
import java.util.Optional;
@Slf4j
@Component("encryptablePropertyResolver")
//@Component
public class DESEncryptablePropertyDetector implements EncryptablePropertyResolver {
@Resource
private PDEncryptablePropertyDetector pdEncryptablePropertyDetector;
@Override
public String resolvePropertyValue(String value) {
return Optional.ofNullable(value)
.filter(pdEncryptablePropertyDetector::isEncrypted)
.map(resolveValue -> {
final String unwrapEncryptedValue = pdEncryptablePropertyDetector.unwrapEncryptedValue(resolveValue);
return decrypt(unwrapEncryptedValue);
})
.orElse(value);
}
/**
* 秘钥字符串
*/
private static final String PASSWORD = "123456abc";
private static final String ALGORITHM = "DES";
/**
* 解密DES
* @param datasource 需要加密的内容
* @return 解密后的明文 数据
*/
public static String decrypt(String datasource){
try{
DESKeySpec desKey = new DESKeySpec(PASSWORD.getBytes());
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM);
SecretKey secretkey = keyFactory.generateSecret(desKey);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, secretkey, new SecureRandom());
return new String(cipher.doFinal(Objects.requireNonNull(parseHexStr2Byte(datasource))));
}catch(Throwable e){
log.error(e.getMessage(),e);
throw new RuntimeException("解密错误");
}
}
/**
* 加密32位
* @param datasource 需要加密的内容
* @return 加密的内容
*/
public static String encrypt(String datasource) {
try{
DESKeySpec desKey = new DESKeySpec(PASSWORD.getBytes());
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM);
SecretKey secretKey = keyFactory.generateSecret(desKey);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, new SecureRandom());
return parseByte2HexStr(cipher.doFinal(datasource.getBytes(StandardCharsets.UTF_8)));
}catch(Throwable e){
log.error(e.getMessage(),e);
throw new RuntimeException("加密错误");
}
}
public static String parseByte2HexStr(byte[] buf) {
StringBuilder sb = new StringBuilder();
for (byte b : buf) {
String hex = Integer.toHexString(b & 255);
if (hex.length() == 1) {
hex = '0' + hex;
}
sb.append(hex.toUpperCase());
}
return sb.toString();
}
private static byte[] parseHexStr2Byte(String hexStr) {
if (hexStr.isEmpty()) {
return null;
} else {
byte[] result = new byte[hexStr.length() / 2];
for (int i = 0; i < hexStr.length() / 2; ++i) {
int high = Integer.parseInt(hexStr.substring(i * 2, i * 2 + 1), 16);
int low = Integer.parseInt(hexStr.substring(i * 2 + 1, i * 2 + 2), 16);
result[i] = (byte) (high * 16 + low);
}
return result;
}
}
}
小结
所以,如果使用jasypt加密配置文件属性,要找到对应的加密解密方法,可以按以下步骤处理:
- 查找名字为jasyptStringEncryptor实现了StringEncryptor类的实例的decrypt方法
- 查找名字为encryptablePropertyResolver实现了EncryptablePropertyResolver类的实例的resolvePropertyValue方法