Talk about how to encrypt the password of the springboot project database

foreword

In our daily development, we may freely expose the database password in plain text in the configuration file. This can be done in the development environment, but it is not recommended in the production environment. After all, security is no trivial matter, and no one knows where The password was leaked out of nowhere. Today, let’s talk about how to [encrypt the database password in the springboot project]

text

Scheme 1. Use the [druid] database connection pool to encrypt the database password

  1. Import the [druid] package into pom.xml

In order to facilitate other operations, the starter of druid is directly introduced here.

<dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>${druid.version}</version>
        </dependency>

  1. Use com.alibaba.druid.filter.config.ConfigTools to generate public and private keys

ps: There are two ways to generate, one is generated using the command line, and the other is generated directly by writing a tool class. The example in this article is directly generated by the tool class

The tool class code is as follows

/**
 * alibaba druid encryption and decryption rules:
 * Plaintext password + private key (privateKey) encryption = encrypted password
 * Encrypted password + public key (publicKey) decryption = plaintext password
 */
public final class DruidEncryptorUtils {

    private static String privateKey;

    private static String publicKey;

    static {
        try {
            String[] keyPair = ConfigTools.genKeyPair(512);
            privateKey = keyPair[0];
            System.out.println(String.format("privateKey-->%s",privateKey));
            publicKey = keyPair[1];
            System.out.println(String.format("publicKey-->%s",publicKey));
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (NoSuchProviderException e) {
            e.printStackTrace();
        }
    }

    /**
     * Plaintext encryption
     * @param plaintext
     * @return
     */
    @SneakyThrows
    public static String encode(String plaintext){System.out 
        .println ( "Plaintext string: " + plaintext);
        String ciphertext = ConfigTools.encrypt(privateKey,plaintext);System.out 
        .println ( "Encrypted string: " + ciphertext);
         return ciphertext;
    }

    /**
     * decrypt
     * @param ciphertext
     * @return
     */
    @SneakyThrows
    public static String decode(String ciphertext){System.out 
        .println ( "Encrypted string: " + ciphertext);
        String plaintext = ConfigTools.decrypt(publicKey,ciphertext);System.out 
        .println ( "Decrypted string: " + plaintext);

        return plaintext;
    }

  1. Modify the content information of the configuration file of the database

a. Change the password

Replace the password with the password generated by the tool class DruidEncryptorUtils

password: ${DATASOURCE_PWD:HB5FmUeAI1U81YJrT/T6awImFg1/Az5o8imy765WkVJouOubC2H80jqmZrr8L9zWKuzS/8aGzuQ4YySAkhywnA==}

b, filter open config

filter:
                config:
                    enabled: true

c. Configure the connectionProperties property

connection-properties: config.decrypt=true;config.decrypt.key=${spring.datasource.publickey}

ps: spring.datasource.publickey The public key generated for the tool class

Appendix: Complete Database Configuration

spring:
    datasource:
        type: com.alibaba.druid.pool.DruidDataSource
        driverClassName: com.mysql.cj.jdbc.Driver
        url: ${DATASOURCE_URL:jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai}
        username: ${DATASOURCE_USERNAME:root}
        password: ${DATASOURCE_PWD:HB5FmUeAI1U81YJrT/T6awImFg1/Az5o8imy765WkVJouOubC2H80jqmZrr8L9zWKuzS/8aGzuQ4YySAkhywnA==}
        publickey: MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAIvP9xF4RCM4oFiu47NZY15iqNOAB9K2Ml9fiTLa05CWaXK7uFwBImR7xltZM1frl6ahWAXJB6a/FSjtJkTZUJECAwEAAQ==
        druid:
            initialSize: 5
            # The minimum number of connection pools 
            minIdle:  10 
            # The maximum number of connection pools 
            maxActive:  20 
            # Configure the time to wait for a timeout to obtain a connection 
            maxWait:  60000 
            # Configure how long the interval is to perform a test to detect idle connections that need to be closed, in milliseconds 
            timeBetweenEvictionRunsMillis:  60000 
            # Configure the minimum survival time of a connection in the pool, in milliseconds 
            minEvictableIdleTimeMillis:  300000 
            # Configure the maximum survival time of a connection in the pool, in milliseconds 
            maxEvictableIdleTimeMillis:  900000 
            # Configure to detect whether the connection is valid 
            validationQuery:  SELECT  1  FROM  DUAL 
            testWhileIdle:  true 
            testOnBorrow:  false 
            testOnReturn:  false
            webStatFilter: 
                enabled:  true 
            statViewServlet: 
                enabled:  true 
                # Set a whitelist, if not filled, all access is allowed. 
                allow: 
                url-pattern:  /druid/* 
                # Console management username and password 
                login-username: 
                login-password: 
            filter: 
                stat: 
                    enabled:  true 
                    # slow SQL logging 
                    log-slow-sql:  true 
                    slow-sql-millis:  1000 
                    merge-sql:  true 
                wall: 
                    config:
                        multi-statement-allow: true
                config:
                    enabled: true
            connection-properties: config.decrypt=true;config.decrypt.key=${spring.datasource.publickey}

Option 2: Use jasypt to encrypt the database password

  1. pom.xml introduces the jasypt package

<dependency>
            <groupId>com.github.ulisesbocchio</groupId>
            <artifactId>jasypt-spring-boot-starter</artifactId>
            <version>${jasypt.verison}</version>
        </dependency>

  1. Use the tools provided by jasypt to encrypt the plaintext password

The encryption tools are as follows

public final class JasyptEncryptorUtils {


    private static final String salt = "lybgeek";

    private static BasicTextEncryptor basicTextEncryptor = new BasicTextEncryptor();

    static {
        basicTextEncryptor.setPassword(salt);
    }

    private JasyptEncryptorUtils(){}

    /**
     * Plaintext encryption
     * @param plaintext
     * @return
     */
    public static String encode(String plaintext){System.out 
        .println ( "Plaintext string: " + plaintext);
        String ciphertext = basicTextEncryptor.encrypt(plaintext);System.out 
        .println ( "Encrypted string: " + ciphertext);
         return ciphertext;
    }

    /**
     * decrypt
     * @param ciphertext
     * @return
     */
    public static String decode(String ciphertext){System.out 
        .println ( "Encrypted string: " + ciphertext);
        ciphertext = "ENC(" + ciphertext + ")";
        if (PropertyValueEncryptionUtils.isEncryptedValue(ciphertext)){
            String plaintext = PropertyValueEncryptionUtils.decrypt(ciphertext,basicTextEncryptor);System.out 
            .println ( "Decrypted string: " + plaintext);
             return plaintext;
        }System.out 
        .println ( "Decryption failed" );
         return  "" ;
    }
}

