Encryption in spring boot application.properties

Time:2020-11-26

1 Overview

What? It’s 2020, and you’re still writing clear text passwords in the configuration file of spring boot?
Although it is a small project, no one read the text
Plaintext is simple, fast and convenient!!!
You see the direct user name root password 123456 how simple!!!

No nonsense. This article mainly talks about how to use jasypt spring boot as an open source component to encrypt configuration files, including simple encryption and asymmetric encryption. It also introduces how to input the encryption password when using jar / War deployment

2 simple encryption

Jasypt simple encryption is to write the encrypted password in the file directly
(Well, it’s almost like no encryption )

2.1 dependence

At present, the latest version is 3.0.2. Please check the official GitHub for details

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

2.2 encrypted password

stay application.properties Add:

jasypt.encryptor.password=xxx

XXX is a symmetric encrypted password
By default, PBE algorithm is used for encryption. In fact, PBE does not contain real encryption and decryption algorithm, but combines existing message digest algorithm (such as MD5, Sha, etc.) with symmetric encryption algorithm (such as AES, DES, RC2, etc.), and the default combination is HCMA message authentication algorithm, PBE uses the password and randomly generated salt to generate the corresponding symmetric encryption key, and then uses the key to perform symmetric encryption

2.3 output ciphertext

Here, a test field password and key test are added to the configuration file for testing
Encryption in spring boot application.properties
Here, to facilitate the test in run:

@SpringBootApplication
@EnableEncryptableProperties
public class DemoApplication implements CommandLineRunner {
    private static final Logger l = LoggerFactory.getLogger(DemoApplication.class);

    @Autowired
    private StringEncryptor stringEncryptor;

    @Autowired
    private ApplicationContext applicationContext;

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        Environment environment = applicationContext.getEnvironment();
        l.info(stringEncryptor.encrypt(environment.getProperty("password")));
    }
}

Note that when using @ Autowired for automatic assembly of stringencryptor, the official document says that

@Configuration
@EnableEncryptableProperties

Encryption in spring boot application.properties
because

@SpringBootApplication

Contains

@Configuration

So only the latter is needed here
Get ciphertext output after running:
Encryption in spring boot application.properties

2.4 replace configuration file

Replace the above ciphertext to the original configuration file, and add the prefix enc (and suffix)
Encryption in spring boot application.properties
In this way, the encryption is successful. If you directly obtain the attribute, you can see the plaintext:
Encryption in spring boot application.properties

3 custom encryption

Of course, the above simple encryption can not meet the actual needs, so we need to do custom encryption here

3.1 custom suffix before encryption

You need to use a prefix to distinguish between fields that need to be encrypted and those that do not need to be encrypted. The default prefix is

ENC(

The suffix is:

)

Therefore, it is necessary to add enc (and) when encrypting
You can specify two attributes before Customization:
Encryption in spring boot application.properties
The password field needs to be modified accordingly

3.2 password parameterization

In fact, it is to add command line parameters or application environment variables at startup, or read passwords through system environment variables. Please refer to point 4 deployment for details
Command line parameters:

java -jar xxx.jar --jasypt.encryptor.password=xxx

Application environment variables:

java -Djasypt.encryptor.password=xxx -jar xxx.jar

System environment variables:

jasypt.encryptor.password=${TEST}

The premise is that the corresponding system variables have been set

3.3 custom encryption class

You can implement the stringencryptor interface, override the encrypt and decrypt methods in it, and then define an encryption configuration class, specifying the name of the encryption class:

@Configuration
@EnableEncryptableProperties
public class MyEncryptorConfiguration {
    @Bean("MyEncryptor")
    public StringEncryptor getStringEncryptor()
    {
        return new StringEncryptor() {
            @Override
            public String encrypt(String s) {
                return "111";
            }

            @Override
            public String decrypt(String s) {
                return "222";
            }
        };
    }
}

This is a very simple example, encryption directly returns 111, decryption returns 222 directly, the specific encryption and decryption algorithm can directly replace the function body
Note that the bean name needs to be written in the configuration file:

