Configuration file information encryption and decryption scheme (jasypt)

jasypt encryption and decryption

reference:

overview

  • The full name of Jasypt is Java Simplified Encryption, which is an open source project on Sourceforge.net.

  • Jasypt can be used for encryption tasks and applications such as encrypting passwords, sensitive information and data communications, including high-security, standards-based cryptography, encrypting passwords, text, numbers and binary files with simultaneous one-way and two-way encryption.

  • Jasypt also complies with the RSA standard for password-based encryption and provides configuration-free encryption tools as well as new, highly configurable standard encryption tools, encrypted properties files, Spring work integration, encrypted Hibernate datasource configuration, new command-line tools, Apache wicket integration for URL encryption, and upgraded documentation.

  • Jasypt can also be integrated with Acegi Security, namely Spring Security. Jasypt also has the integration function of encryption application configuration, and provides an open API so that any Java Cryptography Extension can use Jasypt

  • For security reasons, sensitive information in Spring boot configuration files usually needs to be encrypted/desensitized, and plaintext should not be used as much as possible.

    The open source security framework Jasypt Spring Boot provides encryption support for the property source in the Spring Boot application. It is specially used to handle Spring boot property encryption. Use a specific format to directly configure the ciphertext in the configuration file, and then when the application starts, Jasypt will automatically decrypt the password into plaintext for use by the program.

    • Jasypt encryption attribute configuration format: secret.property=ENC(nrmZtkF7T0kjG/VodDvBw93Ct8EgjCA+), ENC() is its identification, when the program starts, it will automatically decrypt the content, if the decryption fails, it will report an error.
    • Therefore, obtaining these property values ​​is no different from usual, you can directly use @Value(“${secret.property}”) to obtain them, and no special processing is required to obtain the values.
  • Jasypt encrypts the same content with the same key (secretKey), and the ciphertext generated each time is different, but it is possible to decrypt the original content according to these ciphertexts.


jasypt integration method (including dependencies)

There are three ways to integrate jasypt-spring-boot in the project:

  • Method 1:

    If it is a Spring Boot application, using the annotation @SpringBootApplication or @EnableAutoConfiguration, then just add the jasypt-spring-boot-starter dependency, and the entire Spring environment will support encrypted property configuration (this means that any system property, environment property, command line parameter, yaml, properties and any other custom property source can contain encrypted properties)

    <dependency>
            <groupId>com.github.ulisesbocchio</groupId>
            <artifactId>jasypt-spring-boot-starter</artifactId>
            <version>3.0.3</version>
    </dependency>
    
  • Method 2:

    If not using @SpringBootApplication or @EnableAutoConfiguration, add jasypt-spring-boot to the classpath

    1) depend on

    <dependency>
            <groupId>com.github.ulisesbocchio</groupId>
            <artifactId>jasypt-spring-boot</artifactId>
            <version>3.0.3</version>
    </dependency>
    

    2) Add @EnableEncryptableProperties to the configuration class to enable encryptable properties across the entire Spring environment:

    @Configuration
    @EnableEncryptableProperties
    public class MyApplication {
          
          
        ...
    }
    
  • Method 3:

    If you don't use @SpringBootApplication or @EnableAutoConfiguration automatic configuration annotations, and you don't want to enable encryptable properties in the entire Spring environment, you can use this method

    1) Add dependencies to the project

    <dependency>
            <groupId>com.github.ulisesbocchio</groupId>
            <artifactId>jasypt-spring-boot</artifactId>
            <version>3.0.3</version>
    </dependency>
    

    2) Add any number of @EncryptablePropertySource annotations to the configuration file (just like using Spring's @PropertySource annotation)

    @Configuration
    @EncryptablePropertySource(name = "EncryptedProperties", value = "classpath:encrypted.properties")
    public class MyApplication {
          
          
        ...
    }
    

    2) Or you can also use the @EncryptablePropertySources annotation to group annotations of the @EncryptablePropertySource type

    @Configuration
    @EncryptablePropertySources({
          
          @EncryptablePropertySource("classpath:encrypted.properties"),
                                 @EncryptablePropertySource("classpath:encrypted2.properties")})
    public class MyApplication {
          
          
        ...
    }
    

    Note: @EncryptablePropertySource supports YAML files since version 1.8


yml configuration and parameter description

yml configuration

