[JAVA] Try Spring Security AES256 string encryption / decryption

Overview

At work, I sometimes considered how to deal with cases that presuppose data encryption / decryption, so I will write that I tried it as a reminder.

Reference information

English but spring.io documentation is helpful spring.io --Encryptors documentation

In Japanese, the TERA SOLUNA document is easy to read 6. Security measures by TERASOLUNA Server Framework for Java (5.x) >> 6.9. Encryption

Conclusion (suddenly)

From the conclusion, it looks like the following (only my personal opinion)

Method Contents Cipher mode of operation Encryption strength
noOpText Do not encrypt. It is used as a Mock for implementation. None out of the question
queryableText Use "standard" password-based encryption. Generate an invariant value. CBC Low
text Use "standard" password-based encryption. Generate a variable value. CBC During ~
delux Use "stronger" password-based encryption. Generate a variable value. GCM High

Verification

Create the following sample and try it out.

public class EncryptorTest {

  private static String secret = "1234";
  private static String salt = "5678";

  public static void main(String[] args) throws IOException {

    String text = "Hello";

    TextEncryptor encryptor1 = Encryptors.noOpText();
    String noOpTextEncryptText = encryptor1.encrypt(text);
    String noOpTextDecryptText = encryptor1.decrypt(noOpTextEncryptText);

    TextEncryptor encryptor2 = Encryptors.queryableText(secret, salt);
    String queryableTextEncryptText = encryptor2.encrypt(text);
    String queryableTextDecryptText = encryptor2.decrypt(queryableTextEncryptText);

    TextEncryptor encryptor3 = Encryptors.text(secret, salt);
    String textEncryptText = encryptor3.encrypt(text);
    String textDecryptText = encryptor3.decrypt(textEncryptText);

    TextEncryptor encryptor4 = Encryptors.delux(secret, salt);
    String deluxEncryptText = encryptor4.encrypt(text);
    String deluxDecryptText = encryptor4.decrypt(deluxEncryptText);

    System.out.println("noOpText()      :");
 System.out.println ("encryption string:" + noOpTextEncryptText);
 System.out.println ("Decryption Confirmation:" + noOpTextDecryptText);
    System.out.println("");
    System.out.println("queryableText() : ");
 System.out.println ("encryption string:" + queryableTextEncryptText);
 System.out.println ("Decryption Confirmation:" + queryableTextDecryptText);
    System.out.println("");
    System.out.println("text()          : ");
 System.out.println ("encryption string:" + textEncryptText);
 System.out.println ("Decryption Confirmation:" + textDecryptText);
    System.out.println("");
    System.out.println("delux()         : ");
 System.out.println ("encryption string:" + deluxEncryptText);
 System.out.println ("Decryption Confirmation:" + deluxDecryptText);
  }
noOpText()      :
Encrypted string: Hello
Decryption confirmation: Hello

queryableText() : 
Encrypted string: 524110739525b3f266a0064b4229a77d
Decryption confirmation: Hello

text()          : 
Encrypted string: ced1eb40b88e691f502d548ed17752bff82592da917804ea66ea1a49bfa77033
Decryption confirmation: Hello

delux()         : 
Encrypted string: 61e62f473d0cb228305081b927a3225d487a749b87af62071ad60dc6b27703410de45f7fbf
Decryption confirmation: Hello

Although the character length of the encryption is different, it can be seen that the decryption from the encryption is possible in each case. Let's take a look at the logic of each method here to see what the difference is.

noOpText If you create an instance with Encryptors.noOpText () The following logic is called.

  private static final class NoOpTextEncryptor implements TextEncryptor {

    public String encrypt(String text) {
      return text;
    }

    public String decrypt(String encryptedText) {
      return encryptedText;
    }

  }

I commented that it was out of the question, but I didn't do anything. Is there any use for it ...

queryableText

Then ** queryableText **

	public static TextEncryptor queryableText(CharSequence password, CharSequence salt) {
		return new HexEncodingTextEncryptor(new AesBytesEncryptor(password.toString(),
				salt));
	}
	public AesBytesEncryptor(String password, CharSequence salt) {
 this (password, salt, null); // ← The argument here is null
	}

	public AesBytesEncryptor(String password, CharSequence salt,
			BytesKeyGenerator ivGenerator) {
		this(password, salt, ivGenerator, CipherAlgorithm.CBC);
	}

