jasypt配置敏感信息加密解密及其原理

简介

配置文件中一些敏感信息通常需要加密,例如数据库密码等信息。

如何方便快捷的对这些配置加密呢?

比较常见的方式是使用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

【】
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加密配置文件属性,要找到对应的加密解密方法,可以按以下步骤处理:

  1. 查找名字为jasyptStringEncryptor实现了StringEncryptor类的实例的decrypt方法
  2. 查找名字为encryptablePropertyResolver实现了EncryptablePropertyResolver类的实例的resolvePropertyValue方法
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值