# jasypt 配置加密
jasypt:
  encryptor:
    # 自定义加密盐值(密钥)
    password: jasypt
    # 加密算法设置
    algorithm: PBEWithMD5AndDES
    iv-generator-classname: org.jasypt.iv.RandomIvGenerator
    salt-generator-classname: org.jasypt.salt.RandomSaltGenerator

Parameter Description

Key Required Default Value illustrate
jasypt.encryptor. password True - Custom encrypted salt value (key)
jasypt.encryptor.algorithm False PBEWITHHMACSHA512ANDAES_256 Encryption algorithm, must be supported by the JCE provider
jasypt.encryptor.key-obtention-iterations False 1000 Get the number of hash iterations for the encryption key
jasypt.encryptor.pool-size False 1 The size of the encryptor pool to create
jasypt.encryptor.provider-name False SunJCE The name of the security provider requesting the encryption algorithm
jasypt.encryptor.provider-class-name False null
jasypt.encryptor.iv-generator-classname False org.jasypt.iv.RandomIvGenerator IV generator
jasypt.encryptor.salt-generator-classname False org.jasypt.salt.RandomSaltGenerator Sal Generator
jasypt.encryptor.string-output-type False base64
The available encoding types for the encoding form of string output are:
base64, hexadecimal (hexadecimal)
jasypt.encryptor.proxy-property-sources False false
jasypt.encryptor.skip-property-sources False empty list
jasypt.encryptor.property.prefix False ENC( Set ciphertext prefix
jasypt.encryptor.property.suffix False ) Set ciphertext suffix

Note:

  • The only required property is jasypt.encryptor.password, the rest can use default values.

    While all of these properties can be declared in a properties file, for security the password property is officially not recommended to be stored in a properties file, but should be passed as a system property, command line argument, or environment variable.

  • The default encryption and decryption algorithm on the official website is "PBEWITHHMACSHA512ANDAES_256", which is sha512 plus AES advanced encryption, which requires Java JDK 1.9 and above support, or the JCE unlimited-strength permission policy file, otherwise an error will be reported when running: "Encryption caused an exception. One possible reason is that you are using a strong encryption algorithm, and you have not installed the Java Encryption Extension (JCE) unlimited-strength permission policy file in this Java virtual machine."

  • By default, com.ulisesbocchio.jasyptspringboot.encryptor.DefaultLazyEncryptor is used for encryption and decryption

  • All properties of the standard StringEncryptor can be configured in the global configuration file. It is also possible to add a StringEncryptor bean in the background, at which point the default encryption procedure will be ignored.

    import org.jasypt.encryption.StringEncryptor;
    import org.jasypt.encryption.pbe.PooledPBEStringEncryptor;
    import org.jasypt.encryption.pbe.config.SimpleStringPBEConfig;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class JasyptConfig {
          
          
        /**
         * 自定义 StringEncryptor,覆盖默认的 StringEncryptor
         * bean 名称是必需的,从 1.5 版开始按名称检测自定义字符串加密程序,默认 bean 名称为:jasyptStringEncryptor
         */
        @Bean("jasyptStringEncryptor")
        public StringEncryptor jasyptStringEncryptor() {
          
          
            PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
            SimpleStringPBEConfig config = new SimpleStringPBEConfig();
            config.setPassword("jasypt");
            config.setAlgorithm("PBEWithMD5AndDES");
            config.setKeyObtentionIterations("1000");
            config.setPoolSize("1");
            config.setProviderName("SunJCE");
            config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator");
            config.setIvGeneratorClassName("org.jasypt.iv.RandomIvGenerator");
            config.setStringOutputType("base64");
            encryptor.setConfig(config);
            return encryptor;
        }
    }
    

    Notice:

    • The bean name is required because jasypt spring boot detects custom string encryptors by name since version 1.5, the default bean name is: jasyptStringEncryptor

    • But it can also be overridden by defining a property, e.g. jasypt.encryptor.bean=encryptorBean, and then define a custom encryptor with that name:

      @Bean("encryptorBean") 
      public StringEncryptor stringEncryptor() {
              
               ... }
      

Encryption and decryption tools

To obtain the ciphertext, you need to encrypt the data that needs to be encrypted. There are various methods. You can use the command line to operate the jar package to obtain it, or you can directly use the code to encrypt it.

