You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
276 lines
7.8 KiB
276 lines
7.8 KiB
[[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 |
|
The javadoc:org.springframework.security.crypto.encrypt.Encryptors[] class provides factory methods for constructing symmetric encryptors. |
|
This class lets you create javadoc:org.springframework.security.crypto.encrypt.BytesEncryptor[] instances to encrypt data in raw `byte[]` form. |
|
You can also construct javadoc:org.springframework.security.crypto.encrypt.TextEncryptor[] instances to encrypt text strings. |
|
Encryptors are thread-safe. |
|
|
|
[NOTE] |
|
==== |
|
Both `BytesEncryptor` and `TextEncryptor` are interfaces. `BytesEncryptor` has multiple implementations. |
|
==== |
|
|
|
[[spring-security-crypto-encryption-bytes]] |
|
=== BytesEncryptor |
|
You can use the `Encryptors.stronger` factory method to construct a `BytesEncryptor`: |
|
|
|
.BytesEncryptor |
|
[tabs] |
|
====== |
|
Java:: |
|
+ |
|
[source,java,role="primary"] |
|
---- |
|
Encryptors.stronger("password", "salt"); |
|
---- |
|
|
|
Kotlin:: |
|
+ |
|
[source,kotlin,role="secondary"] |
|
---- |
|
Encryptors.stronger("password", "salt") |
|
---- |
|
====== |
|
|
|
The `stronger` encryption method creates an encryptor by using 256-bit AES encryption with |
|
Galois Counter Mode (GCM). |
|
It derives the secret key by using PKCS #5's PBKDF2 (Password-Based Key Derivation Function #2). |
|
This method requires Java 6. |
|
The password used to generate the `SecretKey` should be kept in a secure place and should not be shared. |
|
The salt is used to prevent dictionary attacks against the key in the event that your encrypted data is compromised. |
|
A 16-byte random initialization vector is also applied so that each encrypted message is unique. |
|
|
|
The provided salt should be in hex-encoded String form, be random, and be at least 8 bytes in length. |
|
You can generate such a salt by using a `KeyGenerator`: |
|
|
|
.Generating a key |
|
[tabs] |
|
====== |
|
Java:: |
|
+ |
|
[source,java,role="primary"] |
|
---- |
|
String salt = KeyGenerators.string().generateKey(); // generates a random 8-byte salt that is then hex-encoded |
|
---- |
|
|
|
Kotlin:: |
|
+ |
|
[source,kotlin,role="secondary"] |
|
---- |
|
val salt = KeyGenerators.string().generateKey() // generates a random 8-byte salt that is then hex-encoded |
|
---- |
|
====== |
|
|
|
You can also use the `standard` encryption method, which is 256-bit AES in Cipher Block Chaining (CBC) Mode. |
|
This mode is not https://en.wikipedia.org/wiki/Authenticated_encryption[authenticated] and does not provide any |
|
guarantees about the authenticity of the data. |
|
For a more secure alternative, use `Encryptors.stronger`. |
|
|
|
[[spring-security-crypto-encryption-text]] |
|
=== TextEncryptor |
|
You can use the `Encryptors.text` factory method to construct a standard TextEncryptor: |
|
|
|
.TextEncryptor |
|
[tabs] |
|
====== |
|
Java:: |
|
+ |
|
[source,java,role="primary"] |
|
---- |
|
Encryptors.text("password", "salt"); |
|
---- |
|
|
|
Kotlin:: |
|
+ |
|
[source,kotlin,role="secondary"] |
|
---- |
|
Encryptors.text("password", "salt") |
|
---- |
|
====== |
|
|
|
A `TextEncryptor` uses a standard `BytesEncryptor` to encrypt text data. |
|
Encrypted results are returned as hex-encoded strings for easy storage on the filesystem or in a database. |
|
|
|
[[spring-security-crypto-keygenerators]] |
|
== Key Generators |
|
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. |
|
`KeyGenerators` is a thread-safe class. |
|
|
|
=== BytesKeyGenerator |
|
You can use the `KeyGenerators.secureRandom` factory methods to generate a `BytesKeyGenerator` backed by a `SecureRandom` instance: |
|
|
|
.BytesKeyGenerator |
|
[tabs] |
|
====== |
|
Java:: |
|
+ |
|
[source,java,role="primary"] |
|
---- |
|
BytesKeyGenerator generator = KeyGenerators.secureRandom(); |
|
byte[] key = generator.generateKey(); |
|
---- |
|
|
|
Kotlin:: |
|
+ |
|
[source,kotlin,role="secondary"] |
|
---- |
|
val generator = KeyGenerators.secureRandom() |
|
val key = generator.generateKey() |
|
---- |
|
====== |
|
|
|
The default key length is 8 bytes. |
|
A `KeyGenerators.secureRandom` variant provides control over the key length: |
|
|
|
.KeyGenerators.secureRandom |
|
[tabs] |
|
====== |
|
Java:: |
|
+ |
|
[source,java,role="primary"] |
|
---- |
|
KeyGenerators.secureRandom(16); |
|
---- |
|
|
|
Kotlin:: |
|
+ |
|
[source,kotlin,role="secondary"] |
|
---- |
|
KeyGenerators.secureRandom(16) |
|
---- |
|
====== |
|
|
|
Use the `KeyGenerators.shared` factory method to construct a BytesKeyGenerator that always returns the same key on every invocation: |
|
|
|
.KeyGenerators.shared |
|
[tabs] |
|
====== |
|
Java:: |
|
+ |
|
[source,java,role="primary"] |
|
---- |
|
KeyGenerators.shared(16); |
|
---- |
|
|
|
Kotlin:: |
|
+ |
|
[source,kotlin,role="secondary"] |
|
---- |
|
KeyGenerators.shared(16) |
|
---- |
|
====== |
|
|
|
=== StringKeyGenerator |
|
You can use the `KeyGenerators.string` factory method to construct an 8-byte, `SecureRandom` `KeyGenerator` that hex-encodes each key as a `String`: |
|
|
|
.StringKeyGenerator |
|
[tabs] |
|
====== |
|
Java:: |
|
+ |
|
[source,java,role="primary"] |
|
---- |
|
KeyGenerators.string(); |
|
---- |
|
|
|
Kotlin:: |
|
+ |
|
[source,kotlin,role="secondary"] |
|
---- |
|
KeyGenerators.string() |
|
---- |
|
====== |
|
|
|
[[spring-security-crypto-passwordencoders]] |
|
== Password Encoding |
|
The password package of the `spring-security-crypto` module provides support for encoding passwords. |
|
`PasswordEncoder` is the central service interface and has the following signature: |
|
|
|
[source,java] |
|
---- |
|
public interface PasswordEncoder { |
|
String encode(CharSequence rawPassword); |
|
|
|
boolean matches(CharSequence rawPassword, String encodedPassword); |
|
|
|
default boolean upgradeEncoding(String encodedPassword) { |
|
return false; |
|
} |
|
} |
|
---- |
|
|
|
The `matches` method returns true if the `rawPassword`, once encoded, equals the `encodedPassword`. |
|
This method is designed to support password-based authentication schemes. |
|
|
|
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. |
|
The higher the value, the more work has to be done to calculate the hash. |
|
The default value is `10`. |
|
You can change this value in your deployed system without affecting existing passwords, as the value is also stored in the encoded hash. |
|
The following example uses the `BCryptPasswordEncoder`: |
|
|
|
.BCryptPasswordEncoder |
|
[tabs] |
|
====== |
|
Java:: |
|
+ |
|
[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)); |
|
---- |
|
|
|
Kotlin:: |
|
+ |
|
[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)) |
|
---- |
|
====== |
|
|
|
The `Pbkdf2PasswordEncoder` implementation uses PBKDF2 algorithm to hash the passwords. |
|
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`: |
|
|
|
|
|
.Pbkdf2PasswordEncoder |
|
[tabs] |
|
====== |
|
Java:: |
|
+ |
|
[source,java,role="primary"] |
|
---- |
|
// Create an encoder with all the defaults |
|
Pbkdf2PasswordEncoder encoder = Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_8(); |
|
String result = encoder.encode("myPassword"); |
|
assertTrue(encoder.matches("myPassword", result)); |
|
---- |
|
|
|
Kotlin:: |
|
+ |
|
[source,kotlin,role="secondary"] |
|
---- |
|
// Create an encoder with all the defaults |
|
val encoder = Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_8() |
|
val result: String = encoder.encode("myPassword") |
|
assertTrue(encoder.matches("myPassword", result)) |
|
---- |
|
======
|
|
|