jasypt.encryptor.bean=codeSheepEncryptorBean

Use constructor injection (Autowired can also be used)

private final StringEncryptor stringEncryptor;
public DemoApplication(MyEncryptorConfiguration encryptorConfiguration)
{
    stringEncryptor = encryptorConfiguration.getStringEncryptor();
}

Test:

@Override
public void run(String... args) throws Exception {
    Environment environment = applicationContext.getEnvironment();
    l.info(stringEncryptor.encrypt(environment.getProperty("password")));
    l.info(stringEncryptor.decrypt(environment.getProperty("password")));
}

Encryption in spring boot application.properties

4 deployment

4.1 jar deployment

4.1.1 command line parameter mode

In this way, the jasypt.encryptor.password Remove it, and then modify the running configuration in spring boot for local test
Encryption in spring boot application.properties
When packaging, if you want to test, you need to set Maven parameters. If you don’t test, you can directly check skip tests:
Encryption in spring boot application.properties
After packaging (Maven > package on the right) with parameters, you can run it
Encryption in spring boot application.properties

4.1.2 application environment variable mode

In fact, it is almost the same as the first method jasypt.encryptor.password Set the parameters in VM options. The running configuration of spring boot is as follows:
Encryption in spring boot application.properties
Maven settings (you can skip the test, of course)
Encryption in spring boot application.properties
Unfortunately, the author failed the test
Encryption in spring boot application.properties
There’s no reason why spring boot can be set in this way
(if you know why you fail, you can leave a message. Thank you very much.)
Here we skip the test directly
Encryption in spring boot application.properties
Then it can run happily (the author needs to add two single quotation marks under win)
Encryption in spring boot application.properties

4.1.3 system environment variable mode

Setting environment variables should not be mentioned. Just set them directly, and then modify them jasypt.encryptor.password In the middle of the two curly brackets is the corresponding environment variable name:
Encryption in spring boot application.properties
Running configuration of spring boot:
Encryption in spring boot application.properties
Maven:
Encryption in spring boot application.properties
This Maven test will be OK
It’s amazing
Run (or this comfortable, direct jar)::
Encryption in spring boot application.properties

4.2 War deployment

4.2.1 jar war conversion

The original package is jar package, which needs to be modified when changing to war pom.xml At the same time, the dependency < tompackaging > is added

<packaging>war</packaging>
...
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-tomcat</artifactId>
    <scope>provided</scope>
</dependency>

Add another servlet initializer:

public class ServletInitializer extends SpringBootServletInitializer {
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder)
    {
        return builder.sources(DemoApplication.class);
    }
}

Where demoapplication is the class of main function
When war is converted to jar, you can do the opposite operation

4.2.2 command line parameter mode