It is recommended to use code encryption. The following provides a tool class for encryption. Note:

  • Jasypt uses the StringEncryptor decryption attribute by default, so it must also use StringEncryptor to encrypt by default when encrypting, otherwise the decryption fails and an error is reported at startup

    StringEncryptor interface has many implementation classes, commonly used PooledPBEStringEncryptor

  • Encryption and decryption must have the same properties set for StringEncryptor. For example, the algorithm used for encryption must be the same for decryption. Otherwise, an error will be reported when decryption fails at startup.

  • The commonly used encryption algorithm is "PBEWithMD5AndDES", the default on the official website is "PBEWITHHMACSHA512ANDAES_256", the former is md5 plus des standard encryption, the latter is sha512 plus AES advanced encryption, but Java JDK 1.9 and above support is required, or JCE unlimited strength permission policy files are added.

import org.jasypt.encryption.pbe.PooledPBEStringEncryptor;
import org.jasypt.encryption.pbe.config.SimpleStringPBEConfig;

public class JasyptUtils {
    
    

    /**
     * 加密
     * @param password 加密盐值
     * @param text    需要加密的字符串
     * @return 加密后的字符串
     */
    public static String encrypt(String password, String text) {
    
    
        PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
        encryptor.setConfig(cryptor(password));
        return encryptor.encrypt(text);
    }

    /**
     * 解密
     * @param password 加密盐值
     * @param text    需要解密的字符串
     * @return 解密后的字符串
     */
    public static String decrypt(String password, String text) {
    
    
        PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
        encryptor.setConfig(cryptor(password));
        return encryptor.decrypt(text);
    }

    /**
     * 配置(对应yml中的配置)
     * @param password 加密盐值
     * @return SimpleStringPBEConfig
     */
    public static SimpleStringPBEConfig cryptor(String password) {
    
    
        SimpleStringPBEConfig config = new SimpleStringPBEConfig();
        //设置盐值
        config.setPassword(password);
        //设置算法配置信息
        config.setAlgorithm("PBEWithMD5AndDES");
        config.setKeyObtentionIterations("1000");
        config.setProviderName("SunJCE");
        config.setIvGeneratorClassName("org.jasypt.iv.RandomIvGenerator");
        config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator");
        config.setStringOutputType("base64");
        config.setPoolSize("1");
        return config;
    }


    public static void main(String[] args) {
    
    
        // 加密
        String encryptStr = encrypt("jasypt", "root");
        // 解密
        String decryptStr = decrypt("jasypt", encryptStr);
        System.out.println("加密后:" + encryptStr);
        System.out.println("解密后:" + decryptStr);
    }
}

basic use

The configuration in the global configuration file is as follows, the property jasypt.encryptor.password must be set, and the algorithm algorithm must be consistent with the algorithm used for encryption.

If you want to encrypt which attribute, use ENC() to wrap it, and then put the ciphertext in it, and it will be decrypted automatically when the application starts.

**The format of the ciphertext in the yml configuration file: **ENC (ciphertext)

Example:

username: ENC(vyxdS47pJdWBF38TFdmjKmMm4zEO0FQP)
password: ENC(vyxdS47pJdWBF38TFdmjKmMm4zEO0FQP)

expand:

  • In the SpringBoot project (with normal use @SpringBootApplicationor @EnableAutoConfigurationannotations), annotations can be used in the code @Valueto directly obtain the decrypted configuration values

  • Jasypt uses StringEncryptor to decrypt attributes by default, so it has been placed in the Spring container by default and can be used directly. For example, in addition to encrypting and decrypting attributes in the configuration file, it can also perform any other encryption and decryption operations, such as providing a Controller interface for online encryption and decryption.

    Because the browser address bar is sensitive to special characters, the default base64 is not used, but a hexadecimal string is used instead

    jasypt:
      encryptor:
        password: wangmaox  # 加密的密钥,自定义即可,必填项
        algorithm: PBEWithMD5AndDES  # 指定解密算法
        string-output-type: hexadecimal # 设置加密内容输出的编码形式,可用的编码类型有 base64、hexadecimal(16进制)
    

    Where you want to use StringEncryptor, you can directly get it and use it

        @Resource
        private StringEncryptor stringEncryptor;
     
        /**
         * http://localhost:8080/jasypt/encryptor?message=12日下午17点执行任务&isEncrypt=true
         * http://localhost:8080/jasypt/encryptor?message=702EAA3755766C567F62E83273681A90DC684B6AFADD5CD84691778DAF4A1466E13CE0720E8BABC06081A5D6DBD90EA1&isEncrypt=false
         * 在线使用 {@link StringEncryptor} 加解密消息。
         *
         * @param message   加/解密的内容
         * @param isEncrypt true 表示加密、false 表示解密
         */
        @GetMapping("jasypt/encryptor")
        public ObjectNode encrypt(@RequestParam String message, @RequestParam boolean isEncrypt) {
          
          
            JsonNodeFactory nodeFactory = JsonNodeFactory.instance;
            String encrypt = isEncrypt ? stringEncryptor.encrypt(message) : stringEncryptor.decrypt(message);
            ObjectNode objectNode = nodeFactory.objectNode();
            objectNode.put("code", 200);
            objectNode.put("data", encrypt);
            return objectNode;
        }
    