  1. Modify the content information of the configuration file of the database

a. Wrap the encrypted string generated by JasyptEncryptorUtils with ENC

password: ${DATASOURCE_PWD:ENC(P8m43qmzqN4c07DCTPey4Q==)}

b. Configure keys and specify encryption and decryption algorithms

jasypt:
    encryptor:
        password: lybgeek
        algorithm: PBEWithMD5AndDES
        iv-generator-classname: org.jasypt.iv.NoIvGenerator

Because my tool class uses the encryption and decryption tool class is BasicTextEncryptor, and its corresponding configuration encryption and decryption is PBEWithMD5AndDES and org.jasypt.iv.NoIvGenerator

ps: In the production environment, it is recommended to configure the key in the following way to avoid key leakage

java -jar -Djasypt.encryptor.password=lybgeek

Appendix: Complete Database Configuration

spring: 
    datasource: 
        type:  com.alibaba.druid.pool.DruidDataSource 
        driverClassName:  com.mysql.cj.jdbc.Driver 
        url:  ${DATASOURCE_URL:ENC(kT/gwazwzaFNEp7OCbsgCQN7PHRohaTKJNdGVgLsW2cH67zqBVEq7mN0BTIXAeF4/Fvv4l7myLFx0y6ap4umod7C2VWgyRU5UQtKmdwzQN3hxVxktIkrFPn9DM6+YahM0xP+ppO9HaWqA2ral0ejBCvmor3WScJNHCAhI9kHjYc=)} 
        username:  ${ DATASOURCE_USERNAME:ENC(rEQLlqM5nphqnsuPj3MlJw==)} 
        password:  ${DATASOURCE_PWD:ENC(P8m43qmzqN4c07DCTPey4Q==)} 
        druid: 
            # Initial number of connections 
            initialSize:  5 
            # Minimum number of connection pools 
            minIdle:  10 
            # Maximum number of connection pools 
            maxActive:  20
            # Configure the time to wait for a timeout to acquire a connection 
            maxWait:  60000 
            # Configure how long the interval is to perform detection to detect idle connections that need to be closed, in milliseconds 
            timeBetweenEvictionRunsMillis:  60000 
            # Configure the minimum survival time of a connection in the pool, in milliseconds 
            minEvictableIdleTimeMillis:  300000 
            # Configure the maximum lifetime of a connection in the pool, in milliseconds 
            maxEvictableIdleTimeMillis:  900000 
            # Configure to detect whether the connection is valid 
            validationQuery:  SELECT  1  FROM  DUAL 
            testWhileIdle:  true 
            testOnBorrow:  false 
            testOnReturn:  false 
            webStatFilter: 
                enabled:  true 
            statViewServlet:
                enabled:  true 
                # Set a whitelist, if not filled, all access is allowed 
                allow: 
                url-pattern:  /druid/* 
                # Console management username and password 
                login-username: 
                login-password: 
            filter: 
                stat: 
                    enabled:  true 
                    # Slow SQL record 
                    log-slow-sql:  true 
                    slow-sql-millis:  1000 
                    merge-sql:  true 
                wall: 
                    config: 
                        multi-statement-allow:  true 
jasypt: 
    encryptor: 
        password: lybgeek
        algorithm: PBEWithMD5AndDES
        iv-generator-classname: org.jasypt.iv.NoIvGenerator

Option 3: Custom Implementation

Implementation principle: use spring post processor to modify DataSource

  1. Custom encryption and decryption tools

/**
 * Using the encryption and decryption tool encapsulated by hutool, taking AES symmetric encryption algorithm as an example
 */
public final class EncryptorUtils {

