Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 04f994c

Browse files
Refactor Password4jPasswordEncoder to use AlgorithmFinder for algorithm selection and enhance documentation
Closes gh-17706 Signed-off-by: M.Bozorgmehr <mehrdad.bozorgmehr@gmail.com>
1 parent b711da9 commit 04f994c

File tree

5 files changed

+177
-755
lines changed

5 files changed

+177
-755
lines changed

‎crypto/src/main/java/org/springframework/security/crypto/factory/PasswordEncoderFactories.java

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
import org.springframework.security.crypto.password.DelegatingPasswordEncoder;
2525
import org.springframework.security.crypto.password.PasswordEncoder;
2626
import org.springframework.security.crypto.password.Pbkdf2PasswordEncoder;
27-
import org.springframework.security.crypto.password4j.Password4jPasswordEncoder;
2827
import org.springframework.security.crypto.scrypt.SCryptPasswordEncoder;
2928

3029
/**
@@ -66,10 +65,6 @@ private PasswordEncoderFactories() {
6665
* <li>argon2 - {@link Argon2PasswordEncoder#defaultsForSpringSecurity_v5_2()}</li>
6766
* <li>argon2@SpringSecurity_v5_8 -
6867
* {@link Argon2PasswordEncoder#defaultsForSpringSecurity_v5_8()}</li>
69-
* <li>password4j-bcrypt - {@link Password4jPasswordEncoder} with BCrypt</li>
70-
* <li>password4j-scrypt - {@link Password4jPasswordEncoder} with SCrypt</li>
71-
* <li>password4j-argon2 - {@link Password4jPasswordEncoder} with Argon2</li>
72-
* <li>password4j-pbkdf2 - {@link Password4jPasswordEncoder} with PBKDF2</li>
7368
* </ul>
7469
* @return the {@link PasswordEncoder} to use
7570
*/
@@ -92,14 +87,6 @@ public static PasswordEncoder createDelegatingPasswordEncoder() {
9287
encoders.put("sha256", new org.springframework.security.crypto.password.StandardPasswordEncoder());
9388
encoders.put("argon2", Argon2PasswordEncoder.defaultsForSpringSecurity_v5_2());
9489
encoders.put("argon2@SpringSecurity_v5_8", Argon2PasswordEncoder.defaultsForSpringSecurity_v5_8());
95-
96-
// Password4j implementations
97-
encoders.put("password4j-bcrypt", Password4jPasswordEncoder.bcrypt(10));
98-
encoders.put("password4j-scrypt", Password4jPasswordEncoder.scrypt(16384, 8, 1, 32));
99-
encoders.put("password4j-argon2", Password4jPasswordEncoder.argon2(65536, 3, 4, 32,
100-
com.password4j.types.Argon2.ID));
101-
encoders.put("password4j-pbkdf2", Password4jPasswordEncoder.pbkdf2(310000, 32));
102-
10390
return new DelegatingPasswordEncoder(encodingId, encoders);
10491
}
10592

‎crypto/src/main/java/org/springframework/security/crypto/password4j/Password4jPasswordEncoder.java

Lines changed: 68 additions & 179 deletions
Original file line numberDiff line numberDiff line change
@@ -16,179 +16,102 @@
1616

1717
package org.springframework.security.crypto.password4j;
1818

19-
import com.password4j.*;
20-
import com.password4j.types.Argon2;
19+
import com.password4j.AlgorithmFinder;
20+
import com.password4j.Hash;
21+
import com.password4j.HashingFunction;
22+
import com.password4j.Password;
2123
import org.apache.commons.logging.Log;
2224
import org.apache.commons.logging.LogFactory;
25+
2326
import org.springframework.security.crypto.password.AbstractValidatingPasswordEncoder;
2427
import org.springframework.util.Assert;
2528

