diff --git a/http/digest/src/main/java/org/wildfly/security/http/digest/DigestAuthenticationMechanism.java b/http/digest/src/main/java/org/wildfly/security/http/digest/DigestAuthenticationMechanism.java index 6cc61c1b258..97f2a53a857 100644 --- a/http/digest/src/main/java/org/wildfly/security/http/digest/DigestAuthenticationMechanism.java +++ b/http/digest/src/main/java/org/wildfly/security/http/digest/DigestAuthenticationMechanism.java @@ -23,6 +23,7 @@ import static org.wildfly.security.http.HttpConstants.AUTHORIZATION; import static org.wildfly.security.http.HttpConstants.BAD_REQUEST; import static org.wildfly.security.http.HttpConstants.CNONCE; +import static org.wildfly.security.http.HttpConstants.DIGEST_NAME; import static org.wildfly.security.http.HttpConstants.NC; import static org.wildfly.security.http.HttpConstants.QOP; import static org.wildfly.security.http.HttpConstants.URI; @@ -69,6 +70,7 @@ import org.wildfly.security.mechanism.AuthenticationMechanismException; import org.wildfly.security.mechanism.digest.DigestQuote; import org.wildfly.security.mechanism.digest.PasswordDigestObtainer; +import org.wildfly.security.password.interfaces.DigestPassword; /** * Implementation of the HTTP DIGEST authentication mechanism as defined in RFC 7616. @@ -326,10 +328,19 @@ private byte[] calculateResponseDigest(MessageDigest messageDigest, byte[] hA1, } private byte[] getH_A1(final MessageDigest messageDigest, final String username, final String messageRealm) throws AuthenticationMechanismException { - PasswordDigestObtainer obtainer = new PasswordDigestObtainer(callbackHandler, username, messageRealm, httpDigest, getMechanismName().toLowerCase(Locale.ROOT), messageDigest, providers, null, true, false); + PasswordDigestObtainer obtainer = new PasswordDigestObtainer(callbackHandler, username, messageRealm, httpDigest, getCredentialAlgorithm(getMechanismName()), messageDigest, providers, null, true, false); return obtainer.handleUserRealmPasswordCallbacks(); } + private String getCredentialAlgorithm(String mechanismName) { + switch (mechanismName) { + case DIGEST_NAME: + return DigestPassword.ALGORITHM_DIGEST_MD5; + default: + return mechanismName.toLowerCase(Locale.ROOT); + } + } + private String convertToken(final String name, final byte[] value) throws AuthenticationMechanismException { if (value == null) { throw httpDigest.mechMissingDirective(name); diff --git a/tests/base/src/test/java/org/wildfly/security/http/digest/DigestAuthenticationMechanismTest.java b/tests/base/src/test/java/org/wildfly/security/http/digest/DigestAuthenticationMechanismTest.java index 5f8964d460b..a38274fe6a5 100644 --- a/tests/base/src/test/java/org/wildfly/security/http/digest/DigestAuthenticationMechanismTest.java +++ b/tests/base/src/test/java/org/wildfly/security/http/digest/DigestAuthenticationMechanismTest.java @@ -259,6 +259,38 @@ public void testSha256WithDigestPassword() throws Exception { Assert.assertEquals(Status.COMPLETE, request2.getResult()); } + @Test + public void testDigestMD5Password() throws Exception { + mockDigestNonce("5TsQWLVdgBdmrQ0XsxbDODV+57QdFR34I9HAbC/RVvkK"); + Map props = new HashMap<>(); + props.put(CONFIG_REALM, "api@example.org"); + props.put("org.wildfly.security.http.validate-digest-uri", "false"); + HttpServerAuthenticationMechanism mechanism = digestFactory.createAuthenticationMechanism(DIGEST_NAME, props, getCallbackHandler("J\u00E4s\u00F8n Doe", "api@example.org", "Secret, or not?", true)); + + TestingHttpServerRequest request1 = new TestingHttpServerRequest(null); + mechanism.evaluateRequest(request1); + Assert.assertEquals(Status.NO_AUTH, request1.getResult()); + TestingHttpServerResponse response = request1.getResponse(); + Assert.assertEquals(UNAUTHORIZED, response.getStatusCode()); + Assert.assertEquals("Digest realm=\"api@example.org\", nonce=\"5TsQWLVdgBdmrQ0XsxbDODV+57QdFR34I9HAbC/RVvkK\", opaque=\"00000000000000000000000000000000\", algorithm=MD5, qop=auth", response.getAuthenticateHeader()); + + TestingHttpServerRequest request2 = new TestingHttpServerRequest(new String[] { + "Digest username*=UTF-8''J%C3%A4s%C3%B8n%20Doe,\n" + + " realm=\"api@example.org\",\n" + + " uri=\"/doe.json\",\n" + + " algorithm=MD5,\n" + + " nonce=\"5TsQWLVdgBdmrQ0XsxbDODV+57QdFR34I9HAbC/RVvkK\",\n" + + " nc=00000001,\n" + + " cnonce=\"NTg6RKcb9boFIAS3KrFK9BGeh+iDa/sm6jUMp2wds69v\",\n" + + " qop=auth,\n" + + " response=\"" + computeDigest("/doe.json", "5TsQWLVdgBdmrQ0XsxbDODV+57QdFR34I9HAbC/RVvkK", "NTg6RKcb9boFIAS3KrFK9BGeh+iDa/sm6jUMp2wds69v", "00000001", "J\u00E4s\u00F8n Doe", "Secret, or not?", "MD5", "api@example.org", "auth", "GET") + "\",\n" + + " opaque=\"00000000000000000000000000000000\",\n" + + " userhash=false" + }); + mechanism.evaluateRequest(request2); + Assert.assertEquals(Status.COMPLETE, request2.getResult()); + } + private String computeDigest(String uri, String nonce, String cnonce, String nc, String username, String password, String algorithm, String realm, String qop, String method) throws NoSuchAlgorithmException { String A1, HashA1, A2, HashA2; MessageDigest md = MessageDigest.getInstance(algorithm); diff --git a/tests/base/src/test/java/org/wildfly/security/http/impl/AbstractBaseHttpTest.java b/tests/base/src/test/java/org/wildfly/security/http/impl/AbstractBaseHttpTest.java index 8926c8240d0..4e7640aca00 100644 --- a/tests/base/src/test/java/org/wildfly/security/http/impl/AbstractBaseHttpTest.java +++ b/tests/base/src/test/java/org/wildfly/security/http/impl/AbstractBaseHttpTest.java @@ -430,11 +430,13 @@ protected CallbackHandler getCallbackHandler(String username, String realm, Stri Assert.assertEquals(username, ((NameCallback) callback).getDefaultName()); } else if (callback instanceof CredentialCallback) { if (useDigestPassword) { - if (! DigestPassword.ALGORITHM_DIGEST_SHA_256.equals(((CredentialCallback) callback).getAlgorithm())) { + String credentialAlgorithm = ((CredentialCallback) callback).getAlgorithm(); + if (! DigestPassword.ALGORITHM_DIGEST_SHA_256.equals(credentialAlgorithm) && + ! DigestPassword.ALGORITHM_DIGEST_MD5.equals(credentialAlgorithm)) { throw new UnsupportedCallbackException(callback); } try { - PasswordFactory factory = PasswordFactory.getInstance(DigestPassword.ALGORITHM_DIGEST_SHA_256, ELYTRON_PASSWORD_PROVIDERS); + PasswordFactory factory = PasswordFactory.getInstance(credentialAlgorithm, ELYTRON_PASSWORD_PROVIDERS); DigestPasswordAlgorithmSpec algorithmSpec = new DigestPasswordAlgorithmSpec(username, realm); EncryptablePasswordSpec encryptableSpec = new EncryptablePasswordSpec(password.toCharArray(), algorithmSpec); DigestPassword digestPassword = (DigestPassword) factory.generatePassword(encryptableSpec);