    private static String secretKey;



    static {
        secretKey = Hex.encodeHexString(SecureUtil.generateKey(SymmetricAlgorithm.AES.getValue()).getEncoded());
        System.out.println("secretKey-->" + secretKey);
        System.out.println("--------------------------------------------------------------------------------------");
    }

    /**
     * Plaintext encryption
     * @param plaintext
     * @return
     */
    @SneakyThrows
    public static String encode(String plaintext){
        System.out .println ( "Plaintext string: " + plaintext);
         byte [] key = Hex.decodeHex (secretKey.toCharArray());
        String ciphertext =  SecureUtil.aes(key).encryptHex(plaintext);System.out 
        .println ( "Encrypted string: " + ciphertext);

        return ciphertext;
    }

    /**
     * decrypt
     * @param ciphertext
     * @return
     */
    @SneakyThrows
    public static String decode(String ciphertext){
        System.out .println ( "Encrypted string: " + ciphertext);
         byte [] key = Hex.decodeHex (secretKey.toCharArray());
        String plaintext = SecureUtil.aes(key).decryptStr(ciphertext);System.out 
        .println ( "Decrypted string: " + plaintext);

        return plaintext;
    }

    /**
     * Plaintext encryption
     * @param plaintext
     * @return
     */
    @SneakyThrows
    public static String encode(String secretKey,String plaintext){
        System.out .println ( "Plaintext string: " + plaintext);
         byte [] key = Hex.decodeHex (secretKey.toCharArray());
        String ciphertext =  SecureUtil.aes(key).encryptHex(plaintext);System.out 
        .println ( "Encrypted string: " + ciphertext);

        return ciphertext;
    }

    /**
     * decrypt
     * @param ciphertext
     * @return
     */
    @SneakyThrows
    public static String decode(String secretKey,String ciphertext){
        System.out .println ( "Encrypted string: " + ciphertext);
         byte [] key = Hex.decodeHex (secretKey.toCharArray());
        String plaintext = SecureUtil.aes(key).decryptStr(ciphertext);System.out 
        .println ( "Decrypted string: " + plaintext);

        return plaintext;
    }

}

  1. Write the post-processor

public class DruidDataSourceEncyptBeanPostProcessor implements BeanPostProcessor {

    private CustomEncryptProperties customEncryptProperties;

    private DataSourceProperties dataSourceProperties;