The Maven settings will not be mentioned. Like the above, after packaging
I can’t find the way to set Tomcat command line parameters, so I skip this
(welcome to find the message to add, thank you very much
The author is too vegetable and harmful

4.2.3 application environment variable mode

It can be modified directly under win catalina.bat Or enter tomcat9w.exe (tomcat9, tomcat8 is tomcat8w. Exe) for graphical modification, and select Modify here catalina.bat Find SETLOCAL and add

set "JAVA_OPTS=-Djasypt.encryptor.password=test"

Encryption in spring boot application.properties
Then put war under webapps

4.2.4 environment variable mode

This method is the simplest. After setting the environment variables, you can modify the configuration file
Encryption in spring boot application.properties
Direct war package deployment on the line

5 asymmetric encryption

Spring boot 2.2.1 supports asymmetric encryption, and the format of key pair can be PEM / der

5.1 encryption

The RSA user-defined bit encryption tool class (stamp here) of a big guy is used here. It only comes with JDK implementation (jdk8 +), without additional dependency

import java.util.Base64;
import javax.crypto.Cipher;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
/**
 *Java RSA encryption tool class
 *Reference: https://blog.csdn.net/qy20115549/article/details/83105736
 */
public class Test {
    /**
     *The key length corresponds to the original length and the longer the key length, the slower the speed
     */
    private final static int KEY_SIZE = 2048;
    /**
     *Used to encapsulate randomly generated public and private keys
     */
    private static Map<Integer, String> keyMap = new HashMap<Integer, String>();
    /**
     *Randomly generated key pair
     */
    public static void genKeyPair() throws NoSuchAlgorithmException {
        //The keypairgenerator class is used to generate public key and private key pairs, and generates objects based on RSA algorithm
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
        //Initializing the key pair generator
        keyPairGen.initialize(KEY_SIZE, new SecureRandom());
        //Generate a key pair and save it in keypair
        KeyPair keyPair = keyPairGen.generateKeyPair();
        //Get the private key
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        //Get the public key
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        String publicKeyString = Base64.getEncoder().encodeToString(publicKey.getEncoded());
        //Get the private key字符串
        String privateKeyString = Base64.getEncoder().encodeToString(privateKey.getEncoded());
        //Save the public key and private key to map
        //0 is the public key
        keyMap.put(0, publicKeyString);
        //1 represents the private key
        keyMap.put(1, privateKeyString);
    }
    /**
     *RSA public key encryption
     *
     *@ param STR encrypted string
     *@ param publickey public key
     *@ return ciphertext
     *@ throws exception
     */
    public static String encrypt(String str, String publicKey) throws Exception {
        //Base64 encoded public key
        byte[] decoded = Base64.getDecoder().decode(publicKey);
        RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded));
        //RSA encryption
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, pubKey);
        String outStr = Base64.getEncoder().encodeToString(cipher.doFinal(str.getBytes("UTF-8")));
        return outStr;
    }
    /**
     *RSA private key decryption
     *
     *@ param STR encrypted string
     *@ param privatekey private key
     *@ return plaintext
     *@ throws exception the exception information in the decryption process
     */
    public static String decrypt(String str, String privateKey) throws Exception {
        //64 bit decode encrypted string
        byte[] inputByte = Base64.getDecoder().decode(str);
        //Base64 encoded private key
        byte[] decoded = Base64.getDecoder().decode(privateKey);
        RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded));
        //RSA decryption
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, priKey);
        String outStr = new String(cipher.doFinal(inputByte));
        return outStr;
    }
    
    public static void main(String[] args) throws Exception {
        long temp = System.currentTimeMillis();
        //Generating public and private keys
        genKeyPair();
        //Encrypted string
        System.out.println (public key:+ keyMap.get (0));
        System.out.println (private key:+ keyMap.get (1));
        System.out.println ("key generation time: +( System.currentTimeMillis () - Temp) / 1000.0 + "sec.);
        //String message = "RSA test ABCD ~! @ # $";
        String message = "test";
        System.out.println (original text) + message;
        temp = System.currentTimeMillis();
        String messageEn = encrypt(message, keyMap.get(0));
        System.out.println ("en message");
        System.out.println ("encryption consumption time: +( System.currentTimeMillis () - Temp) / 1000.0 + "sec.);
        temp = System.currentTimeMillis();
        String messageDe = decrypt(messageEn, keyMap.get(1));
        System.out.println ("decryption: + message de)";
        System.out.println ("decryption time consumed: +( System.currentTimeMillis () - Temp) / 1000.0 + "sec.);
    }
}

5.2 modify configuration file

Input the plaintext, get the ciphertext and private key, and replace the original configuration file:
Encryption in spring boot application.properties
Copy the ciphertext to the corresponding encrypted field, add the Prefix suffix, and select der as the private key format to copy the private key
Encryption in spring boot application.properties
It’s OK to run the test

If you think the article looks good, welcome to like

Meanwhile, welcome to WeChat official account: the Ling Ling Road.

Encryption in spring boot application.properties

Recommended Today

Talking about Java collection

preface Most programming languages provide array to save objects, and array is one of the most important data structures. However, the length of the array has been defined during initialization, which is immutable and troublesome to use. Therefore, Java inJDK 1.2The collection framework is added to the version to save and manipulate objects. The container […]