Skip to content

Commit 8d8dd3b

Browse files
committed
SCRAM-SHA-256 support
Included a SaslPrep helper class. Refactored the Hi() method, so to not rely on PBEKeySpec, thus supporting Java 6+ JAVA-2771 JAVA-2759
1 parent 95840af commit 8d8dd3b

13 files changed

+594
-47
lines changed

THIRD-PARTY-NOTICES

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,3 +103,20 @@ https://github.com/mongodb/mongo-java-driver.
103103
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
104104
See the License for the specific language governing permissions and
105105
limitations under the License.
106+
107+
7) The following files: SaslPrep.java
108+
109+
Copyright 2008-present MongoDB, Inc.
110+
Copyright 2017 Tom Bentley
111+
112+
Licensed under the Apache License, Version 2.0 (the "License");
113+
you may not use this file except in compliance with the License.
114+
You may obtain a copy of the License at
115+
116+
http://www.apache.org/licenses/LICENSE-2.0
117+
118+
Unless required by applicable law or agreed to in writing, software
119+
distributed under the License is distributed on an "AS IS" BASIS,
120+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
121+
See the License for the specific language governing permissions and
122+
limitations under the License.

driver-core/src/main/com/mongodb/AuthenticationMechanism.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,13 @@ public enum AuthenticationMechanism {
5151
/**
5252
* The SCRAM-SHA-1 mechanism. See the <a href="http://tools.ietf.org/html/rfc5802">RFC</a>.
5353
*/
54-
SCRAM_SHA_1("SCRAM-SHA-1");
54+
SCRAM_SHA_1("SCRAM-SHA-1"),
55+
56+
/**
57+
* The SCRAM-SHA-256 mechanism. See the <a href="http://tools.ietf.org/html/rfc7677">RFC</a>.
58+
* @since 3.8
59+
*/
60+
SCRAM_SHA_256("SCRAM-SHA-256");
5561

5662
private static final Map<String, AuthenticationMechanism> AUTH_MAP = new HashMap<String, AuthenticationMechanism>();
5763
private final String mechanismName;

driver-core/src/main/com/mongodb/ConnectionString.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -666,6 +666,9 @@ private MongoCredential createMongoCredentialWithMechanism(final AuthenticationM
666666
case SCRAM_SHA_1:
667667
credential = MongoCredential.createScramSha1Credential(userName, authSource, password);
668668
break;
669+
case SCRAM_SHA_256:
670+
credential = MongoCredential.createScramSha256Credential(userName, authSource, password);
671+
break;
669672
default:
670673
throw new UnsupportedOperationException(format("The connection string contains an invalid authentication mechanism'. "
671674
+ "'%s' is not a supported authentication mechanism",

driver-core/src/main/com/mongodb/MongoCredential.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,15 @@ public final class MongoCredential {
8989
*/
9090
public static final String SCRAM_SHA_1_MECHANISM = AuthenticationMechanism.SCRAM_SHA_1.getMechanismName();
9191

92+
/**
93+
* The SCRAM-SHA-256 Mechanism.
94+
*
95+
* @since 3.8
96+
* @mongodb.server.release 4.0
97+
* @mongodb.driver.manual core/authentication/#authentication-scram-sha-256 SCRAM-SHA-256
98+
*/
99+
public static final String SCRAM_SHA_256_MECHANISM = AuthenticationMechanism.SCRAM_SHA_256.getMechanismName();
100+
92101
/**
93102
* Mechanism property key for overriding the service name for GSSAPI authentication.
94103
*

driver-core/src/main/com/mongodb/connection/DefaultAuthenticator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ void authenticateAsync(final InternalConnection connection, final ConnectionDesc
4040

4141
Authenticator createAuthenticator(final ConnectionDescription connectionDescription) {
4242
if (connectionDescription.getServerVersion().compareTo(new ServerVersion(2, 7)) >= 0) {
43-
return new ScramShaAuthenticator(getCredential());
43+
return new ScramSha1Authenticator(getCredential());
4444
} else {
4545
return new NativeAuthenticator(getCredential());
4646
}

driver-core/src/main/com/mongodb/connection/InternalStreamConnectionFactory.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ private Authenticator createAuthenticator(final MongoCredential credential) {
7272
case MONGODB_X509:
7373
return new X509Authenticator(credential);
7474
case SCRAM_SHA_1:
75+
case SCRAM_SHA_256:
7576
return new ScramShaAuthenticator(credential);
7677
case MONGODB_CR:
7778
return new NativeAuthenticator(credential);

driver-core/src/main/com/mongodb/connection/ScramShaAuthenticator.java

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@
1616

1717
package com.mongodb.connection;
1818

19+
import com.mongodb.AuthenticationMechanism;
1920
import com.mongodb.MongoCredential;
2021
import com.mongodb.ServerAddress;
22+
import com.mongodb.internal.authentication.SaslPrep;
2123
import org.bson.internal.Base64;
2224

2325
import javax.crypto.Mac;
@@ -29,23 +31,29 @@
2931
import java.security.MessageDigest;
3032
import java.security.NoSuchAlgorithmException;
3133
import java.security.SecureRandom;
34+
import java.util.Collections;
3235
import java.util.HashMap;
36+
import java.util.LinkedHashMap;
37+
import java.util.Map;
3338
import java.util.Random;
3439

40+
import static com.mongodb.AuthenticationMechanism.SCRAM_SHA_1;
41+
import static com.mongodb.AuthenticationMechanism.SCRAM_SHA_256;
3542
import static com.mongodb.internal.authentication.NativeAuthenticationHelper.createAuthenticationHash;
3643
import static java.lang.String.format;
3744

3845
class ScramShaAuthenticator extends SaslAuthenticator {
3946
private final RandomStringGenerator randomStringGenerator;
4047
private final AuthenticationHashGenerator authenticationHashGenerator;
48+
4149
private static final int MINIMUM_ITERATION_COUNT = 4096;
4250

4351
ScramShaAuthenticator(final MongoCredential credential) {
44-
this(credential, new DefaultRandomStringGenerator(), AUTHENTICATION_HASH_GENERATOR);
52+
this(credential, new DefaultRandomStringGenerator(), getAuthenicationHashGenerator(credential.getAuthenticationMechanism()));
4553
}
4654

4755
ScramShaAuthenticator(final MongoCredential credential, final RandomStringGenerator randomStringGenerator) {
48-
this(credential, randomStringGenerator, AUTHENTICATION_HASH_GENERATOR);
56+
this(credential, randomStringGenerator, getAuthenicationHashGenerator(credential.getAuthenticationMechanism()));
4957
}
5058

5159
ScramShaAuthenticator(final MongoCredential credential, final RandomStringGenerator randomStringGenerator,
@@ -87,8 +95,13 @@ static class ScramShaSaslClient implements SaslClient {
8795
this.credential = credential;
8896
this.randomStringGenerator = randomStringGenerator;
8997
this.authenticationHashGenerator = authenticationHashGenerator;
90-
hAlgorithm = "SHA-1";
91-
hmacAlgorithm = "HmacSHA1";
98+
if (credential.getAuthenticationMechanism().equals(SCRAM_SHA_1)) {
99+
hAlgorithm = "SHA-1";
100+
hmacAlgorithm = "HmacSHA1";
101+
} else {
102+
hAlgorithm = "SHA-256";
103+
hmacAlgorithm = "HmacSHA256";
104+
}
92105
}
93106

94107
public String getMechanismName() {
@@ -122,7 +135,7 @@ private byte[] validateServerSignature(final byte[] challenge) throws SaslExcept
122135
}
123136

124137
public boolean isComplete() {
125-
return step == 3;
138+
return step == 2;
126139
}
127140

128141
public byte[] unwrap(final byte[] incoming, final int offset, final int len) {
@@ -280,11 +293,19 @@ private HashMap<String, String> parseServerResponse(final String response) {
280293
}
281294

282295
private String getUserName() {
283-
return credential.getUserName().replace("=", "=3D").replace(",", "=2C");
296+
String userName = credential.getUserName().replace("=", "=3D").replace(",", "=2C");
297+
if (credential.getAuthenticationMechanism() == SCRAM_SHA_256) {
298+
userName = SaslPrep.saslPrepStored(userName);
299+
}
300+
return userName;
284301
}
285302

286303
private String getAuthenicationHash() {
287-
return authenticationHashGenerator.generate(credential);
304+
String password = authenticationHashGenerator.generate(credential);
305+
if (credential.getAuthenticationMechanism() == SCRAM_SHA_256) {
306+
password = SaslPrep.saslPrepStored(password);
307+
}
308+
return password;
288309
}
289310

290311
private byte[] xorInPlace(final byte[] a, final byte[] b) {
@@ -330,7 +351,16 @@ public String generate(final int length) {
330351
}
331352
}
332353

333-
private static final AuthenticationHashGenerator AUTHENTICATION_HASH_GENERATOR = new AuthenticationHashGenerator() {
354+
private static final AuthenticationHashGenerator DEFAULT_AUTHENTICATION_HASH_GENERATOR = new AuthenticationHashGenerator() {
355+
// Suppress warning of MongoCredential#getAuthenicationHash possibly returning null
356+
@SuppressWarnings("ConstantConditions")
357+
@Override
358+
public String generate(final MongoCredential credential) {
359+
return new String(credential.getPassword());
360+
}
361+
};
362+
363+
private static final AuthenticationHashGenerator LEGACY_AUTHENTICATION_HASH_GENERATOR = new AuthenticationHashGenerator() {
334364
// Suppress warning of MongoCredential#getAuthenicationHash possibly returning null
335365
@SuppressWarnings("ConstantConditions")
336366
@Override
@@ -339,4 +369,8 @@ public String generate(final MongoCredential credential) {
339369
return createAuthenticationHash(credential.getUserName(), credential.getPassword());
340370
}
341371
};
372+
373+
private static AuthenticationHashGenerator getAuthenicationHashGenerator(final AuthenticationMechanism authenticationMechanism) {
374+
return authenticationMechanism == SCRAM_SHA_1 ? LEGACY_AUTHENTICATION_HASH_GENERATOR : DEFAULT_AUTHENTICATION_HASH_GENERATOR;
375+
}
342376
}

0 commit comments

Comments
 (0)