1
0
mirror of synced 2026-08-31 14:35:18 +00:00
Files

297 lines
8.4 KiB
Plaintext
Raw Permalink Normal View History

2021-08-19 14:17:49 -05:00
[[crypto]]
= Spring Security Crypto Module
[[spring-security-crypto-introduction]]
The Spring Security Crypto module provides support for symmetric encryption, key generation, and password encoding.
The code is distributed as part of the core module but has no dependencies on any other Spring Security (or Spring) code.
[[spring-security-crypto-encryption]]
== Encryptors
2026-06-03 15:39:43 -06:00
Spring Security provides javadoc:org.springframework.security.crypto.encrypt.AesGcmBytesEncryptor[] and javadoc:org.springframework.security.crypto.encrypt.AesCbcBytesEncryptor[] for constructing symmetric encryptors.
These can be used to encrypt data in raw `byte[]` form.
2024-07-09 13:23:24 -05:00
You can also construct javadoc:org.springframework.security.crypto.encrypt.TextEncryptor[] instances to encrypt text strings.
2021-08-19 14:17:49 -05:00
Encryptors are thread-safe.
2021-06-04 14:12:59 -05:00
[NOTE]
====
Both `BytesEncryptor` and `TextEncryptor` are interfaces. `BytesEncryptor` has multiple implementations.
====
2021-08-19 14:17:49 -05:00
[[spring-security-crypto-encryption-bytes]]
=== BytesEncryptor
2026-06-03 15:39:43 -06:00
Use `AesGcmBytesEncryptor` to construct a BytesEncryptor with authenticated encryption:
2021-08-19 14:17:49 -05:00
.BytesEncryptor
2023-06-18 21:30:41 -05:00
[tabs]
======
Java::
+
2021-08-19 14:17:49 -05:00
[source,java,role="primary"]
----
2026-06-03 15:39:43 -06:00
AesGcmBytesEncryptor.withPassword("password", "salt").build();
2021-08-19 14:17:49 -05:00
----
2023-06-18 21:30:41 -05:00
Kotlin::
+
2021-08-19 14:17:49 -05:00
[source,kotlin,role="secondary"]
----
2026-06-03 15:39:43 -06:00
AesGcmBytesEncryptor.withPassword("password", "salt").build()
2021-08-19 14:17:49 -05:00
----
2023-06-18 21:30:41 -05:00
======
2021-08-19 14:17:49 -05:00
2026-06-03 15:39:43 -06:00
`AesGcmBytesEncryptor` uses 256-bit AES encryption with Galois Counter Mode (GCM), providing https://en.wikipedia.org/wiki/Authenticated_encryption[authenticated encryption] (AEAD).
It derives the secret key using PKCS #5's PBKDF2 (Password-Based Key Derivation Function #2).
The password used to generate the SecretKey should be kept in a secure place and not be shared.
The salt is used to prevent dictionary attacks against the key in the event your encrypted data is compromised.
A 16-byte random initialization vector is also applied so each encrypted message is unique.
2021-08-19 14:17:49 -05:00
The provided salt should be in hex-encoded String form, be random, and be at least 8 bytes in length.
2021-04-21 16:01:26 -05:00
You can generate such a salt by using a `KeyGenerator`:
2021-08-19 14:17:49 -05:00
.Generating a key
2023-06-18 21:30:41 -05:00
[tabs]
======
Java::
+
2021-08-19 14:17:49 -05:00
[source,java,role="primary"]
----
String salt = KeyGenerators.string().generateKey(); // generates a random 8-byte salt that is then hex-encoded
----
2023-06-18 21:30:41 -05:00
Kotlin::
+
2021-08-19 14:17:49 -05:00
[source,kotlin,role="secondary"]
----
val salt = KeyGenerators.string().generateKey() // generates a random 8-byte salt that is then hex-encoded
----
2023-06-18 21:30:41 -05:00
======
2021-08-19 14:17:49 -05:00
2026-06-03 15:39:43 -06:00
Users who require CBC mode may use `AesCbcBytesEncryptor`:
.AesCbcBytesEncryptor
[tabs]
======
Java::
+
[source,java,role="primary"]
----
AesCbcBytesEncryptor.withPassword("password", "salt").build();
----
Kotlin::
+
[source,kotlin,role="secondary"]
----
AesCbcBytesEncryptor.withPassword("password", "salt").build()
----
======
2021-08-19 14:17:49 -05:00
This mode is not https://en.wikipedia.org/wiki/Authenticated_encryption[authenticated] and does not provide any
guarantees about the authenticity of the data.
2026-06-03 15:39:43 -06:00
For a more secure alternative, users should prefer `AesGcmBytesEncryptor`.
2021-08-19 14:17:49 -05:00
[[spring-security-crypto-encryption-text]]
=== TextEncryptor
2026-06-03 15:39:43 -06:00
Use `AesCbcBytesEncryptor` to encrypt text data:
2021-08-19 14:17:49 -05:00
.TextEncryptor
2023-06-18 21:30:41 -05:00
[tabs]
======
Java::
+
2021-08-19 14:17:49 -05:00
[source,java,role="primary"]
----
2026-06-03 15:39:43 -06:00
AesCbcBytesEncryptor.withPassword("password", "salt").build();
2021-08-19 14:17:49 -05:00
----
2023-06-18 21:30:41 -05:00
Kotlin::
+
2021-08-19 14:17:49 -05:00
[source,kotlin,role="secondary"]
----
2026-06-03 15:39:43 -06:00
AesCbcBytesEncryptor.withPassword("password", "salt").build()
2021-08-19 14:17:49 -05:00
----
2023-06-18 21:30:41 -05:00
======
2021-08-19 14:17:49 -05:00
2026-06-03 15:39:43 -06:00
`AesCbcBytesEncryptor` encrypts data as raw bytes.
You can hex-encode the result for easy storage on the filesystem or in the database.
NOTE: Queryable text encryption (encrypting such that the same plaintext always produces the same ciphertext) is no longer recommended, as it relies on a fixed initialization vector and does not provide adequate security.
Instead, look to your data store for a mechanism to query encrypted data.
2021-08-19 14:17:49 -05:00
[[spring-security-crypto-keygenerators]]
== Key Generators
2024-07-09 13:23:24 -05:00
The javadoc:org.springframework.security.crypto.keygen.KeyGenerators[] class provides a number of convenience factory methods for constructing different types of key generators.
By using this class, you can create a javadoc:org.springframework.security.crypto.keygen.BytesKeyGenerator[] to generate `byte[]` keys.
You can also construct a javadoc:org.springframework.security.crypto.keygen.StringKeyGenerator[] to generate string keys.
2021-04-21 16:01:26 -05:00
`KeyGenerators` is a thread-safe class.
2021-08-19 14:17:49 -05:00
=== BytesKeyGenerator
2021-04-21 16:01:26 -05:00
You can use the `KeyGenerators.secureRandom` factory methods to generate a `BytesKeyGenerator` backed by a `SecureRandom` instance:
2021-08-19 14:17:49 -05:00
.BytesKeyGenerator
2023-06-18 21:30:41 -05:00
[tabs]
======
Java::
+
2021-08-19 14:17:49 -05:00
[source,java,role="primary"]
----
BytesKeyGenerator generator = KeyGenerators.secureRandom();
byte[] key = generator.generateKey();
----
2023-06-18 21:30:41 -05:00
Kotlin::
+
2021-08-19 14:17:49 -05:00
[source,kotlin,role="secondary"]
----
val generator = KeyGenerators.secureRandom()
val key = generator.generateKey()
----
2023-06-18 21:30:41 -05:00
======
2021-08-19 14:17:49 -05:00
The default key length is 8 bytes.
2021-04-21 16:01:26 -05:00
A `KeyGenerators.secureRandom` variant provides control over the key length:
2021-08-19 14:17:49 -05:00
.KeyGenerators.secureRandom
2023-06-18 21:30:41 -05:00
[tabs]
======
Java::
+
2021-08-19 14:17:49 -05:00
[source,java,role="primary"]
----
KeyGenerators.secureRandom(16);
----
2023-06-18 21:30:41 -05:00
Kotlin::
+
2021-08-19 14:17:49 -05:00
[source,kotlin,role="secondary"]
----
KeyGenerators.secureRandom(16)
----
2023-06-18 21:30:41 -05:00
======
2021-08-19 14:17:49 -05:00
2021-04-21 16:01:26 -05:00
Use the `KeyGenerators.shared` factory method to construct a BytesKeyGenerator that always returns the same key on every invocation:
2021-08-19 14:17:49 -05:00
.KeyGenerators.shared
2023-06-18 21:30:41 -05:00
[tabs]
======
Java::
+
2021-08-19 14:17:49 -05:00
[source,java,role="primary"]
----
KeyGenerators.shared(16);
----
2023-06-18 21:30:41 -05:00
Kotlin::
+
2021-08-19 14:17:49 -05:00
[source,kotlin,role="secondary"]
----
KeyGenerators.shared(16)
----
2023-06-18 21:30:41 -05:00
======
2021-08-19 14:17:49 -05:00
=== StringKeyGenerator
2021-04-21 16:01:26 -05:00
You can use the `KeyGenerators.string` factory method to construct an 8-byte, `SecureRandom` `KeyGenerator` that hex-encodes each key as a `String`:
2021-08-19 14:17:49 -05:00
.StringKeyGenerator
2023-06-18 21:30:41 -05:00
[tabs]
======
Java::
+
2021-08-19 14:17:49 -05:00
[source,java,role="primary"]
----
KeyGenerators.string();
----
2023-06-18 21:30:41 -05:00
Kotlin::
+
2021-08-19 14:17:49 -05:00
[source,kotlin,role="secondary"]
----
KeyGenerators.string()
----
2023-06-18 21:30:41 -05:00
======
2021-08-19 14:17:49 -05:00
[[spring-security-crypto-passwordencoders]]
== Password Encoding
2021-04-21 16:01:26 -05:00
The password package of the `spring-security-crypto` module provides support for encoding passwords.
2021-08-19 14:17:49 -05:00
`PasswordEncoder` is the central service interface and has the following signature:
[source,java]
----
public interface PasswordEncoder {
2022-03-01 21:37:31 +07:00
String encode(CharSequence rawPassword);
2021-08-19 14:17:49 -05:00
2022-03-01 21:37:31 +07:00
boolean matches(CharSequence rawPassword, String encodedPassword);
2021-08-19 14:17:49 -05:00
2022-03-01 21:37:31 +07:00
default boolean upgradeEncoding(String encodedPassword) {
return false;
}
2021-08-19 14:17:49 -05:00
}
----
2021-04-21 16:01:26 -05:00
The `matches` method returns true if the `rawPassword`, once encoded, equals the `encodedPassword`.
2021-08-19 14:17:49 -05:00
This method is designed to support password-based authentication schemes.
2021-04-21 16:01:26 -05:00
The `BCryptPasswordEncoder` implementation uses the widely supported "`bcrypt`" algorithm to hash the passwords.
Bcrypt uses a random 16-byte salt value and is a deliberately slow algorithm, to hinder password crackers.
You can tune the amount of work it does by using the `strength` parameter, which takes a value from 4 to 31.
2021-08-19 14:17:49 -05:00
The higher the value, the more work has to be done to calculate the hash.
2021-04-21 16:01:26 -05:00
The default value is `10`.
2021-08-19 14:17:49 -05:00
You can change this value in your deployed system without affecting existing passwords, as the value is also stored in the encoded hash.
2021-04-21 16:01:26 -05:00
The following example uses the `BCryptPasswordEncoder`:
2021-08-19 14:17:49 -05:00
.BCryptPasswordEncoder
2023-06-18 21:30:41 -05:00
[tabs]
======
Java::
+
2021-08-19 14:17:49 -05:00
[source,java,role="primary"]
----
// Create an encoder with strength 16
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(16);
String result = encoder.encode("myPassword");
assertTrue(encoder.matches("myPassword", result));
----
2023-06-18 21:30:41 -05:00
Kotlin::
+
2021-08-19 14:17:49 -05:00
[source,kotlin,role="secondary"]
----
// Create an encoder with strength 16
val encoder = BCryptPasswordEncoder(16)
val result: String = encoder.encode("myPassword")
assertTrue(encoder.matches("myPassword", result))
----
2023-06-18 21:30:41 -05:00
======
2021-08-19 14:17:49 -05:00
The `Pbkdf2PasswordEncoder` implementation uses PBKDF2 algorithm to hash the passwords.
2021-04-21 16:01:26 -05:00
To defeat password cracking, PBKDF2 is a deliberately slow algorithm and should be tuned to take about .5 seconds to verify a password on your system.
The following system uses the `Pbkdf2PasswordEncoder`:
2021-08-19 14:17:49 -05:00
.Pbkdf2PasswordEncoder
2023-06-18 21:30:41 -05:00
[tabs]
======
Java::
+
2021-08-19 14:17:49 -05:00
[source,java,role="primary"]
----
// Create an encoder with all the defaults
2022-10-12 07:22:58 -04:00
Pbkdf2PasswordEncoder encoder = Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_8();
2021-08-19 14:17:49 -05:00
String result = encoder.encode("myPassword");
assertTrue(encoder.matches("myPassword", result));
----
2023-06-18 21:30:41 -05:00
Kotlin::
+
2021-08-19 14:17:49 -05:00
[source,kotlin,role="secondary"]
----
// Create an encoder with all the defaults
2022-10-12 07:22:58 -04:00
val encoder = Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_8()
2021-08-19 14:17:49 -05:00
val result: String = encoder.encode("myPassword")
assertTrue(encoder.matches("myPassword", result))
----
2023-06-18 21:30:41 -05:00
======