    public DruidDataSourceEncyptBeanPostProcessor(CustomEncryptProperties customEncryptProperties, DataSourceProperties dataSourceProperties) {
        this.customEncryptProperties = customEncryptProperties;
        this.dataSourceProperties = dataSourceProperties;
    }



    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if(bean instanceof DruidDataSource){
            if(customEncryptProperties.isEnabled()){
                DruidDataSource druidDataSource = (DruidDataSource)bean;
                System.out.println("--------------------------------------------------------------------------------------");
                String username = dataSourceProperties.getUsername();
                druidDataSource.setUsername(EncryptorUtils.decode(customEncryptProperties.getSecretKey(),username));
                System.out.println("--------------------------------------------------------------------------------------");
                String password = dataSourceProperties.getPassword();
                druidDataSource.setPassword(EncryptorUtils.decode(customEncryptProperties.getSecretKey(),password));
                System.out.println("--------------------------------------------------------------------------------------");
                String url = dataSourceProperties.getUrl();
                druidDataSource.setUrl(EncryptorUtils.decode(customEncryptProperties.getSecretKey(),url));
                System.out.println("--------------------------------------------------------------------------------------");
            }

        }
        return bean;
    }
}

  1. Modify the content information of the configuration file of the database

a. Change the password

Replace the password with the encrypted password generated by the custom encryption tool class

password: ${DATASOURCE_PWD:fb31cdd78a5fa2c43f530b849f1135e7}

b. Specify the key and turn on the encryption function

custom:
    encrypt:
        enabled: true
        secret-key: 2f8ba810011e0973728afa3f28a0ecb6

ps: Similarly, secret-key should not be directly exposed in the configuration file, it can be specified with -Dcustom.encrypt.secret-key

Appendix: Complete Database Configuration

spring: 
    datasource: 
        type:  com.alibaba.druid.pool.DruidDataSource 
        driverClassName:  com.mysql.cj.jdbc.Driver 
        url:  ${DATASOURCE_URL:dcb268cf3a2626381d2bc5c96f94fb3d7f99352e0e392362cb818a321b0ca61f3a8dad3aeb084242b745c61a1d3dc244ed1484bf745c858c44560dde10e60e90ac65f77ce2926676df7af6b35aefd2bb984ff9a868f1f9052ee9cae5572fa015b66a602f32df39fb1bbc36e04cc0f148e4d610a3e5d54f2eb7c57e4729c9d7b4} 
        username:  ${DATASOURCE_USERNAME:61db3bf3c6d3fe3ce87549c1af1e9061} 
        password:  ${DATASOURCE_PWD:fb31cdd78a5fa2c43f530b849f1135e7} 
        druid: 
            # Initial number of connections 
            initialSize:  5 
            # Minimum number of connection pools 
            minIdle: 10 
            # The maximum number of connection pools 
            maxActive:  20 
            # Configure the time to wait for a timeout to obtain a connection 
            maxWait:  60000 
            # Configure how long the interval is to perform detection to detect idle connections that need to be closed, in milliseconds 
            timeBetweenEvictionRunsMillis:  60000 
            # Configure a connection to be the smallest in the pool Time to live, in milliseconds 
            minEvictableIdleTimeMillis:  300000 
            # Configure the maximum lifetime of a connection in the pool, in milliseconds 
            maxEvictableIdleTimeMillis:  900000 
            # Configure to detect whether the connection is valid 
            validationQuery:  SELECT  1  FROM  DUAL 
            testWhileIdle:  true 
            testOnBorrow:  false 
            testOnReturn:  false 
            webStatFilter :
                enabled:  true 
            statViewServlet: 
                enabled:  true 
                # Set a whitelist, if not filled, all access is allowed 
                allow: 
                url-pattern:  /druid/* 
                # Console management username and password 
                login-username: 
                login-password: 
            filter: 
                stat: 
                    enabled:  true 
                    # slow SQL logging 
                    log-slow-sql:  true 
                    slow-sql-millis:  1000 
                    merge-sql:  true 
                wall: 
                    config: 
                        multi-statement-allow: true
custom:
    encrypt:
        enabled: true
        secret-key: 2f8ba810011e0973728afa3f28a0ecb6

Summarize

For the above three schemes, I personally recommend the jasypt scheme, because it can not only encrypt passwords, but also encrypt other content. Druid can only encrypt database passwords. As for the custom solution, it belongs to practice. After all, there are things that are already available in open source, so don’t make your own wheels.

Finally, there is another point to note that if jasypt is higher than version 2 and lower than 3.0.3, it will cause the configuration center, such as apollo or nacos to dynamically refresh the configuration to fail (the latest version of 3.0.3 officially said that this has been fixed. question).

If you use the configuration center, jasypt recommends using version 3 or below, or using version 3.0.3

demo link

[https://github.com/lyb-geek/springboot-learning/tree/master/springboot-datasouce-encrypt]

Leave a Comment

Your email address will not be published. Required fields are marked *