Key (salt value) storage description

  • The encryption and decryption process itself is processed through the salt value, so under normal circumstances the salt value and the encrypted string are stored separately.

  • Salt values ​​should be used in system properties, command line or environment variables, not in configuration files.

    Key delivery method :

    # 方式1:启动参数
    java -jar jasypt-spring-boot-demo.jar --jasypt.encryptor.password=password
    
    # 方式2:系统属性
    java -Djasypt.encryptor.password=password -jar jasypt-spring-boot-demo.jar
    
    # 方式3:环境变量
    jasypt:
        encryptor:
            password: ${JASYPT_ENCRYPTOR_PASSWORD:password}
    # 也可以先设置环境变量
    export JASYPT_ENCRYPTOR_PASSWORD=password
    java -jar jasypt-spring-boot-demo.jar
    

    After removing the salt value set in the configuration file:

    • Add in the startup configuration item "VM options" of idea: Djasypt.encryptor.password=salt value.
    • Start method after packaging: java -jar -Djasypt.encryptor.password=salt value xxx.jar

Use jar package method - encryption

 @echo off
 set/p input=待加密的明文字符串:
 set/p password=加密密钥(盐值)echo 加密中......
 java -cp jasypt-1.9.2.jar org.jasypt.intf.cli.JasyptPBEStringEncryptionCLI  input=%input% password=%password% algorithm=PBEWithMD5AndDES
 pause

Use the jar package method - decrypt

java -cp jasypt-1.9.3.jar org.jasypt.intf.cli.JasyptPBEStringDecryptionCLI password=123456 algorithm=PBEWithMD5AndDES ivGeneratorClassName=org.jasypt.iv.RandomIvGenerator input=BwNPdUi+syCTKFj/nlbI5fAtGUKuhN8r

finder, parser

EncryptablePropertyDetector: finder

This interface provides two methods:

  • isEncrypted method: determine whether it is an attribute encrypted by jasypt convention rules
  • unwrapEncryptedValue method: ciphertext preprocessing. You can customize the return of the real encrypted value with the prefix and suffix removed

The default implementation of this interface is DefaultPropertyDetector


EncryptablePropertyResolver: Resolver

Only one method is provided in this interface:

  • resolvePropertyValue method: Traverse configuration file properties, determine whether they are encrypted properties, and then decrypt and return plaintext.

    In the default implementation of DefaultPropertyResolver, relying on EncryptablePropertyDetector and StringEncryptor, the real decryption method is written in StringEncryptor

The default implementation of this interface is DefaultPropertyResolver

Custom decomposer (generally not customized, here is just an example)

public class JasyptEncryptablePropertyResolver implements EncryptablePropertyResolver {
    
    
    //自定义解密方法
    @Override
    public String resolvePropertyValue(String s) {
    
    
        if (null != s && s.startsWith(MyEncryptablePropertyDetector.ENCODED_PASSWORD_HINT)) {
    
    
            return PasswordUtil.decode(s.substring(MyEncryptablePropertyDetector.ENCODED_PASSWORD_HINT.length()));
        }
        return s;
    }
}

custom encryption algorithm

If you do not want to use the encryption algorithm in the jasypt tool, or require a specific encryption algorithm internally, the jasypt-spring-boot component provides a way to implement custom encryption and decryption.

You can implement the EncryptablePropertyDetector and EncryptablePropertyResolver interfaces by yourself, and hand them over to Spring for management, set the bean names as encryptedPropertyDetector and encryptedPropertyResolver to override the default implementation provided by the framework, and complete the customization of encryption algorithms and prefixes and suffixes.