2629
/**
27-
* Implementation of {@link org.springframework.security.crypto.password.PasswordEncoder} that uses the Password4j library.
28-
* This encoder supports multiple password hashing algorithms including BCrypt, SCrypt, Argon2, and PBKDF2.
30+
* Implementation of {@link org.springframework.security.crypto.password.PasswordEncoder}
31+
* that uses the Password4j library. This encoder supports multiple password hashing
32+
* algorithms including BCrypt, SCrypt, Argon2, and PBKDF2.
33+
*
34+
* <p>
35+
* The encoder uses the provided {@link HashingFunction} for both encoding and
36+
* verification. Password4j can automatically detect the algorithm used in existing hashes
37+
* during verification.
38+
* </p>
2939
*
30-
* <p>The encoder determines the algorithm used based on the algorithm type specified during construction.
31-
* For verification, it can automatically detect the algorithm used in existing hashes.</p>
40+
* <p>
41+
* This implementation is thread-safe and can be shared across multiple threads.
42+
* </p>
3243
*
33-
* <p>This implementation is thread-safe and can be shared across multiple threads.</p>
44+
* <p>
45+
* <strong>Usage Examples:</strong>
46+
* </p>
47+
* <pre>{@code
48+
* // Using default algorithms from AlgorithmFinder (recommended approach)
49+
* PasswordEncoder bcryptEncoder = new Password4jPasswordEncoder(AlgorithmFinder.getBcryptInstance());
50+
* PasswordEncoder argon2Encoder = new Password4jPasswordEncoder(AlgorithmFinder.getArgon2Instance());
51+
* PasswordEncoder scryptEncoder = new Password4jPasswordEncoder(AlgorithmFinder.getScryptInstance());
52+
* PasswordEncoder pbkdf2Encoder = new Password4jPasswordEncoder(AlgorithmFinder.getPBKDF2Instance());
53+
*
54+
* // Using customized algorithm parameters
55+
* PasswordEncoder customBcrypt = new Password4jPasswordEncoder(BcryptFunction.getInstance(12));
56+
* PasswordEncoder customArgon2 = new Password4jPasswordEncoder(
57+
* Argon2Function.getInstance(65536, 3, 4, 32, Argon2.ID));
58+
* PasswordEncoder customScrypt = new Password4jPasswordEncoder(
59+
* ScryptFunction.getInstance(32768, 8, 1, 32));
60+
* PasswordEncoder customPbkdf2 = new Password4jPasswordEncoder(
61+
* CompressedPBKDF2Function.getInstance("SHA256", 310000, 32));
62+
* }</pre>
3463
*
3564
* @author Mehrdad Bozorgmehr
36-
* @since 6.5
65+
* @since 7.0
66+
* @see AlgorithmFinder
3767
*/
3868
public class Password4jPasswordEncoder extends AbstractValidatingPasswordEncoder {
3969

4070
private final Log logger = LogFactory.getLog(getClass());
4171

4272
private final HashingFunction hashingFunction;
4373

44-
private final Password4jAlgorithm algorithm;
45-
46-
47-
/**
48-
* Enumeration of supported Password4j algorithms.
49-
*/
50-
public enum Password4jAlgorithm {
51-
/**
52-
* BCrypt algorithm.
53-
*/
54-
BCRYPT,
55-
/**
56-
* SCrypt algorithm.
57-
*/
58-
SCRYPT,
59-
/**
60-
* Argon2 algorithm.
61-
*/
62-
ARGON2,
63-
/**
64-
* PBKDF2 algorithm.
65-
*/
66-
PBKDF2,
67-
/**
68-
* Compressed PBKDF2 algorithm.
69-
*/
70-
COMPRESSED_PBKDF2
71-
}
72-
73-
/**
74-
* Constructs a Password4j password encoder with the default BCrypt algorithm.
75-
*/
76-
public Password4jPasswordEncoder() {
77-
this(Password4jAlgorithm.BCRYPT);
78-
}
79-
8074
/**
81-
* Constructs a Password4j password encoder with the specified algorithm using default parameters.
75+
* Constructs a Password4j password encoder with the specified hashing function.
8276
*
83-
* @param algorithm the password hashing algorithm to use
84-
*/
85-
publicPassword4jPasswordEncoder(Password4jAlgorithmalgorithm) {
86-
Assert.notNull(algorithm, "algorithm cannot be null");
87-
this.algorithm = algorithm;
88-
this.hashingFunction = createDefaultHashingFunction(algorithm);
89-
}
90-
91-
/**
92-
* Constructs a Password4j password encoder with a custom hashing function.
77+
* <p>
78+
* It is recommended to use password4j's {@link AlgorithmFinder} to obtain default
79+
* instances with secure configurations:
80+
* </p>
81+
* <ul>
82+
* <li>{@code AlgorithmFinder.getBcryptInstance()} - BCrypt with default settings</li>
83+
* <li>{@code AlgorithmFinder.getArgon2Instance()} - Argon2 with default settings</li>
84+
* <li>{@code AlgorithmFinder.getScryptInstance()} - SCrypt with default settings</li>
85+
* <li>{@code AlgorithmFinder.getPBKDF2Instance()} - PBKDF2 with default settings</li>
86+
* </ul>
9387
*
94-
* @param hashingFunction the custom hashing function to use
95-
* @param algorithm the password hashing algorithm type
88+
* <p>
89+
* For custom configurations, you can create specific function instances:
90+
* </p>
91+
* <ul>
92+
* <li>{@code BcryptFunction.getInstance(12)} - BCrypt with 12 rounds</li>
93+
* <li>{@code Argon2Function.getInstance(65536, 3, 4, 32, Argon2.ID)} - Custom
94+
* Argon2</li>
95+
* <li>{@code ScryptFunction.getInstance(16384, 8, 1, 32)} - Custom SCrypt</li>
96+
* <li>{@code CompressedPBKDF2Function.getInstance("SHA256", 310000, 32)} - Custom
97+
* PBKDF2</li>
98+
* </ul>
99+
* @param hashingFunction the hashing function to use for encoding passwords, must not
100+
* be null
101+
* @throws IllegalArgumentException if hashingFunction is null
96102
*/
97-
public Password4jPasswordEncoder(HashingFunction hashingFunction, Password4jAlgorithmalgorithm) {
103+
public Password4jPasswordEncoder(HashingFunction hashingFunction) {
98104
Assert.notNull(hashingFunction, "hashingFunction cannot be null");
99-
Assert.notNull(algorithm, "algorithm cannot be null");
100105
this.hashingFunction = hashingFunction;
101-
this.algorithm = algorithm;
102-
}
103-
104-
/**
105-
* Creates a Password4j password encoder with BCrypt algorithm and specified rounds.
106-
*
107-
* @param rounds the number of rounds (cost factor) for BCrypt
108-
* @return a new Password4j password encoder
109-
*/
110-
public static Password4jPasswordEncoder bcrypt(int rounds) {
111-
return new Password4jPasswordEncoder(BcryptFunction.getInstance(rounds), Password4jAlgorithm.BCRYPT);
112-
}
113-
114-
/**
115-
* Creates a Password4j password encoder with SCrypt algorithm and specified parameters.
116-
*
117-
* @param workFactor the work factor (N parameter)
118-
* @param resources the resources (r parameter)
119-
* @param parallelization the parallelization (p parameter)
120-
* @param derivedKeyLength the derived key length
121-
* @return a new Password4j password encoder
122-
*/
123-
public static Password4jPasswordEncoder scrypt(int workFactor, int resources, int parallelization, int derivedKeyLength) {
124-
return new Password4jPasswordEncoder(
125-
ScryptFunction.getInstance(workFactor, resources, parallelization, derivedKeyLength),
126-
Password4jAlgorithm.SCRYPT
127-
);
128-
}
129-
130-
/**
131-
* Creates a Password4j password encoder with Argon2 algorithm and specified parameters.
132-
*
133-
* @param memory the memory cost
134-
* @param iterations the number of iterations
135-
* @param parallelism the parallelism
136-
* @param outputLength the output length
137-
* @param type the Argon2 type
138-
* @return a new Password4j password encoder
139-
*/
140-
public static Password4jPasswordEncoder argon2(int memory, int iterations, int parallelism, int outputLength, Argon2 type) {
141-
return new Password4jPasswordEncoder(
142-
Argon2Function.getInstance(memory, iterations, parallelism, outputLength, type),
143-
Password4jAlgorithm.ARGON2
144-
);
145-
}
146-
147-
/**
148-
* Creates a Password4j password encoder with PBKDF2 algorithm and specified parameters.
149-
*
150-
* @param iterations the number of iterations
151-
* @param derivedKeyLength the derived key length
152-
* @return a new Password4j password encoder
153-
*/
154-
public static Password4jPasswordEncoder pbkdf2(int iterations, int derivedKeyLength) {
155-
return new Password4jPasswordEncoder(
156-
CompressedPBKDF2Function.getInstance("SHA256", iterations, derivedKeyLength),
157-
Password4jAlgorithm.PBKDF2
158-
);
159-
}
160-
161-
/**
162-
* Creates a Password4j password encoder with compressed PBKDF2 algorithm.
163-
*
164-
* @param iterations the number of iterations
165-
* @param derivedKeyLength the derived key length
166-
* @return a new Password4j password encoder
167-
*/
168-
public static Password4jPasswordEncoder compressedPbkdf2(int iterations, int derivedKeyLength) {
169-
return new Password4jPasswordEncoder(
170-
CompressedPBKDF2Function.getInstance("SHA256", iterations, derivedKeyLength),
171-
Password4jAlgorithm.COMPRESSED_PBKDF2
172-
);
173-
}
174-
175-
/**
176-
* Creates a Password4j password encoder with default settings for Spring Security v5.8+.
177-
* This uses BCrypt with 10 rounds.
178-
*
179-
* @return a new Password4j password encoder with recommended defaults
180-
* @since 6.5
181-
*/
182-
public static Password4jPasswordEncoder defaultsForSpringSecurity() {
183-
return bcrypt(10);
184106
}
185107

186108
@Override
187109
protected String encodeNonNullPassword(String rawPassword) {
188110
try {
189111
Hash hash = Password.hash(rawPassword).with(this.hashingFunction);
190112
return hash.getResult();
191-
} catch (Exception ex) {
113+
}
114+
catch (Exception ex) {
192115
throw new IllegalStateException("Failed to encode password using Password4j", ex);
193116
}
194117
}
@@ -198,7 +121,8 @@ protected boolean matchesNonNull(String rawPassword, String encodedPassword) {
198121
try {
199122
// Use the specific hashing function for verification
200123
return Password.check(rawPassword, encodedPassword).with(this.hashingFunction);
201-
} catch (Exception ex) {
124+
}
125+
catch (Exception ex) {
202126
this.logger.warn("Password verification failed for encoded password: " + encodedPassword, ex);
203127
return false;
204128
}
@@ -211,39 +135,4 @@ protected boolean upgradeEncodingNonNull(String encodedPassword) {
211135
return false;
212136
}
213137

214-
/**
215-
* Creates a default hashing function for the specified algorithm.
216-
*
217-
* @param algorithm the password hashing algorithm
218-
* @return the default hashing function
219-
*/
220-
private static HashingFunction createDefaultHashingFunction(Password4jAlgorithm algorithm) {
221-
return switch (algorithm) {
222-
case BCRYPT -> BcryptFunction.getInstance(10); // Default 10 rounds
223-
case SCRYPT -> ScryptFunction.getInstance(16384, 8, 1, 32); // Default parameters
224-
case ARGON2 -> Argon2Function.getInstance(65536, 3, 4, 32, Argon2.ID); // Default parameters
225-
case PBKDF2 ->
226-
CompressedPBKDF2Function.getInstance("SHA256", 310000, 32); // Use compressed format for self-contained encoding
227-
case COMPRESSED_PBKDF2 -> CompressedPBKDF2Function.getInstance("SHA256", 310000, 32);
228-
};
229-
}
230-
231-
/**
232-
* Gets the algorithm used by this encoder.
233-
*
234-
* @return the password hashing algorithm
235-
*/
236-
public Password4jAlgorithm getAlgorithm() {
237-
return this.algorithm;
238-
}
239-
240-
/**
241-
* Gets the hashing function used by this encoder.
242-
*
243-
* @return the hashing function
244-
*/
245-
public HashingFunction getHashingFunction() {
246-
return this.hashingFunction;
247-
}
248-
249138
}

‎crypto/src/main/java/org/springframework/security/crypto/password4j/package-info.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
* limitations under the License.
1515
*/
1616

17-
1817
@NullMarked
1918
package org.springframework.security.crypto.password4j;
2019

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /