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

image.png

 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