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 fe8626b

Browse files
Add Argon2 and BCrypt and Scrypt password encoders using Password4j library
Closes gh-17706
1 parent 265e1d1 commit fe8626b

File tree

9 files changed

+994
-204
lines changed

9 files changed

+994
-204
lines changed
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
* Copyright 2004-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.crypto.password4j;
18+
19+
import com.password4j.AlgorithmFinder;
20+
import com.password4j.Argon2Function;
21+
22+
/**
23+
* Implementation of {@link org.springframework.security.crypto.password.PasswordEncoder}
24+
* that uses the Password4j library with Argon2 hashing algorithm.
25+
*
26+
* <p>
27+
* Argon2 is the winner of the Password Hashing Competition (2015) and is recommended for
28+
* new applications. It provides excellent resistance against GPU-based attacks and
29+
* includes built-in salt generation. This implementation leverages Password4j's Argon2
30+
* support which properly includes the salt in the output hash.
31+
* </p>
32+
*
33+
* <p>
34+
* This implementation is thread-safe and can be shared across multiple threads.
35+
* </p>
36+
*
37+
* <p>
38+
* <strong>Usage Examples:</strong>
39+
* </p>
40+
* <pre>{@code
41+
* // Using default Argon2 settings (recommended)
42+
* PasswordEncoder encoder = new Argon2Password4jPasswordEncoder();
43+
*
44+
* // Using custom Argon2 configuration
45+
* PasswordEncoder customEncoder = new Argon2Password4jPasswordEncoder(
46+
* Argon2Function.getInstance(65536, 3, 4, 32, Argon2.ID));
47+
* }</pre>
48+
*
49+
* @author Mehrdad Bozorgmehr
50+
* @since 7.0
51+
* @see Argon2Function
52+
* @see AlgorithmFinder#getArgon2Instance()
53+
*/
54+
public class Argon2Password4jPasswordEncoder extends Password4jPasswordEncoder {
55+
56+
/**
57+
* Constructs an Argon2 password encoder using the default Argon2 configuration from
58+
* Password4j's AlgorithmFinder.
59+
*/
60+
public Argon2Password4jPasswordEncoder() {
61+
super(AlgorithmFinder.getArgon2Instance());
62+
}
63+
64+
/**
65+
* Constructs an Argon2 password encoder with a custom Argon2 function.
66+
* @param argon2Function the Argon2 function to use for encoding passwords, must not
67+
* be null
68+
* @throws IllegalArgumentException if argon2Function is null
69+
*/
70+
public Argon2Password4jPasswordEncoder(Argon2Function argon2Function) {
71+
super(argon2Function);
72+
}
73+
74+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* Copyright 2004-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.crypto.password4j;
18+
19+
import com.password4j.AlgorithmFinder;
20+
import com.password4j.BcryptFunction;
21+
22+
/**
23+
* Implementation of {@link org.springframework.security.crypto.password.PasswordEncoder}
24+
* that uses the Password4j library with BCrypt hashing algorithm.
25+
*
26+
* <p>
27+
* BCrypt is a well-established password hashing algorithm that includes built-in salt
28+
* generation and is resistant to rainbow table attacks. This implementation leverages
29+
* Password4j's BCrypt support which properly includes the salt in the output hash.
30+
* </p>
31+
*
32+
* <p>
33+
* This implementation is thread-safe and can be shared across multiple threads.
34+
* </p>
35+
*
36+
* <p>
37+
* <strong>Usage Examples:</strong>
38+
* </p>
39+
* <pre>{@code
40+
* // Using default BCrypt settings (recommended)
41+
* PasswordEncoder encoder = new BcryptPassword4jPasswordEncoder();
42+
*
43+
* // Using custom round count
44+
* PasswordEncoder customEncoder = new BcryptPassword4jPasswordEncoder(BcryptFunction.getInstance(12));
45+
* }</pre>
46+
*
47+
* @author Mehrdad Bozorgmehr
48+
* @since 7.0
49+
* @see BcryptFunction
50+
* @see AlgorithmFinder#getBcryptInstance()
51+
*/
52+
public class BcryptPassword4jPasswordEncoder extends Password4jPasswordEncoder {
53+
54+
/**
55+
* Constructs a BCrypt password encoder using the default BCrypt configuration from
56+
* Password4j's AlgorithmFinder.
57+
*/
58+
public BcryptPassword4jPasswordEncoder() {
59+
super(AlgorithmFinder.getBcryptInstance());
60+
}
61+
62+
/**
63+
* Constructs a BCrypt password encoder with a custom BCrypt function.
64+
* @param bcryptFunction the BCrypt function to use for encoding passwords, must not
65+
* be null
66+
* @throws IllegalArgumentException if bcryptFunction is null
67+
*/
68+
public BcryptPassword4jPasswordEncoder(BcryptFunction bcryptFunction) {
69+
super(bcryptFunction);
70+
}
71+
72+
}

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

Lines changed: 13 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -16,116 +16,56 @@
1616

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

19-
import com.password4j.AlgorithmFinder;
2019
import com.password4j.Hash;
2120
import com.password4j.HashingFunction;
2221
import com.password4j.Password;
23-
import org.apache.commons.logging.Log;
24-
import org.apache.commons.logging.LogFactory;
2522

2623
import org.springframework.security.crypto.password.AbstractValidatingPasswordEncoder;
2724
import org.springframework.util.Assert;
2825

2926
/**
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.
27+
* Abstract base class for Password4j-based password encoders. This class provides the
28+
* common functionality for password encoding and verification using the Password4j
29+
* library.
3330
*
3431
* <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.
32+
* This class is package-private and should not be used directly. Instead, use the
33+
* specific public subclasses that support verified hashing algorithms such as BCrypt,
34+
* Argon2, and SCrypt implementations.
3835
* </p>
3936
*
4037
* <p>
4138
* This implementation is thread-safe and can be shared across multiple threads.
4239
* </p>
4340
*
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>
63-
*
6441
* @author Mehrdad Bozorgmehr
6542
* @since 7.0
66-
* @see AlgorithmFinder
6743
*/
68-
public class Password4jPasswordEncoder extends AbstractValidatingPasswordEncoder {
69-
70-
private final Log logger = LogFactory.getLog(getClass());
44+
abstract class Password4jPasswordEncoder extends AbstractValidatingPasswordEncoder {
7145

7246
private final HashingFunction hashingFunction;
7347

7448
/**
75-
* Constructs a Password4j password encoder with the specified hashing function.
76-
*
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>
87-
*
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>
49+
* Constructs a Password4j password encoder with the specified hashing function. This
50+
* constructor is package-private and intended for use by subclasses only.
9951
* @param hashingFunction the hashing function to use for encoding passwords, must not
10052
* be null
10153
* @throws IllegalArgumentException if hashingFunction is null
10254
*/
103-
publicPassword4jPasswordEncoder(HashingFunction hashingFunction) {
55+
Password4jPasswordEncoder(HashingFunction hashingFunction) {
10456
Assert.notNull(hashingFunction, "hashingFunction cannot be null");
10557
this.hashingFunction = hashingFunction;
10658
}
10759

10860
@Override
10961
protected String encodeNonNullPassword(String rawPassword) {
110-
try {
111-
Hash hash = Password.hash(rawPassword).with(this.hashingFunction);
112-
return hash.getResult();
113-
}
114-
catch (Exception ex) {
115-
throw new IllegalStateException("Failed to encode password using Password4j", ex);
116-
}
62+
Hash hash = Password.hash(rawPassword).with(this.hashingFunction);
63+
return hash.getResult();
11764
}
11865

11966
@Override
12067
protected boolean matchesNonNull(String rawPassword, String encodedPassword) {
121-
try {
122-
// Use the specific hashing function for verification
123-
return Password.check(rawPassword, encodedPassword).with(this.hashingFunction);
124-
}
125-
catch (Exception ex) {
126-
this.logger.warn("Password verification failed for encoded password: " + encodedPassword, ex);
127-
return false;
128-
}
68+
return Password.check(rawPassword, encodedPassword).with(this.hashingFunction);
12969
}
13070

13171
@Override
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
* Copyright 2004-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.crypto.password4j;
18+
19+
import com.password4j.AlgorithmFinder;
20+
import com.password4j.ScryptFunction;
21+
22+
/**
23+
* Implementation of {@link org.springframework.security.crypto.password.PasswordEncoder}
24+
* that uses the Password4j library with SCrypt hashing algorithm.
25+
*
26+
* <p>
27+
* SCrypt is a memory-hard password hashing algorithm designed to be resistant to hardware
28+
* brute-force attacks. It includes built-in salt generation and is particularly effective
29+
* against ASIC and GPU-based attacks. This implementation leverages Password4j's SCrypt
30+
* support which properly includes the salt in the output hash.
31+
* </p>
32+
*
33+
* <p>
34+
* This implementation is thread-safe and can be shared across multiple threads.
35+
* </p>
36+
*
37+
* <p>
38+
* <strong>Usage Examples:</strong>
39+
* </p>
40+
* <pre>{@code
41+
* // Using default SCrypt settings (recommended)
42+
* PasswordEncoder encoder = new ScryptPassword4jPasswordEncoder();
43+
*
44+
* // Using custom SCrypt configuration
45+
* PasswordEncoder customEncoder = new ScryptPassword4jPasswordEncoder(
46+
* ScryptFunction.getInstance(32768, 8, 1, 32));
47+
* }</pre>
48+
*
49+
* @author Mehrdad Bozorgmehr
50+
* @since 7.0
51+
* @see ScryptFunction
52+
* @see AlgorithmFinder#getScryptInstance()
53+
*/
54+
public class ScryptPassword4jPasswordEncoder extends Password4jPasswordEncoder {
55+
56+
/**
57+
* Constructs an SCrypt password encoder using the default SCrypt configuration from
58+
* Password4j's AlgorithmFinder.
59+
*/
60+
public ScryptPassword4jPasswordEncoder() {
61+
super(AlgorithmFinder.getScryptInstance());
62+
}
63+
64+
/**
65+
* Constructs an SCrypt password encoder with a custom SCrypt function.
66+
* @param scryptFunction the SCrypt function to use for encoding passwords, must not
67+
* be null
68+
* @throws IllegalArgumentException if scryptFunction is null
69+
*/
70+
public ScryptPassword4jPasswordEncoder(ScryptFunction scryptFunction) {
71+
super(scryptFunction);
72+
}
73+
74+
}

0 commit comments

Comments
(0)

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