	public AesBytesEncryptor(String password, CharSequence salt,
			BytesKeyGenerator ivGenerator, CipherAlgorithm alg) {
		PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray(), Hex.decode(salt),
				1024, 256);
		SecretKey secretKey = newSecretKey("PBKDF2WithHmacSHA1", keySpec);
		this.secretKey = new SecretKeySpec(secretKey.getEncoded(), "AES");
		this.alg = alg;
		this.encryptor = alg.createCipher();
		this.decryptor = alg.createCipher();
		this.ivGenerator = ivGenerator != null ? ivGenerator : alg.defaultIvGenerator();
	}

text

Leave the comment of ** queryableText ** and leave ** text **

	public static TextEncryptor text(CharSequence password, CharSequence salt) {
		return new HexEncodingTextEncryptor(standard(password, salt));
	}

	public static BytesEncryptor standard(CharSequence password, CharSequence salt) {
		return new AesBytesEncryptor(password.toString(), salt,
 KeyGenerators.secureRandom (16)); // ← The argument here is a random number
	}
	public AesBytesEncryptor(String password, CharSequence salt,
			BytesKeyGenerator ivGenerator) {
		this(password, salt, ivGenerator, CipherAlgorithm.CBC);
	}

	public AesBytesEncryptor(String password, CharSequence salt,
			BytesKeyGenerator ivGenerator, CipherAlgorithm alg) {
		PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray(), Hex.decode(salt),
				1024, 256);
		SecretKey secretKey = newSecretKey("PBKDF2WithHmacSHA1", keySpec);
		this.secretKey = new SecretKeySpec(secretKey.getEncoded(), "AES");
		this.alg = alg;
		this.encryptor = alg.createCipher();
		this.decryptor = alg.createCipher();
		this.ivGenerator = ivGenerator != null ? ivGenerator : alg.defaultIvGenerator();
	}

What is the difference between ** queryableText ** and ** text ** The difference is whether to pass BytesKeyGenerator as ** null ** or ** KeyGenerators.secureRandom (16) **.

Since ** queryableText ** is passed as ** null **, it generates the same encrypted string every time. Since ** text ** passes ** KeyGenerators.secureRandom (16) **, a different random number is generated each time, and as a result, a different encrypted string is generated each time.

delux

And finally ** delux **

	public static TextEncryptor delux(CharSequence password, CharSequence salt) {
		return new HexEncodingTextEncryptor(stronger(password, salt));
	}

	public static BytesEncryptor stronger(CharSequence password, CharSequence salt) {
		return new AesBytesEncryptor(password.toString(), salt,
				KeyGenerators.secureRandom(16), CipherAlgorithm.GCM);
	}
	public AesBytesEncryptor(String password, CharSequence salt,
			BytesKeyGenerator ivGenerator, CipherAlgorithm alg) {
		PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray(), Hex.decode(salt),
				1024, 256);
		SecretKey secretKey = newSecretKey("PBKDF2WithHmacSHA1", keySpec);
		this.secretKey = new SecretKeySpec(secretKey.getEncoded(), "AES");
		this.alg = alg;
		this.encryptor = alg.createCipher();
		this.decryptor = alg.createCipher();
		this.ivGenerator = ivGenerator != null ? ivGenerator : alg.defaultIvGenerator();
	}

What is the difference between ** text ** and ** delux **? The difference is whether ** CBC ** or ** GCM ** is specified for CipherAlgorithm.

It is generally said that GCM (Galois / Counter Mode) is capable of parallel processing and is more efficient than CBC. Despite implementing higher encryption than CBC, there is not much difference in processing time.

As a matter of fact, write a process that repeats encryption → decryption 10,000 times and sample it several times. I compared ** text ** and ** delux **, but the processing time was almost the same. (Error less than 1 second)

Impressions

I felt that it would be better to use ** delux ** unless there was a specific reason. (There are few systems that say that the data length is long in this era, so the disk capacity is ...)

This is the end of this trial

Recommended Posts

Try Spring Security AES256 string encryption / decryption
Try using Spring Boot Security
Try LDAP authentication with Spring Security (Spring Boot) + OpenLDAP
Try to work with Keycloak using Spring Security SAML (Spring 5)
About Spring Security authentication
Spring Security causes 403 forbidden
Try using Spring JDBC