1. Jasypt库的使用
1.1 简介
Jasypt是一个Java简易加密库,用于加密配置文件中的敏感信息,如数据库密码。jasypt库与springboot集成,在实际开发中非常方便。
1.2 添加依赖
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
jasypt版本 springboot版本
2.1.0 2.1.0
1.5 1.4.2
1.5 1.5.3
1.8 1.4.2
注意:根据spring boot版本选择对应的jasypt版本
1.3 配置使用
将加密后的配置信息使用ENC函数,添加到配置文件中,应用启动加载配置文件时,会自动解密。
Jasypt默认使用的算法为PBEWithMD5AndDES,该算法需要一个加密密钥,可以在应用启动时指定。也可以直接写入配置文件,安全性稍差。
jasypt:
encryptor:
password: password
注意:这里指定加密密钥为password
1.4 测试示例
1.4.1 使用加密函数加密配置文件
public static void main(String[] args) {
// 创建加密对象,默认 PBEWithMD5AndDES
BasicTextEncryptor textEncryptor = new BasicTextEncryptor();
// 加密所需的密钥
textEncryptor.setPassword("password");
// 加密后的数据(数据库的用户名或密码)
String encData = textEncryptor.encrypt("Password@1");
// 解密后的数据(原数据)
String decData = textEncryptor.decrypt(encData);
System.out.println("encData: " + encData);
System.out.println("decData: " + decData);
}
输出:
encData: rJIqHYPSHc0EPesm650srg==
decData: root
1.4.2 添加加密后的属性配置
在配置文件中加入加密后的属性配置信息,我们加密了字符串root,使用的加密密钥为password,添加到application.yml文件中。
datasource:
master:
url: jdbc:mysql://127.0.0.1:3306/jeecg-boot?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
username: root
password: ENC(r7PD0+haO/ALC+txojmG/A==)
driver-class-name: com.mysql.cj.jdbc.Driver
2. 使用自定义加密器进行加密解密
2.1 SM4算法加密器代码案例
2.1.1 创建SM4算法加密器接口
package org.jeecg.config.jasypt.encryption.sm4;
import org.jasypt.encryption.StringEncryptor;
/**
* @Description SM4算法加密器接口
* @date 2022/3/7 14:25
* @Version 1.0
* @Author gezongyang
*/
public interface SM4StringEncryptor extends StringEncryptor {
}
2.1.2 创建SM4算法加密器
package org.jeecg.config.jasypt.encryption.sm4;
import lombok.extern.slf4j.Slf4j;
import org.jasypt.commons.CommonUtils;
import org.jeecg.common.util.security.sm4.SM4Utils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* @Description 国密Sm4 算法加密器
* @date 2022/3/7 14:17
* @Version 1.0
* @Author gezongyang
*/
@Slf4j
@Component("sM4ECBStringEncryptor")
public class SM4ECBStringEncryptor implements SM4StringEncryptor {
@Override
public String encrypt(String msg) {
String secretKey = System.getProperty("secretKey");
CommonUtils.validateNotNull(msg, "msg cannot be set null");
CommonUtils.validateNotNull(secretKey, "secretKey config not be set");
try {
log.info("加密前配置信息:{}",msg);
SM4Utils sm4 = new SM4Utils();
sm4.secretKey = secretKey;
sm4.hexString = true;
msg = sm4.encryptData_ECB(msg);
log.info("加密后配置信息:{}", msg);
} catch (Exception e) {
log.error("配置信息加密失败,{}",e.getStackTrace());
}
return msg;
}
@Override
public String decrypt(String msg) {
String secretKey = System.getProperty("secretKey");
CommonUtils.validateNotNull(msg, "msg cannot be set null");
CommonUtils.validateNotNull(secretKey, "secretKey config not be set");
try {
log.info("解密前配置信息:{}",msg);
SM4Utils sm4 = new SM4Utils();
sm4.secretKey = secretKey;
sm4.hexString = true;
msg = sm4.decryptData_ECB(msg);
log.info("解密后配置信息:{}", msg);
} catch (Exception e) {
log.error("配置信息解密失败,{}",e.getStackTrace());
}
return msg;
}
}
注意:
① 是加密器在spring IOC 中注册的名称,在使用jasypt加密,解密配置文件时,会需要指定加密器的名称
② 这里通过获取虚拟机选项的值,来动态获取启动时指定的secretKey
2.1.2 在配置文件中指定自定义加密器
jasypt:
encryptor:
bean: sM4ECBStringEncryptor
2.1.3 在启动命令中配置JVM参数
java -DsecretKey=64EC7C763AB7BF64E2D75FF83A319918 -jar xxx.jar
2.2 自定义加密器测试
2.2.1 使用SM4算法加密工具类生成密文
public static void main(String[] args) throws IOException {
String txt = "root";
SM4Utils sm43 = new SM4Utils();
sm4.secretKey = "64EC7C763AB7BF64E2D75FF83A319918";
sm4.hexString = true;
String secText = sm4.encryptData_ECB(txt);
System.out.println("->" + secText + "<-");
}
执行结果:
22ffdacdb8c5d1665aa0024b3b69cecb
2.2.2 在启动参数中配置JVM参数
-DsecretKey=64EC7C763AB7BF64E2D75FF83A319918
2.2.3 修改配置文件中需要加密的信息
datasource:
master:
url: jdbc:mysql://127.0.0.1:3306/jeecg-boot?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
username: root
password: ENC(dfgsfgdsfb8c5d166dfg553b6945c5)
driver-class-name: com.mysql.cj.jdbc.Driver
注意:需要将生成的密钥串用ENC()括起来,这样Jasypt就会知道这个配置是密文加密的,需要进行解密。
2.2.4 启动项目,解密日志打印正常则成功
2022-09-08 14:55:45.127 [main] INFO c.u.j.encryptor.DefaultLazyEncryptor:30 – Found Custom Encryptor Bean org.jeecg.config.jasypt.encryption.sm4.SM4ECBStringEncryptor@288728e with name: sM4ECBStringEncryptor
2022-09-08 14:55:45.129 [main] INFO o.j.c.jasypt.encryption.sm4.SM4ECBStringEncryptor:45 – 解密前配置信息:dfgsfgdsfb8c5d166dfg553b6945c5
2022-09-08 14:55:45.136 [main] INFO o.j.c.jasypt.encryption.sm4.SM4ECBStringEncryptor:50 – 解密后配置信息:root
2.3 SM4加密工具代码
2.3.1 SM4 类
package org.jeecg.common.util.security.sm4;
import org.jeecg.common.util.Util;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
public class SM4 {
public static final int SM4_ENCRYPT = 1;
public static final int SM4_DECRYPT = 0;
private long GET_ULONG_BE(byte[] b, int i) {
long n = (long) (b[i] & 0xff) << 24 | (long) ((b[i + 1] & 0xff) << 16) | (long) ((b[i + 2] & 0xff) << 8) | (long) (b[i + 3] & 0xff) & 0xffffffffL;
return n;
}
private void PUT_ULONG_BE(long n, byte[] b, int i) {
b[i] = (byte) (int) (0xFF & n >> 24);
b[i + 1] = (byte) (int) (0xFF & n >> 16);
b[i + 2] = (byte) (int) (0xFF & n >> 8);
b[i + 3] = (byte) (int) (0xFF & n);
}
private long SHL(long x, int n) {
return (x & 0xFFFFFFFF) << n;
}
private long ROTL(long x, int n) {
return SHL(x, n) | x >> (32 – n);
}
private void SWAP(long[] sk, int i) {
long t = sk[i];
sk[i] = sk[(31 – i)];
sk[(31 – i)] = t;
}
public static final byte[] SboxTable = {(byte) 0xd6, (byte) 0x90, (byte) 0xe9, (byte) 0xfe,
(byte) 0xcc, (byte) 0xe1, 0x3d, (byte) 0xb7, 0x16, (byte) 0xb6,
0x14, (byte) 0xc2, 0x28, (byte) 0xfb, 0x2c, 0x05, 0x2b, 0x67,
(byte) 0x9a, 0x76, 0x2a, (byte) 0xbe, 0x04, (byte) 0xc3,
(byte) 0xaa, 0x44, 0x13, 0x26, 0x49, (byte) 0x86, 0x06,
(byte) 0x99, (byte) 0x9c, 0x42, 0x50, (byte) 0xf4, (byte) 0x91,
(byte) 0xef, (byte) 0x98, 0x7a, 0x33, 0x54, 0x0b, 0x43,
(byte) 0xed, (byte) 0xcf, (byte) 0xac, 0x62, (byte) 0xe4,
(byte) 0xb3, 0x1c, (byte) 0xa9, (byte) 0xc9, 0x08, (byte) 0xe8,
(byte) 0x95, (byte) 0x80, (byte) 0xdf, (byte) 0x94, (byte) 0xfa,
0x75, (byte) 0x8f, 0x3f, (byte) 0xa6, 0x47, 0x07, (byte) 0xa7,
(byte) 0xfc, (byte) 0xf3, 0x73, 0x17, (byte) 0xba, (byte) 0x83,
0x59, 0x3c, 0x19, (byte) 0xe6, (byte) 0x85, 0x4f, (byte) 0xa8,
0x68, 0x6b, (byte) 0x81, (byte) 0xb2, 0x71, 0x64, (byte) 0xda,
(byte) 0x8b, (byte) 0xf8, (byte) 0xeb, 0x0f, 0x4b, 0x70, 0x56,
(byte) 0x9d, 0x35, 0x1e, 0x24, 0x0e, 0x5e, 0x63, 0x58, (byte) 0xd1,
(byte) 0xa2, 0x25, 0x22, 0x7c, 0x3b, 0x01, 0x21, 0x78, (byte) 0x87,
(byte) 0xd4, 0x00, 0x46, 0x57, (byte) 0x9f, (byte) 0xd3, 0x27,
0x52, 0x4c, 0x36, 0x02, (byte) 0xe7, (byte) 0xa0, (byte) 0xc4,
(byte) 0xc8, (byte) 0x9e, (byte) 0xea, (byte) 0xbf, (byte) 0x8a,
(byte) 0xd2, 0x40, (byte) 0xc7, 0x38, (byte) 0xb5, (byte) 0xa3,
(byte) 0xf7, (byte) 0xf2, (byte) 0xce, (byte) 0xf9, 0x61, 0x15,
(byte) 0xa1, (byte) 0xe0, (byte) 0xae, 0x5d, (byte) 0xa4,
(byte) 0x9b, 0x34, 0x1a, 0x55, (byte) 0xad, (byte) 0x93, 0x32,
0x30, (byte) 0xf5, (byte) 0x8c, (byte) 0xb1, (byte) 0xe3, 0x1d,
(byte) 0xf6, (byte) 0xe2, 0x2e, (byte) 0x82, 0x66, (byte) 0xca,
0x60, (byte) 0xc0, 0x29, 0x23, (byte) 0xab, 0x0d, 0x53, 0x4e, 0x6f,
(byte) 0xd5, (byte) 0xdb, 0x37, 0x45, (byte) 0xde, (byte) 0xfd,
(byte) 0x8e, 0x2f, 0x03, (byte) 0xff, 0x6a, 0x72, 0x6d, 0x6c, 0x5b,
0x51, (byte) 0x8d, 0x1b, (byte) 0xaf, (byte) 0x92, (byte) 0xbb,
(byte) 0xdd, (byte) 0xbc, 0x7f, 0x11, (byte) 0xd9, 0x5c, 0x41,
0x1f, 0x10, 0x5a, (byte) 0xd8, 0x0a, (byte) 0xc1, 0x31,
(byte) 0x88, (byte) 0xa5, (byte) 0xcd, 0x7b, (byte) 0xbd, 0x2d,
0x74, (byte) 0xd0, 0x12, (byte) 0xb8, (byte) 0xe5, (byte) 0xb4,
(byte) 0xb0, (byte) 0x89, 0x69, (byte) 0x97, 0x4a, 0x0c,
(byte) 0x96, 0x77, 0x7e, 0x65, (byte) 0xb9, (byte) 0xf1, 0x09,
(byte) 0xc5, 0x6e, (byte) 0xc6, (byte) 0x84, 0x18, (byte) 0xf0,
0x7d, (byte) 0xec, 0x3a, (byte) 0xdc, 0x4d, 0x20, 0x79,
(byte) 0xee, 0x5f, 0x3e, (byte) 0xd7, (byte) 0xcb, 0x39, 0x48};
public static final int[] FK = {0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc};
public static final int[] CK = {0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269,
0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9,
0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249,
0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9,
0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229,
0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299,
0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209,
0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279};
private byte sm4Sbox(byte inch) {
int i = inch & 0xFF;
byte retVal = SboxTable[i];
return retVal;
}
private long sm4Lt(long ka) {
long bb = 0L;
long c = 0L;
byte[] a = new byte[4];
byte[] b = new byte[4];
PUT_ULONG_BE(ka, a, 0);
b[0] = sm4Sbox(a[0]);
b[1] = sm4Sbox(a[1]);
b[2] = sm4Sbox(a[2]);
b[3] = sm4Sbox(a[3]);
bb = GET_ULONG_BE(b, 0);
c = bb ^ ROTL(bb, 2) ^ ROTL(bb, 10) ^ ROTL(bb, 18) ^ ROTL(bb, 24);
return c;
}
private long sm4F(long x0, long x1, long x2, long x3, long rk) {
return x0 ^ sm4Lt(x1 ^ x2 ^ x3 ^ rk);
}
private long sm4CalciRK(long ka) {
long bb = 0L;
long rk = 0L;
byte[] a = new byte[4];
byte[] b = new byte[4];
PUT_ULONG_BE(ka, a, 0);
b[0] = sm4Sbox(a[0]);
b[1] = sm4Sbox(a[1]);
b[2] = sm4Sbox(a[2]);
b[3] = sm4Sbox(a[3]);
bb = GET_ULONG_BE(b, 0);
rk = bb ^ ROTL(bb, 13) ^ ROTL(bb, 23);
return rk;
}
private void sm4_setkey(long[] SK, byte[] key) {
long[] MK = new long[4];
long[] k = new long[36];
int i = 0;
MK[0] = GET_ULONG_BE(key, 0);
MK[1] = GET_ULONG_BE(key, 4);
MK[2] = GET_ULONG_BE(key, 8);
MK[3] = GET_ULONG_BE(key, 12);
k[0] = MK[0] ^ (long) FK[0];
k[1] = MK[1] ^ (long) FK[1];
k[2] = MK[2] ^ (long) FK[2];
k[3] = MK[3] ^ (long) FK[3];
for (; i < 32; i++) {
k[(i + 4)] = (k[i] ^ sm4CalciRK(k[(i + 1)] ^ k[(i + 2)] ^ k[(i + 3)] ^ (long) CK[i]));
SK[i] = k[(i + 4)];
}
}
private void sm4_one_round(long[] sk, byte[] input, byte[] output) {
int i = 0;
long[] ulbuf = new long[36];
ulbuf[0] = GET_ULONG_BE(input, 0);
ulbuf[1] = GET_ULONG_BE(input, 4);
ulbuf[2] = GET_ULONG_BE(input, 8);
ulbuf[3] = GET_ULONG_BE(input, 12);
while (i < 32) {
ulbuf[(i + 4)] = sm4F(ulbuf[i], ulbuf[(i + 1)], ulbuf[(i + 2)], ulbuf[(i + 3)], sk[i]);
i++;
}
PUT_ULONG_BE(ulbuf[35], output, 0);
PUT_ULONG_BE(ulbuf[34], output, 4);
PUT_ULONG_BE(ulbuf[33], output, 8);
PUT_ULONG_BE(ulbuf[32], output, 12);
}
//修改了填充模式,为模式
private byte[] padding(byte[] input, int mode) {
if (input == null) {
return null;
}
byte[] ret = (byte[]) null;
if (mode == SM4_ENCRYPT) {
//填充:hex必须是32的整数倍填充 ,填充的是80 00 00 00
int p = 16 – input.length % 16;
String inputHex = Util.byteToHex(input)+ "80";
StringBuffer stringBuffer =new StringBuffer(inputHex);
for (int i = 0; i <p-1 ; i++) {
stringBuffer.append("00");
}
ret= Util.hexToByte(stringBuffer.toString());
//ret = new byte[input.length + p];
} else {
/*int p = input[input.length – 1];
ret = new byte[input.length – p];
System.arraycopy(input, 0, ret, 0, input.length – p);*/
String inputHex =Util.byteToHex(input);
int i = inputHex.lastIndexOf("80");
String substring = inputHex.substring(0, i);
ret= Util.hexToByte(substring);
}
return ret;
}
public void sm4_setkey_enc(SM4_Context ctx, byte[] key) throws Exception {
if (ctx == null) {
throw new Exception("ctx is null!");
}
if (key == null || key.length != 16) {
throw new Exception("key error!");
}
ctx.mode = SM4_ENCRYPT;
sm4_setkey(ctx.sk, key);
}
public void sm4_setkey_dec(SM4_Context ctx, byte[] key) throws Exception {
if (ctx == null) {
throw new Exception("ctx is null!");
}
if (key == null || key.length != 16) {
throw new Exception("key error!");
}
int i = 0;
ctx.mode = SM4_DECRYPT;
sm4_setkey(ctx.sk, key);
for (i = 0; i < 16; i++) {
SWAP(ctx.sk, i);
}
}
public byte[] sm4_crypt_ecb(SM4_Context ctx, byte[] input) throws Exception {
if (input == null) {
throw new Exception("input is null!");
}
if ((ctx.isPadding) && (ctx.mode == SM4_ENCRYPT)) {
input = padding(input, SM4_ENCRYPT);
}
int length = input.length;
ByteArrayInputStream bins = new ByteArrayInputStream(input);
ByteArrayOutputStream bous = new ByteArrayOutputStream();
for (; length > 0; length -= 16) {
byte[] in = new byte[16];
byte[] out = new byte[16];
bins.read(in);
sm4_one_round(ctx.sk, in, out);
bous.write(out);
}
byte[] output = bous.toByteArray();
if (ctx.isPadding && ctx.mode == SM4_DECRYPT) {
output = padding(output, SM4_DECRYPT);
}
bins.close();
bous.close();
return output;
}
public byte[] sm4_crypt_cbc(SM4_Context ctx, byte[] iv, byte[] input) throws Exception {
if (iv == null || iv.length != 16) {
throw new Exception("iv error!");
}
if (input == null) {
throw new Exception("input is null!");
}
if (ctx.isPadding && ctx.mode == SM4_ENCRYPT) {
input = padding(input, SM4_ENCRYPT);
}
int i = 0;
int length = input.length;
ByteArrayInputStream bins = new ByteArrayInputStream(input);
ByteArrayOutputStream bous = new ByteArrayOutputStream();
if (ctx.mode == SM4_ENCRYPT) {
for (; length > 0; length -= 16) {
byte[] in = new byte[16];
byte[] out = new byte[16];
byte[] out1 = new byte[16];
bins.read(in);
for (i = 0; i < 16; i++) {
out[i] = ((byte) (in[i] ^ iv[i]));
}
sm4_one_round(ctx.sk, out, out1);
System.arraycopy(out1, 0, iv, 0, 16);
bous.write(out1);
}
} else {
byte[] temp = new byte[16];
for (; length > 0; length -= 16) {
byte[] in = new byte[16];
byte[] out = new byte[16];
byte[] out1 = new byte[16];
bins.read(in);
System.arraycopy(in, 0, temp, 0, 16);
sm4_one_round(ctx.sk, in, out);
for (i = 0; i < 16; i++) {
out1[i] = ((byte) (out[i] ^ iv[i]));
}
System.arraycopy(temp, 0, iv, 0, 16);
bous.write(out1);
}
}
byte[] output = bous.toByteArray();
if (ctx.isPadding && ctx.mode == SM4_DECRYPT) {
output = padding(output, SM4_DECRYPT);
}
bins.close();
bous.close();
return output;
}
}
2.3.3 SM4Utils工具类
package org.jeecg.common.util.security.sm4;
import org.apache.commons.codec.binary.Base64;
import org.jeecg.common.util.Util;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class SM4Utils {
public String secretKey = "";
public String iv = "";
public boolean hexString = false;
public SM4Utils() {
}
/**
* SM4以ECB模式加密数据
* @param plainText
* @return
*/
public String encryptData_ECB(String plainText) {
try {
SM4_Context ctx = new SM4_Context();
ctx.isPadding = true;
ctx.mode = SM4.SM4_ENCRYPT;
byte[] keyBytes;
if (hexString) {
keyBytes = Util.hexStringToBytes(secretKey);
} else {
//keyBytes = secretKey.getBytes();
keyBytes = Util.hexStringToBytes(secretKey);
}
SM4 sm4 = new SM4();
sm4.sm4_setkey_enc(ctx, keyBytes);
byte[] encrypted = sm4.sm4_crypt_ecb(ctx, plainText.getBytes("UTF-8"));
return Util.byteToHex(encrypted);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* SM4 以ECB模式解密数据
* @param cipherText
* @return
*/
public String decryptData_ECB(String cipherText) {
try {
byte[] encrypted = Util.hexToByte(cipherText);
cipherText= Base64.encodeBase64String(encrypted);;
//cipherText = new BASE64Encoder().encode(encrypted);
if (cipherText != null && cipherText.trim().length() > 0) {
Pattern p = Pattern.compile("\\s*|\t|\r|\n");
Matcher m = p.matcher(cipherText);
cipherText = m.replaceAll("");
}
SM4_Context ctx = new SM4_Context();
ctx.isPadding = true;
ctx.mode = SM4.SM4_DECRYPT;
byte[] keyBytes;
if (hexString) {
keyBytes = Util.hexStringToBytes(secretKey);
} else {
keyBytes = secretKey.getBytes();
}
SM4 sm4 = new SM4();
sm4.sm4_setkey_dec(ctx, keyBytes);
byte[] decrypted = sm4.sm4_crypt_ecb(ctx, Base64.decodeBase64(cipherText));
//byte[] decrypted = sm4.sm4_crypt_ecb(ctx, new BASE64Decoder().decodeBuffer(cipherText));
return new String(decrypted, "UTF-8");
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* SM4 以CBC模式加密数据
* @param plainText
* @return
*/
public String encryptData_CBC(String plainText) {
try {
SM4_Context ctx = new SM4_Context();
ctx.isPadding = true;
ctx.mode = SM4.SM4_ENCRYPT;
byte[] keyBytes;
byte[] ivBytes;
if (hexString) {
keyBytes = Util.hexStringToBytes(secretKey);
ivBytes = Util.hexStringToBytes(iv);
} else {
keyBytes = secretKey.getBytes();
ivBytes = iv.getBytes();
}
SM4 sm4 = new SM4();
sm4.sm4_setkey_enc(ctx, keyBytes);
byte[] encrypted = sm4.sm4_crypt_cbc(ctx, ivBytes, plainText.getBytes("UTF-8"));
return Util.byteToHex(encrypted);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* SM4 以CCB模式解密数据
* @param cipherText
* @return
*/
public String decryptData_CBC(String cipherText) {
try {
byte[] encrypted = Util.hexToByte(cipherText);
cipherText= Base64.encodeBase64String(encrypted);;
//cipherText = new BASE64Encoder().encode(encrypted);
if (cipherText != null && cipherText.trim().length() > 0) {
Pattern p = Pattern.compile("\\s*|\t|\r|\n");
Matcher m = p.matcher(cipherText);
cipherText = m.replaceAll("");
}
SM4_Context ctx = new SM4_Context();
ctx.isPadding = true;
ctx.mode = SM4.SM4_DECRYPT;
byte[] keyBytes;
byte[] ivBytes;
if (hexString) {
keyBytes = Util.hexStringToBytes(secretKey);
ivBytes = Util.hexStringToBytes(iv);
} else {
keyBytes = secretKey.getBytes();
ivBytes = iv.getBytes();
}
SM4 sm4 = new SM4();
sm4.sm4_setkey_dec(ctx, keyBytes);
//byte[] decrypted = sm4.sm4_crypt_cbc(ctx, ivBytes, new BASE64Decoder().decodeBuffer(cipherText));
byte[] decrypted = sm4.sm4_crypt_cbc(ctx, ivBytes, Base64.decodeBase64(cipherText));
/*String text = new String(decrypted, "UTF-8");
return text.substring(0,text.length()-1);*/
return new String(decrypted, "UTF-8");
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
3. jasypt 运行原理解析
3.1 Jasypt 如何工作
1)它注册了一个 Spring post processor,该处理器装饰 Spring Environment 中包含的所有 PropertySource 对象,因此它们是“加密感知”的,并检测何时按照 jasypt 的属性约定对属性进行加密。
package com.ulisesbocchio.jasyptspringboot.configuration;
import com.ulisesbocchio.jasyptspringboot.EncryptablePropertyFilter;
import com.ulisesbocchio.jasyptspringboot.EncryptablePropertyResolver;
import com.ulisesbocchio.jasyptspringboot.annotation.EncryptablePropertySource;
import com.ulisesbocchio.jasyptspringboot.annotation.EncryptablePropertySources;
import com.ulisesbocchio.jasyptspringboot.wrapper.EncryptableEnumerablePropertySourceWrapper;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.env.PropertySourceLoader;
import org.springframework.context.ApplicationContextException;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.env.CompositePropertySource;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
public class EncryptablePropertySourceBeanFactoryPostProcessor implements BeanFactoryPostProcessor, Ordered {
private static final Logger log = LoggerFactory.getLogger(EncryptablePropertySourceBeanFactoryPostProcessor.class);
private ConfigurableEnvironment env;
public EncryptablePropertySourceBeanFactoryPostProcessor(ConfigurableEnvironment env) {
this.env = env;
}
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
ResourceLoader ac = new DefaultResourceLoader();
MutablePropertySources propertySources = this.env.getPropertySources();
Stream<AnnotationAttributes> encryptablePropertySourcesMetadata = this.getEncryptablePropertySourcesMetadata(beanFactory);
EncryptablePropertyResolver propertyResolver = (EncryptablePropertyResolver)beanFactory.getBean("lazyEncryptablePropertyResolver", EncryptablePropertyResolver.class);
EncryptablePropertyFilter propertyFilter = (EncryptablePropertyFilter)beanFactory.getBean("lazyEncryptablePropertyFilter", EncryptablePropertyFilter.class);
List<PropertySourceLoader> loaders = this.initPropertyLoaders();
encryptablePropertySourcesMetadata.forEach((eps) -> {
this.loadEncryptablePropertySource(eps, this.env, ac, propertyResolver, propertyFilter, propertySources, loaders);
});
}
private List<PropertySourceLoader> initPropertyLoaders() {
return SpringFactoriesLoader.loadFactories(PropertySourceLoader.class, this.getClass().getClassLoader());
}
/**
* 加载加密属性源
* @param encryptablePropertySource
* @param env
* @param resourceLoader
* @param resolver 属性解析器
* @param propertyFilter 属性过滤器
* @param propertySources 属性源
* @param loaders
* @throws BeansException
*/
private void loadEncryptablePropertySource(AnnotationAttributes encryptablePropertySource, ConfigurableEnvironment env, ResourceLoader resourceLoader, EncryptablePropertyResolver resolver, EncryptablePropertyFilter propertyFilter, MutablePropertySources propertySources, List<PropertySourceLoader> loaders) throws BeansException {
try {
PropertySource ps = this.createPropertySource(encryptablePropertySource, env, resourceLoader, resolver, propertyFilter, loaders);
propertySources.addLast(ps);
log.info("Created Encryptable Property Source '{}' from locations: {}", ps.getName(), Arrays.asList(encryptablePropertySource.getStringArray("value")));
} catch (Exception var9) {
throw new ApplicationContextException("Exception Creating PropertySource", var9);
}
}
/**
* 包装PropertySource 为 EncryptableEnumerablePropertySourceWrapper
* @param attributes
* @param environment
* @param resourceLoader
* @param resolver
* @param propertyFilter
* @param loaders
* @return
* @throws Exception
*/
private PropertySource createPropertySource(AnnotationAttributes attributes, ConfigurableEnvironment environment, ResourceLoader resourceLoader, EncryptablePropertyResolver resolver, EncryptablePropertyFilter propertyFilter, List<PropertySourceLoader> loaders) throws Exception {
String name = this.generateName(attributes.getString("name"));
String[] locations = attributes.getStringArray("value");
boolean ignoreResourceNotFound = attributes.getBoolean("ignoreResourceNotFound");
CompositePropertySource compositePropertySource = new CompositePropertySource(name);
Assert.isTrue(locations.length > 0, "At least one @PropertySource(value) location is required");
String[] var11 = locations;
int var12 = locations.length;
for(int var13 = 0; var13 < var12; ++var13) {
String location = var11[var13];
String resolvedLocation = environment.resolveRequiredPlaceholders(location);
Resource resource = resourceLoader.getResource(resolvedLocation);
if (!resource.exists()) {
if (!ignoreResourceNotFound) {
throw new IllegalStateException(String.format("Encryptable Property Source '%s' from location: %s Not Found", name, resolvedLocation));
}
log.info("Ignoring NOT FOUND Encryptable Property Source '{}' from locations: {}", name, resolvedLocation);
} else {
String actualName = name + "#" + resolvedLocation;
this.loadPropertySource(loaders, resource, actualName).ifPresent((psources) -> {
psources.forEach(compositePropertySource::addPropertySource);
});
}
}
return new EncryptableEnumerablePropertySourceWrapper(compositePropertySource, resolver, propertyFilter);
}
private String generateName(String name) {
return !StringUtils.isEmpty(name) ? name : "EncryptedPropertySource#" + System.currentTimeMillis();
}
private Stream<AnnotationAttributes> getEncryptablePropertySourcesMetadata(ConfigurableListableBeanFactory beanFactory) {
Stream<AnnotationAttributes> source = this.getBeanDefinitionsForAnnotation(beanFactory, EncryptablePropertySource.class);
Stream<AnnotationAttributes> sources = this.getBeanDefinitionsForAnnotation(beanFactory, EncryptablePropertySources.class).flatMap((map) -> {
return Arrays.stream((AnnotationAttributes[])((AnnotationAttributes[])map.get("value")));
});
return Stream.concat(source, sources);
}
private Stream<AnnotationAttributes> getBeanDefinitionsForAnnotation(ConfigurableListableBeanFactory bf, Class<? extends Annotation> annotation) {
Stream var10000 = Arrays.stream(bf.getBeanNamesForAnnotation(annotation));
bf.getClass();
return var10000.map(bf::getBeanDefinition).filter((bd) -> {
return bd instanceof AnnotatedBeanDefinition;
}).map((bd) -> {
return (AnnotatedBeanDefinition)bd;
}).map(AnnotatedBeanDefinition::getMetadata).filter((md) -> {
return md.hasAnnotation(annotation.getName());
}).map((md) -> {
return (AnnotationAttributes)md.getAnnotationAttributes(annotation.getName());
});
}
private Optional<List<PropertySource<?>>> loadPropertySource(List<PropertySourceLoader> loaders, Resource resource, String sourceName) throws IOException {
return Optional.of(resource).filter(this::isFile).map((res) -> {
return (List)loaders.stream().filter((loader) -> {
return this.canLoadFileExtension(loader, resource);
}).findFirst().map((loader) -> {
return this.load(loader, sourceName, resource);
}).orElse((Object)null);
});
}
private List<PropertySource<?>> load(PropertySourceLoader loader, String sourceName, Resource resource) {
try {
return loader.load(sourceName, resource);
} catch (Throwable var5) {
throw var5;
}
}
private boolean canLoadFileExtension(PropertySourceLoader loader, Resource resource) {
return Arrays.stream(loader.getFileExtensions()).anyMatch((extension) -> {
return resource.getFilename().toLowerCase().endsWith("." + extension.toLowerCase());
});
}
private boolean isFile(Resource resource) {
return resource != null && resource.exists() && StringUtils.hasText(StringUtils.getFilenameExtension(resource.getFilename()));
}
public int getOrder() {
return 2147483647;
}
}
2)它定义了一个默认的 StringEncryptor,可以通过常规属性、系统属性或命令行参数进行配置。
package com.ulisesbocchio.jasyptspringboot.encryptor;
import com.ulisesbocchio.jasyptspringboot.util.Functional;
import com.ulisesbocchio.jasyptspringboot.util.Singleton;
import java.util.Optional;
import org.jasypt.encryption.StringEncryptor;
import org.jasypt.encryption.pbe.PooledPBEStringEncryptor;
import org.jasypt.encryption.pbe.config.SimpleStringPBEConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.core.env.Environment;
public class DefaultLazyEncryptor implements StringEncryptor {
private static final Logger log = LoggerFactory.getLogger(DefaultLazyEncryptor.class);
private final Singleton<StringEncryptor> singleton;
public DefaultLazyEncryptor(Environment e, String customEncryptorBeanName, BeanFactory bf) {
this.singleton = new Singleton(() -> {
Optional var10000 = Optional.of(customEncryptorBeanName);
bf.getClass();
return (StringEncryptor)var10000.filter(bf::containsBean).map((name) -> {
return (StringEncryptor)bf.getBean(name);
}).map(Functional.tap((bean) -> {
log.info("Found Custom Encryptor Bean {} with name: {}", bean, customEncryptorBeanName);
})).orElseGet(() -> {
log.info("String Encryptor custom Bean not found with name '{}'. Initializing Default String Encryptor", customEncryptorBeanName);
return this.createDefault(e);
});
});
}
public DefaultLazyEncryptor(Environment e) {
this.singleton = new Singleton(() -> {
return this.createDefault(e);
});
}
private StringEncryptor createDefault(Environment e) {
PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
SimpleStringPBEConfig config = new SimpleStringPBEConfig();
config.setPassword(getRequiredProperty(e, "jasypt.encryptor.password"));
config.setAlgorithm(getProperty(e, "jasypt.encryptor.algorithm", "PBEWithMD5AndDES"));
config.setKeyObtentionIterations(getProperty(e, "jasypt.encryptor.keyObtentionIterations", "1000"));
config.setPoolSize(getProperty(e, "jasypt.encryptor.poolSize", "1"));
config.setProviderName(getProperty(e, "jasypt.encryptor.providerName", (String)null));
config.setProviderClassName(getProperty(e, "jasypt.encryptor.providerClassName", (String)null));
config.setSaltGeneratorClassName(getProperty(e, "jasypt.encryptor.saltGeneratorClassname", "org.jasypt.salt.RandomSaltGenerator"));
config.setStringOutputType(getProperty(e, "jasypt.encryptor.stringOutputType", "base64"));
encryptor.setConfig(config);
return encryptor;
}
private static String getProperty(Environment environment, String key, String defaultValue) {
if (!propertyExists(environment, key)) {
log.info("Encryptor config not found for property {}, using default value: {}", key, defaultValue);
}
return environment.getProperty(key, defaultValue);
}
private static boolean propertyExists(Environment environment, String key) {
return environment.getProperty(key) != null;
}
private static String getRequiredProperty(Environment environment, String key) {
if (!propertyExists(environment, key)) {
throw new IllegalStateException(String.format("Required Encryption configuration property missing: %s", key));
} else {
return environment.getProperty(key);
}
}
public String encrypt(String message) {
return ((StringEncryptor)this.singleton.get()).encrypt(message);
}
public String decrypt(String encryptedMessage) {
return ((StringEncryptor)this.singleton.get()).decrypt(encryptedMessage);
}
}
————————————————
版权声明:本文为CSDN博主「独行客-编码爱好者」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/donkeyboy001/article/details/124400281