Specify the custom encryption and decryption implementation class in the yml file to inject the bean name into the Spring container

#数据库配置文件加密
jasypt:
  encryptor:
    ## 实现jasypt加密解密的类
    bean: customJasyptStringEncryptor

Custom encryption and decryption implementation class

import com.test.ssmtest.encryption.utils.AESUtils;
import com.test.ssmtest.encryption.utils.Sm4Utils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jasypt.encryption.StringEncryptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;

/**
 * 自定义 jasypt 加解密实现类
 */
@Slf4j
public class JasyptCustomStringEncryptor implements StringEncryptor {
    
    

    @Autowired
    private AESUtils aesUtils;
    @Autowired
    private Sm4Utils sm4Utils;

    @Value("${jasypt.encryption.encryptedMethod:aes}")
    private String encryptedMethod;

    @Override
    public String encrypt(String s) {
    
    
        return s;
    }

    @Override
    public String decrypt(String s) {
    
    
        log.info("get encrypted text:" + s);
        String encryptedText = null;
        if (StringUtils.isNotBlank(s)){
    
    
            try {
    
    
                String encodedPrefix = s.substring(0, s.indexOf("("));
                if (JasyptEncryptableDetector.ENCODED_HINT_ENC.equalsIgnoreCase(encodedPrefix)){
    
    
                    encodedPrefix = encryptedMethod;
                }

                String ciphertext = s.substring(s.indexOf("(") + 1, s.lastIndexOf(")"));
                if (AESUtils.AES.equalsIgnoreCase(encodedPrefix)){
    
    
                    encryptedText = aesUtils.decryptAESAndDecode(ciphertext);
                } else if (Sm4Utils.SM4.equalsIgnoreCase(encodedPrefix)){
    
    
                    encryptedText = sm4Utils.decryptSM4AndDecode(ciphertext);
                }

                log.info("generate decrypted text:"+ encryptedText);
                if (StringUtils.isNotBlank(encryptedText)){
    
    
                    log.info("decrypt text success!");
                } else {
    
    
                    log.error("decrypt text failed!");
                }
            } catch (Exception e) {
    
    
                log.error("decrypt text error!", e);
            }
        }
        return encryptedText;
    }
}

custom finder

In order to support custom encrypted property prefixes, you need to implement the EncryptablePropertyDetector interface yourself

import com.ulisesbocchio.jasyptspringboot.EncryptablePropertyDetector;
import java.util.Arrays;

/**
 * 自定义 jasypt 发现器实现类
 */
public class JasyptEncryptableDetector implements EncryptablePropertyDetector {
    
    

    private static final String[] ENCODED_HINTS = {
    
    "ENC", "AES", "SM4"};
    public static final String ENCODED_HINT_ENC = "ENC";

   // 判断是否是 jasypt 约定规则加密的属性
    @Override
    public boolean isEncrypted(String s) {
    
    
        if (null != s && s.contains("(") && s.contains(")")) {
    
    
            s = s.trim().toUpperCase();
            return Arrays.asList(ENCODED_HINTS).contains(s.substring(0, s.indexOf("(")));
        }
        return false;
    }
    // 密文预处理。可以自定义返回去除掉前缀和后缀的真正加密的值。
    // 因解密方法需要加密提示判断加密的算法,此处不去除前缀和后缀
    @Override
    public String unwrapEncryptedValue(String s) {
    
    
        return s.trim();
    }
}

register bean

import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
// 配置添加该注解,开启属性自动解密功能。若用jasypt-spring-boot-starter依赖包,可以不用配置该注解
// @EnableEncryptableProperties
public class JasyptConfig {
    
    

    @Bean
    @ConditionalOnProperty(name = "jasypt.encryptor.bean", havingValue = "jasyptCustomStringEncryptor")
    public JasyptCustomStringEncryptor jasyptCustomStringEncryptor(){
    
    
        return new JasyptCustomStringEncryptor();
    }

    @Bean("encryptablePropertyDetector")
    @ConditionalOnProperty(name = "jasypt.encryptor.bean", havingValue = "jasyptCustomStringEncryptor")
    public JasyptEncryptableDetector jasyptEncryptableDetector(){
    
    
        return new JasyptEncryptableDetector();
}

Guess you like

Origin blog.csdn.net/footless_bird/article/details/128287795