/* * JBoss, Home of Professional Open Source. * Copyright 2014 Red Hat, Inc., and individual contributors * as indicated by the @author tags. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.wildfly.security.sasl.digest; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.net.URI; import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.HashMap; import java.util.Map; import javax.security.auth.callback.CallbackHandler; import javax.security.sasl.Sasl; import javax.security.sasl.SaslClient; import javax.security.sasl.SaslException; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.wildfly.security.auth.client.AuthenticationConfiguration; import org.wildfly.security.auth.client.AuthenticationContext; import org.wildfly.security.auth.client.ClientUtils; import org.wildfly.security.auth.client.MatchRule; import org.wildfly.security.auth.server.IdentityCredentials; import org.wildfly.security.credential.PasswordCredential; import org.wildfly.security.credential.store.CredentialStore; import org.wildfly.security.credential.store.CredentialStoreBuilder; import org.wildfly.security.credential.store.impl.KeyStoreCredentialStore; import org.wildfly.security.password.interfaces.ClearPassword; import org.wildfly.security.sasl.test.BaseTestCase; import org.wildfly.security.sasl.util.SaslMechanismInformation; import org.wildfly.security.util.ByteIterator; import org.wildfly.security.util.CodePointIterator; import mockit.Mock; import mockit.MockUp; import mockit.integration.junit4.JMockit; /** * Test of client side of the Digest mechanism. * JMockit ensure same generated nonce in every test run. * * @author <a href="mailto:jkalina@redhat.com">Jan Kalina</a> */ @RunWith(JMockit.class) public class CompatibilityClientTest extends BaseTestCase { /** mechanism name */ protected static final String DIGEST = "DIGEST-MD5"; /** QoP property name */ protected static final String QOP_PROPERTY = "javax.security.sasl.qop"; private SaslClient client; private void mockNonce(final String nonce){ new MockUp<DigestSaslClient>(){ @Mock byte[] generateNonce(){ return nonce.getBytes(StandardCharsets.UTF_8); } }; } private static String CS_FILE_NAME = "target/" + CompatibilityClientTest.class.getSimpleName() + ".cs"; /** * Setup method to create required KeystorePassword for later use by tests. * @throws Exception if something goes wrong */ @BeforeClass public static void setupCredentialStore() throws Exception { // setup credential store that need to be complete before a test starts CredentialStoreBuilder.get().setKeyStoreFile(CS_FILE_NAME) .setKeyStorePassword("secret_store_1") .addPassword("chris_pwd_alias", "secret") .build(); } /** * Test communication by first example in RFC 2831 [page 18] * classic version * @throws Exception if something goes wrong */ @Test public void testRfc2831example1Classic() throws Exception { testRfc2831example1(false); } /** * Test communication by first example in RFC 2831 [page 18] using Credential Store * {@code CredentialStore} version * @throws Exception if something goes wrong */ @Test public void testRfc2831example1CredentialStore() throws Exception { testRfc2831example1(true); } /** * Test communication by first example in RFC 2831 [page 18] * @param useCredentialStore set to true to use CredentialStore * @throws Exception if something goes wrong */ private void testRfc2831example1(boolean useCredentialStore) throws Exception { mockNonce("OA6MHXh6VqTrRk"); CallbackHandler clientCallback; Map<String, Object> clientProps = new HashMap<>(); if (useCredentialStore) { clientCallback = createCredentialStoreBasedClientCallbackHandler("chris", null, "testRfc2831example1", "chris_pwd_alias", CS_FILE_NAME, "secret_store_1", "secret_key_1"); } else { clientCallback = createClientCallbackHandler("chris", "secret".toCharArray(), null); } SaslClient client = Sasl.createSaslClient(new String[]{DIGEST}, null, "imap", "elwood.innosoft.com", clientProps, clientCallback); assertNotNull(client); assertFalse(client.isComplete()); byte[] message1 = "realm=\"elwood.innosoft.com\",nonce=\"OA6MG9tEQGm2hh\",qop=\"auth\",algorithm=md5-sess,charset=utf-8".getBytes(StandardCharsets.UTF_8); byte[] message2 = client.evaluateChallenge(message1); assertEquals("charset=utf-8,username=\"chris\",realm=\"elwood.innosoft.com\",nonce=\"OA6MG9tEQGm2hh\",nc=00000001,cnonce=\"OA6MHXh6VqTrRk\",digest-uri=\"imap/elwood.innosoft.com\",maxbuf=65536,response=d388dad90d4bbd760a152321f2143af7,qop=auth", new String(message2, "UTF-8")); assertFalse(client.isComplete()); byte[] message3 = "rspauth=ea40f60335c427b5527b84dbabcdfffd".getBytes(StandardCharsets.UTF_8); byte[] message4 = client.evaluateChallenge(message3); assertEquals(null, message4); assertTrue(client.isComplete()); } /** * Test communication by second example in RFC 2831 [page 18] * classic version * @throws Exception if something goes wrong */ @Test public void testRfc2831example2Classic() throws Exception { testRfc2831example2(false); } /** * Test communication by second example in RFC 2831 [page 18] * {@code CredentialStore} version * @throws Exception if something goes wrong */ @Test public void testRfc2831example2CredentialStore() throws Exception { testRfc2831example2(true); } /** * Test communication by second example in RFC 2831 [page 18] * @param useCredentialStore set to true to use CredentialStore * @throws Exception if something goes wrong */ private void testRfc2831example2(boolean useCredentialStore) throws Exception { mockNonce("OA9BSuZWMSpW8m"); CallbackHandler clientCallback; Map<String, Object> clientProps = new HashMap<>(); if (useCredentialStore) { clientCallback = createCredentialStoreBasedClientCallbackHandler("chris", null, "testRfc2831example2", "chris_pwd_alias", CS_FILE_NAME, "secret_store_1", "secret_key_1"); } else { clientCallback = createClientCallbackHandler("chris", "secret".toCharArray(), null); } client = Sasl.createSaslClient(new String[] { DIGEST }, null, "acap", "elwood.innosoft.com", clientProps, clientCallback); assertFalse(client.hasInitialResponse()); assertFalse(client.isComplete()); byte[] message1 = "realm=\"elwood.innosoft.com\",nonce=\"OA9BSXrbuRhWay\",qop=\"auth\",algorithm=md5-sess,charset=utf-8".getBytes(StandardCharsets.UTF_8); byte[] message2 = client.evaluateChallenge(message1); assertEquals("charset=utf-8,username=\"chris\",realm=\"elwood.innosoft.com\",nonce=\"OA9BSXrbuRhWay\",nc=00000001,cnonce=\"OA9BSuZWMSpW8m\",digest-uri=\"acap/elwood.innosoft.com\",maxbuf=65536,response=6084c6db3fede7352c551284490fd0fc,qop=auth", new String(message2, "UTF-8")); assertFalse(client.isComplete()); byte[] message3 = "rspauth=2f0b3d7c3c2e486600ef710726aa2eae".getBytes(StandardCharsets.UTF_8); byte[] message4 = client.evaluateChallenge(message3); assertEquals(null, message4); assertTrue(client.isComplete()); } /** * Test with authorization ID (authzid) - authorized * classic version * @throws Exception if something goes wrong */ @Test public void testAuthorizedAuthorizationIdClassic() throws Exception { testAuthorizedAuthorizationId(false); } /** * Test with authorization ID (authzid) - authorized * {@code CredentialStore} version * @throws Exception if something goes wrong */ @Test public void testAuthorizedAuthorizationIdCredentialStore() throws Exception { testAuthorizedAuthorizationId(true); } /** * Test with authorization ID (authzid) - authorized * @param useCredentialStore set to true to use CredentialStore * @throws Exception if something goes wrong */ private void testAuthorizedAuthorizationId(boolean useCredentialStore) throws Exception { mockNonce("OA9BSuZWMSpW8m"); CallbackHandler clientCallback; Map<String, Object> clientProps = new HashMap<>(); if (useCredentialStore) { clientCallback = createCredentialStoreBasedClientCallbackHandler("chris", null, "testAuthorizedAuthorizationId", "chris_pwd_alias", CS_FILE_NAME, "secret_store_1", "secret_key_1"); } else { clientCallback = createClientCallbackHandler("chris", "secret".toCharArray(), null); } client = Sasl.createSaslClient(new String[] { DIGEST }, "chris", "acap", "elwood.innosoft.com", clientProps, clientCallback); assertFalse(client.hasInitialResponse()); assertFalse(client.isComplete()); byte[] message1 = "realm=\"elwood.innosoft.com\",nonce=\"OA9BSXrbuRhWay\",qop=\"auth\",algorithm=md5-sess,charset=utf-8".getBytes(StandardCharsets.UTF_8); byte[] message2 = client.evaluateChallenge(message1); assertEquals("charset=utf-8,username=\"chris\",realm=\"elwood.innosoft.com\",nonce=\"OA9BSXrbuRhWay\",nc=00000001,cnonce=\"OA9BSuZWMSpW8m\",digest-uri=\"acap/elwood.innosoft.com\",maxbuf=65536,response=aa4e81f1c6656350f7bce05d436665de,qop=auth,authzid=\"chris\"", new String(message2, "UTF-8")); assertFalse(client.isComplete()); byte[] message3 = "rspauth=af3ca83a805d4cfa00675a17315475c4".getBytes(StandardCharsets.UTF_8); byte[] message4 = client.evaluateChallenge(message3); assertEquals(null, message4); assertTrue(client.isComplete()); } /** * Test with authentication plus integrity protection (qop=auth-int) * classic version * @throws Exception if something goes wrong */ @Test public void testQopAuthIntClassic() throws Exception { testQopAuthInt(false); } /** * Test with authentication plus integrity protection (qop=auth-int) * {@code CredentialStore} version * @throws Exception if something goes wrong */ @Test public void testQopAuthIntCredentialStore() throws Exception { testQopAuthInt(true); } /** * Test with authentication plus integrity protection (qop=auth-int) * @param useCredentialStore set to true to use CredentialStore * @throws Exception if something goes wrong */ private void testQopAuthInt(boolean useCredentialStore) throws Exception { mockNonce("OA9BSuZWMSpW8m"); CallbackHandler clientCallback; Map<String, Object> clientProps = new HashMap<>(); clientProps.put(QOP_PROPERTY, "auth-int"); if (useCredentialStore) { clientCallback = createCredentialStoreBasedClientCallbackHandler("chris", null, "testAuthorizedAuthorizationId", "chris_pwd_alias", CS_FILE_NAME, "secret_store_1", "secret_key_1"); } else { clientCallback = createClientCallbackHandler("chris", "secret".toCharArray(), null); } client = Sasl.createSaslClient(new String[] { DIGEST }, "chris", "acap", "elwood.innosoft.com", clientProps, clientCallback); assertFalse(client.hasInitialResponse()); assertFalse(client.isComplete()); byte[] message1 = "realm=\"elwood.innosoft.com\",nonce=\"OA9BSXrbuRhWay\",qop=\"auth-int\",charset=utf-8,algorithm=md5-sess".getBytes(StandardCharsets.UTF_8); byte[] message2 = client.evaluateChallenge(message1); assertEquals("charset=utf-8,username=\"chris\",realm=\"elwood.innosoft.com\",nonce=\"OA9BSXrbuRhWay\",nc=00000001,cnonce=\"OA9BSuZWMSpW8m\",digest-uri=\"acap/elwood.innosoft.com\",maxbuf=65536,response=d8b17f55b410208c6ebb22f89f9d6cbb,qop=auth-int,authzid=\"chris\"", new String(message2, "UTF-8")); assertFalse(client.isComplete()); byte[] message3 = "rspauth=7a8794654d6d6de607e9143d52b554a8".getBytes(StandardCharsets.UTF_8); byte[] message4 = client.evaluateChallenge(message3); assertEquals(null, message4); assertTrue(client.isComplete()); byte[] outcoming1 = CodePointIterator.ofString("11223344").hexDecode().drain(); byte[] outcoming1wrapped = client.wrap(outcoming1, 0, outcoming1.length); assertEquals("1122334499191be7952a49d8549b000100000000", ByteIterator.ofBytes(outcoming1wrapped).hexEncode().drainToString()); byte[] incoming1 = CodePointIterator.ofString("55667788cf5e02ad15987d9076b8000100000000").hexDecode().drain(); byte[] incoming1unwrapped = client.unwrap(incoming1, 0, incoming1.length); assertEquals("55667788", ByteIterator.ofBytes(incoming1unwrapped).hexEncode().drainToString()); byte[] outcoming2 = CodePointIterator.ofString("aabbcc").hexDecode().drain(); byte[] outcoming2wrapped = client.wrap(outcoming2, 0, outcoming2.length); assertEquals("aabbcc7e845ed48b0474447543000100000001", ByteIterator.ofBytes(outcoming2wrapped).hexEncode().drainToString()); byte[] incoming2 = new byte[0]; byte[] incoming2unwrapped = client.unwrap(incoming2, 0, incoming2.length); assertEquals("", ByteIterator.ofBytes(incoming2unwrapped).hexEncode().drainToString()); // MAC not corresponds to message and sequence number byte[] incoming3 = CodePointIterator.ofString("016603ce7148b6869e1b8df557000100000001").hexDecode().drain(); byte[] incoming3unwrapped = client.unwrap(incoming3, 0, incoming3.length); assertEquals("", ByteIterator.ofBytes(incoming3unwrapped).hexEncode().drainToString()); // bad sequence number try { byte[] incoming4 = CodePointIterator.ofString("01020352873023be5e875d6a93000100000002").hexDecode().drain(); client.unwrap(incoming4, 0, incoming4.length); fail("Out of order sequencing SaslException expected!"); } catch(SaslException e){} } /** * Test with authentication plus integrity and confidentiality protection (qop=auth-conf, cipher=default=3des) * @throws Exception if something goes wrong */ @Test public void testQopAuthConf() throws Exception { mockNonce("OA9BSuZWMSpW8m"); CallbackHandler clientCallback = createClientCallbackHandler("chris", "secret".toCharArray(), null); Map<String, Object> clientProps = new HashMap<>(); clientProps.put(QOP_PROPERTY, "auth-conf"); client = Sasl.createSaslClient(new String[] { DIGEST }, "chris", "acap", "elwood.innosoft.com", clientProps, clientCallback); assertFalse(client.hasInitialResponse()); assertFalse(client.isComplete()); byte[] message1 = "realm=\"elwood.innosoft.com\",nonce=\"OA9BSXrbuRhWay\",qop=\"auth-conf\",charset=utf-8,cipher=\"3des,rc4,des,rc4-56,rc4-40\",algorithm=md5-sess".getBytes(StandardCharsets.UTF_8); byte[] message2 = client.evaluateChallenge(message1); assertEquals("charset=utf-8,username=\"chris\",realm=\"elwood.innosoft.com\",nonce=\"OA9BSXrbuRhWay\",nc=00000001,cnonce=\"OA9BSuZWMSpW8m\",digest-uri=\"acap/elwood.innosoft.com\",maxbuf=65536,response=4520cf48234bb93b95548a25cd56601b,qop=auth-conf,cipher=\"3des\",authzid=\"chris\"", new String(message2, "UTF-8")); assertFalse(client.isComplete()); byte[] message3 = "rspauth=a804fda66588e2d911bbacd1b1163bc1".getBytes(StandardCharsets.UTF_8); byte[] message4 = client.evaluateChallenge(message3); assertEquals(null, message4); assertTrue(client.isComplete()); byte[] outcoming1 = CodePointIterator.ofString("11223344").hexDecode().drain(); byte[] outcoming1wrapped = client.wrap(outcoming1, 0, outcoming1.length); assertEquals("13f7644f8c783501177522c1a455cb1f000100000000", ByteIterator.ofBytes(outcoming1wrapped).hexEncode().drainToString()); byte[] incoming1 = CodePointIterator.ofString("93ce33409e0fe5187e07c16fc3041f64000100000000").hexDecode().drain(); byte[] incoming1unwrapped = client.unwrap(incoming1, 0, incoming1.length); assertEquals("55667788", ByteIterator.ofBytes(incoming1unwrapped).hexEncode().drainToString()); byte[] outcoming2 = CodePointIterator.ofString("aabbcc").hexDecode().drain(); byte[] outcoming2wrapped = client.wrap(outcoming2, 0, outcoming2.length); assertEquals("ec426d9cd3276f22285ab5da8df8f26b000100000001", ByteIterator.ofBytes(outcoming2wrapped).hexEncode().drainToString()); byte[] incoming2 = new byte[0]; byte[] incoming2unwrapped = client.unwrap(incoming2, 0, incoming2.length); assertEquals("", ByteIterator.ofBytes(incoming2unwrapped).hexEncode().drainToString()); // bad message byte[] incoming3 = CodePointIterator.ofString("cb8905522a50046ecb969c11a9d72014000100000001").hexDecode().drain(); byte[] incoming3unwrapped = client.unwrap(incoming3, 0, incoming3.length); assertEquals("", ByteIterator.ofBytes(incoming3unwrapped).hexEncode().drainToString()); // bad sequence number try { byte[] incoming4 = CodePointIterator.ofString("b12efd35ef3289f98cf6d98e6547bd3a000100000002").hexDecode().drain(); client.unwrap(incoming4, 0, incoming4.length); fail("Out of order sequencing SaslException expected!"); } catch(SaslException e){} } /** * Test with authentication plus integrity and confidentiality protection (qop=auth-conf, cipher=rc4) * classic version * @throws Exception if something goes wrong */ @Test public void testQopAuthConfRc4Classic() throws Exception { testQopAuthConfRc4(false); } /** * Test with authentication plus integrity and confidentiality protection (qop=auth-conf, cipher=rc4) * {@code CredentialStore} version * @throws Exception if something goes wrong */ @Test public void testQopAuthConfRc4CredentialStore() throws Exception { testQopAuthConfRc4(true); } /** * Test with authentication plus integrity and confidentiality protection (qop=auth-conf, cipher=rc4) * @param useCredentialStore set to true to use CredentialStore * @throws Exception if something goes wrong */ private void testQopAuthConfRc4(boolean useCredentialStore) throws Exception { mockNonce("OA9BSuZWMSpW8m"); CallbackHandler clientCallback; Map<String, Object> clientProps = new HashMap<>(); clientProps.put(QOP_PROPERTY, "auth-conf"); if (useCredentialStore) { clientCallback = createCredentialStoreBasedClientCallbackHandler("chris", null, "testQopAuthConfRc4", "chris_pwd_alias", CS_FILE_NAME, "secret_store_1", "secret_key_1"); } else { clientCallback = createClientCallbackHandler("chris", "secret".toCharArray(), null); } client = Sasl.createSaslClient(new String[] { DIGEST }, "chris", "acap", "elwood.innosoft.com", clientProps, clientCallback); assertFalse(client.hasInitialResponse()); assertFalse(client.isComplete()); byte[] message1 = "realm=\"elwood.innosoft.com\",nonce=\"OA9BSXrbuRhWay\",qop=\"auth-conf\",charset=utf-8,cipher=\"rc4\",algorithm=md5-sess".getBytes(StandardCharsets.UTF_8); byte[] message2 = client.evaluateChallenge(message1); assertEquals("charset=utf-8,username=\"chris\",realm=\"elwood.innosoft.com\",nonce=\"OA9BSXrbuRhWay\",nc=00000001,cnonce=\"OA9BSuZWMSpW8m\",digest-uri=\"acap/elwood.innosoft.com\",maxbuf=65536,response=4520cf48234bb93b95548a25cd56601b,qop=auth-conf,cipher=\"rc4\",authzid=\"chris\"", new String(message2, "UTF-8")); assertFalse(client.isComplete()); byte[] message3 = "rspauth=a804fda66588e2d911bbacd1b1163bc1".getBytes(StandardCharsets.UTF_8); byte[] message4 = client.evaluateChallenge(message3); assertEquals(null, message4); assertTrue(client.isComplete()); byte[] outcoming1 = CodePointIterator.ofString("11223344").hexDecode().drain(); byte[] outcoming1wrapped = client.wrap(outcoming1, 0, outcoming1.length); assertEquals("6a9328ca634e47c8d1ecc3c3f6e6000100000000", ByteIterator.ofBytes(outcoming1wrapped).hexEncode().drainToString()); byte[] incoming1 = CodePointIterator.ofString("9fc7eb1c3c9e04b52df6e347a389000100000000").hexDecode().drain(); byte[] incoming1unwrapped = client.unwrap(incoming1, 0, incoming1.length); assertEquals("55667788", ByteIterator.ofBytes(incoming1unwrapped).hexEncode().drainToString()); byte[] outcoming2 = CodePointIterator.ofString("aabbcc").hexDecode().drain(); byte[] outcoming2wrapped = client.wrap(outcoming2, 0, outcoming2.length); assertEquals("7e15b940fccbb58a5612f54da7000100000001", ByteIterator.ofBytes(outcoming2wrapped).hexEncode().drainToString()); byte[] incoming2 = new byte[0]; byte[] incoming2unwrapped = client.unwrap(incoming2, 0, incoming2.length); assertEquals("", ByteIterator.ofBytes(incoming2unwrapped).hexEncode().drainToString()); // bad message byte[] incoming3 = CodePointIterator.ofString("b0d829402149855796493cdf21000100000001").hexDecode().drain(); byte[] incoming3unwrapped = client.unwrap(incoming3, 0, incoming3.length); assertEquals("", ByteIterator.ofBytes(incoming3unwrapped).hexEncode().drainToString()); // bad sequence number try { byte[] incoming4 = CodePointIterator.ofString("a5a7390698ed8ab7ac667406a3000100000002").hexDecode().drain(); client.unwrap(incoming4, 0, incoming4.length); fail("Out of order sequencing SaslException expected!"); } catch(SaslException e){} } /** * Test with authentication plus integrity and confidentiality protection (qop=auth-conf, cipher=des) * @throws Exception if something goes wrong */ @Test public void testQopAuthConfDes() throws Exception { mockNonce("OA9BSuZWMSpW8m"); CallbackHandler clientCallback = createClientCallbackHandler("chris", "secret".toCharArray(), null); Map<String, Object> clientProps = new HashMap<>(); clientProps.put(QOP_PROPERTY, "auth-conf"); client = Sasl.createSaslClient(new String[] { DIGEST }, "chris", "acap", "elwood.innosoft.com", clientProps, clientCallback); assertFalse(client.hasInitialResponse()); assertFalse(client.isComplete()); byte[] message1 = "realm=\"elwood.innosoft.com\",nonce=\"OA9BSXrbuRhWay\",qop=\"auth-conf\",charset=utf-8,cipher=\"des\",algorithm=md5-sess".getBytes(StandardCharsets.UTF_8); byte[] message2 = client.evaluateChallenge(message1); assertEquals("charset=utf-8,username=\"chris\",realm=\"elwood.innosoft.com\",nonce=\"OA9BSXrbuRhWay\",nc=00000001,cnonce=\"OA9BSuZWMSpW8m\",digest-uri=\"acap/elwood.innosoft.com\",maxbuf=65536,response=4520cf48234bb93b95548a25cd56601b,qop=auth-conf,cipher=\"des\",authzid=\"chris\"", new String(message2, "UTF-8")); assertFalse(client.isComplete()); byte[] message3 = "rspauth=a804fda66588e2d911bbacd1b1163bc1".getBytes(StandardCharsets.UTF_8); byte[] message4 = client.evaluateChallenge(message3); assertEquals(null, message4); assertTrue(client.isComplete()); byte[] outcoming1 = CodePointIterator.ofString("11223344").hexDecode().drain(); byte[] outcoming1wrapped = client.wrap(outcoming1, 0, outcoming1.length); assertEquals("b2a12ba8ccd1030e7da4bac57a224197000100000000", ByteIterator.ofBytes(outcoming1wrapped).hexEncode().drainToString()); byte[] incoming1 = CodePointIterator.ofString("8bc1267e71a769456f0c60f030e13f32000100000000").hexDecode().drain(); byte[] incoming1unwrapped = client.unwrap(incoming1, 0, incoming1.length); assertEquals("55667788", ByteIterator.ofBytes(incoming1unwrapped).hexEncode().drainToString()); byte[] outcoming2 = CodePointIterator.ofString("aabbcc").hexDecode().drain(); byte[] outcoming2wrapped = client.wrap(outcoming2, 0, outcoming2.length); assertEquals("13144fc90ca65d3838d3547cca43e8ad000100000001", ByteIterator.ofBytes(outcoming2wrapped).hexEncode().drainToString()); byte[] incoming2 = new byte[0]; byte[] incoming2unwrapped = client.unwrap(incoming2, 0, incoming2.length); assertEquals("", ByteIterator.ofBytes(incoming2unwrapped).hexEncode().drainToString()); // bad message byte[] incoming3 = CodePointIterator.ofString("54d717857f511fb1964a723e08bf810c000100000001").hexDecode().drain(); byte[] incoming3unwrapped = client.unwrap(incoming3, 0, incoming3.length); assertEquals("", ByteIterator.ofBytes(incoming3unwrapped).hexEncode().drainToString()); // bad sequence number try { byte[] incoming4 = CodePointIterator.ofString("44dd10b5277ee6c7de87cd0c3acacfad000100000002").hexDecode().drain(); client.unwrap(incoming4, 0, incoming4.length); fail("Out of order sequencing SaslException expected!"); } catch(SaslException e){} } /** * Test with authentication plus integrity and confidentiality protection (qop=auth-conf, cipher=rc4-56) * @throws Exception if something goes wrong */ @Test public void testQopAuthConfRc456() throws Exception { mockNonce("OA9BSuZWMSpW8m"); CallbackHandler clientCallback = createClientCallbackHandler("chris", "secret".toCharArray(), null); Map<String, Object> clientProps = new HashMap<>(); clientProps.put(QOP_PROPERTY, "auth-conf"); client = Sasl.createSaslClient(new String[] { DIGEST }, "chris", "acap", "elwood.innosoft.com", clientProps, clientCallback); assertFalse(client.hasInitialResponse()); assertFalse(client.isComplete()); byte[] message1 = "realm=\"elwood.innosoft.com\",nonce=\"OA9BSXrbuRhWay\",qop=\"auth-conf\",charset=utf-8,cipher=\"rc4-56\",algorithm=md5-sess".getBytes(StandardCharsets.UTF_8); byte[] message2 = client.evaluateChallenge(message1); assertEquals("charset=utf-8,username=\"chris\",realm=\"elwood.innosoft.com\",nonce=\"OA9BSXrbuRhWay\",nc=00000001,cnonce=\"OA9BSuZWMSpW8m\",digest-uri=\"acap/elwood.innosoft.com\",maxbuf=65536,response=4520cf48234bb93b95548a25cd56601b,qop=auth-conf,cipher=\"rc4-56\",authzid=\"chris\"", new String(message2, "UTF-8")); assertFalse(client.isComplete()); byte[] message3 = "rspauth=a804fda66588e2d911bbacd1b1163bc1".getBytes(StandardCharsets.UTF_8); byte[] message4 = client.evaluateChallenge(message3); assertEquals(null, message4); assertTrue(client.isComplete()); byte[] outcoming1 = CodePointIterator.ofString("11223344").hexDecode().drain(); byte[] outcoming1wrapped = client.wrap(outcoming1, 0, outcoming1.length); assertEquals("7a77c4b8b20208e502e5dc09bbfc000100000000", ByteIterator.ofBytes(outcoming1wrapped).hexEncode().drainToString()); byte[] incoming1 = CodePointIterator.ofString("c10acbf737cdebf2298df53417bc000100000000").hexDecode().drain(); byte[] incoming1unwrapped = client.unwrap(incoming1, 0, incoming1.length); assertEquals("55667788", ByteIterator.ofBytes(incoming1unwrapped).hexEncode().drainToString()); byte[] outcoming2 = CodePointIterator.ofString("aabbcc").hexDecode().drain(); byte[] outcoming2wrapped = client.wrap(outcoming2, 0, outcoming2.length); assertEquals("efcb8662925427788b0ffeab2c000100000001", ByteIterator.ofBytes(outcoming2wrapped).hexEncode().drainToString()); byte[] incoming2 = new byte[0]; byte[] incoming2unwrapped = client.unwrap(incoming2, 0, incoming2.length); assertEquals("", ByteIterator.ofBytes(incoming2unwrapped).hexEncode().drainToString()); // bad message byte[] incoming3 = CodePointIterator.ofString("b18150d7204da90f0f733e3f73000100000001").hexDecode().drain(); byte[] incoming3unwrapped = client.unwrap(incoming3, 0, incoming3.length); assertEquals("", ByteIterator.ofBytes(incoming3unwrapped).hexEncode().drainToString()); // bad sequence number try { byte[] incoming4 = CodePointIterator.ofString("ed5cc6b9058c9e5f3a175cdcbf000100000002").hexDecode().drain(); client.unwrap(incoming4, 0, incoming4.length); fail("Out of order sequencing SaslException expected!"); } catch(SaslException e){} } /** * Test with authentication plus integrity and confidentiality protection (qop=auth-conf, cipher=rc4-40) * @throws Exception if something goes wrong */ @Test public void testQopAuthConfRc440Classic() throws Exception { testQopAuthConfRc440(false); } /** * Test with authentication plus integrity and confidentiality protection (qop=auth-conf, cipher=rc4-40) * @throws Exception if something goes wrong */ @Test public void testQopAuthConfRc440CredentialStore() throws Exception { testQopAuthConfRc440(true); } /** * Test with authentication plus integrity and confidentiality protection (qop=auth-conf, cipher=rc4-40) * @param useCredentialStore set to true to use CredentialStore * @throws Exception if something goes wrong */ public void testQopAuthConfRc440(boolean useCredentialStore) throws Exception { mockNonce("OA9BSuZWMSpW8m"); CallbackHandler clientCallback; Map<String, Object> clientProps = new HashMap<>(); clientProps.put(QOP_PROPERTY, "auth-conf"); if (useCredentialStore) { clientCallback = createCredentialStoreBasedClientCallbackHandler("chris", null, "testQopAuthConfRc4", "chris_pwd_alias", CS_FILE_NAME, "secret_store_1", "secret_key_1"); } else { clientCallback = createClientCallbackHandler("chris", "secret".toCharArray(), null); } client = Sasl.createSaslClient(new String[] { DIGEST }, "chris", "acap", "elwood.innosoft.com", clientProps, clientCallback); assertFalse(client.hasInitialResponse()); assertFalse(client.isComplete()); byte[] message1 = "realm=\"elwood.innosoft.com\",nonce=\"OA9BSXrbuRhWay\",qop=\"auth-conf\",charset=utf-8,cipher=\"rc4-40\",algorithm=md5-sess".getBytes(StandardCharsets.UTF_8); byte[] message2 = client.evaluateChallenge(message1); assertEquals("charset=utf-8,username=\"chris\",realm=\"elwood.innosoft.com\",nonce=\"OA9BSXrbuRhWay\",nc=00000001,cnonce=\"OA9BSuZWMSpW8m\",digest-uri=\"acap/elwood.innosoft.com\",maxbuf=65536,response=4520cf48234bb93b95548a25cd56601b,qop=auth-conf,cipher=\"rc4-40\",authzid=\"chris\"", new String(message2, "UTF-8")); assertFalse(client.isComplete()); byte[] message3 = "rspauth=a804fda66588e2d911bbacd1b1163bc1".getBytes(StandardCharsets.UTF_8); byte[] message4 = client.evaluateChallenge(message3); assertEquals(null, message4); assertTrue(client.isComplete()); byte[] outcoming1 = CodePointIterator.ofString("11223344").hexDecode().drain(); byte[] outcoming1wrapped = client.wrap(outcoming1, 0, outcoming1.length); assertEquals("ed46c6b0d38acb719aad661f9625000100000000", ByteIterator.ofBytes(outcoming1wrapped).hexEncode().drainToString()); byte[] incoming1 = CodePointIterator.ofString("44aca6145a89353d26258e524724000100000000").hexDecode().drain(); byte[] incoming1unwrapped = client.unwrap(incoming1, 0, incoming1.length); assertEquals("55667788", ByteIterator.ofBytes(incoming1unwrapped).hexEncode().drainToString()); byte[] outcoming2 = CodePointIterator.ofString("aabbcc").hexDecode().drain(); byte[] outcoming2wrapped = client.wrap(outcoming2, 0, outcoming2.length); assertEquals("b7bdc8f08733182154289e7f3d000100000001", ByteIterator.ofBytes(outcoming2wrapped).hexEncode().drainToString()); byte[] incoming2 = new byte[0]; byte[] incoming2unwrapped = client.unwrap(incoming2, 0, incoming2.length); assertEquals("", ByteIterator.ofBytes(incoming2unwrapped).hexEncode().drainToString()); // bad message byte[] incoming3 = CodePointIterator.ofString("685082d4671e03ac60df93d1b9000100000001").hexDecode().drain(); byte[] incoming3unwrapped = client.unwrap(incoming3, 0, incoming3.length); assertEquals("", ByteIterator.ofBytes(incoming3unwrapped).hexEncode().drainToString()); // bad sequence number try { byte[] incoming4 = CodePointIterator.ofString("c7b5198826c7066b48e474db0c000100000002").hexDecode().drain(); client.unwrap(incoming4, 0, incoming4.length); fail("Out of order sequencing SaslException expected!"); } catch(SaslException e){} } /** * Test with authentication plus integrity and confidentiality protection (qop=auth-conf, cipher=unknown) * @throws Exception if something goes wrong */ @Test public void testQopAuthConfUnknown() throws Exception { mockNonce("OA9BSuZWMSpW8m"); CallbackHandler clientCallback = createClientCallbackHandler("chris", "secret".toCharArray(), null); Map<String, Object> clientProps = new HashMap<>(); clientProps.put(QOP_PROPERTY, "auth-conf"); client = Sasl.createSaslClient(new String[] { DIGEST }, "chris", "acap", "elwood.innosoft.com", clientProps, clientCallback); assertFalse(client.hasInitialResponse()); assertFalse(client.isComplete()); byte[] message1 = "realm=\"elwood.innosoft.com\",nonce=\"OA9BSXrbuRhWay\",qop=\"auth-conf\",charset=utf-8,cipher=\"unknown\",algorithm=md5-sess".getBytes(StandardCharsets.UTF_8); try{ client.evaluateChallenge(message1); fail("Not thrown SaslException!"); } catch (SaslException e) {} assertFalse(client.isComplete()); } /** * More realms from server (realm="other-realm",realm="elwood.innosoft.com",realm="next-realm" -> elwood.innosoft.com) * @throws Exception if something goes wrong */ @Test public void testMoreRealmsFromServer() throws Exception { mockNonce("OA6MHXh6VqTrRk"); CallbackHandler clientCallback = createClientCallbackHandler("chris", "secret".toCharArray(), "elwood.innosoft.com"); client = Sasl.createSaslClient(new String[] { DIGEST }, null, "imap", "elwood.innosoft.com", Collections.<String, Object> emptyMap(), clientCallback); assertFalse(client.isComplete()); byte[] message1 = "realm=\"other-realm\",realm=\"elwood.innosoft.com\",realm=\"next-realm\",nonce=\"OA6MG9tEQGm2hh\",qop=\"auth\",algorithm=md5-sess,charset=utf-8".getBytes(StandardCharsets.UTF_8); byte[] message2 = client.evaluateChallenge(message1); assertEquals("charset=utf-8,username=\"chris\",realm=\"elwood.innosoft.com\",nonce=\"OA6MG9tEQGm2hh\",nc=00000001,cnonce=\"OA6MHXh6VqTrRk\",digest-uri=\"imap/elwood.innosoft.com\",maxbuf=65536,response=d388dad90d4bbd760a152321f2143af7,qop=auth", new String(message2, "UTF-8")); assertFalse(client.isComplete()); byte[] message3 = "rspauth=ea40f60335c427b5527b84dbabcdfffd".getBytes(StandardCharsets.UTF_8); byte[] message4 = client.evaluateChallenge(message3); assertEquals(null, message4); assertTrue(client.isComplete()); } /** * No realms from server * @throws Exception if something goes wrong */ @Test public void testNoRealmsFromServer() throws Exception { mockNonce("OA6MHXh6VqTrRk"); CallbackHandler clientCallback = createClientCallbackHandler("chris", "secret".toCharArray(), null); client = Sasl.createSaslClient(new String[] { DIGEST }, null, "imap", "elwood.innosoft.com", Collections.<String, Object> emptyMap(), clientCallback); assertFalse(client.isComplete()); byte[] message1 = "nonce=\"OA6MG9tEQGm2hh\",qop=\"auth\",algorithm=md5-sess,charset=utf-8".getBytes(StandardCharsets.UTF_8); byte[] message2 = client.evaluateChallenge(message1); assertEquals("charset=utf-8,username=\"chris\",nonce=\"OA6MG9tEQGm2hh\",nc=00000001,cnonce=\"OA6MHXh6VqTrRk\",digest-uri=\"imap/elwood.innosoft.com\",maxbuf=65536,response=695dcc815019923b9d438fd28c641aa9,qop=auth", new String(message2, "UTF-8")); assertFalse(client.isComplete()); byte[] message3 = "rspauth=ef0a550cd88d926ff426790bef156af3".getBytes(StandardCharsets.UTF_8); byte[] message4 = client.evaluateChallenge(message3); assertEquals(null, message4); assertTrue(client.isComplete()); } /** * No server nonce * @throws Exception if something goes wrong */ @Test public void testNoServerNonce() throws Exception { CallbackHandler clientCallback = createClientCallbackHandler("chris", "secret".toCharArray(), null); client = Sasl.createSaslClient(new String[] { DIGEST }, null, "imap", "elwood.innosoft.com", Collections.<String, Object> emptyMap(), clientCallback); assertFalse(client.isComplete()); byte[] message1 = "qop=\"auth\",algorithm=md5-sess,charset=utf-8".getBytes(StandardCharsets.UTF_8); try{ client.evaluateChallenge(message1); fail("Not thrown SaslException!"); } catch (SaslException e) {} assertFalse(client.isComplete()); } /** * Blank nonce from server (connection with naughty server) * @throws Exception if something goes wrong */ @Test public void testBlankServerNonce() throws Exception { mockNonce("OA6MHXh6VqTrRk"); CallbackHandler clientCallback = createClientCallbackHandler("chris", "secret".toCharArray(), null); client = Sasl.createSaslClient(new String[] { DIGEST }, null, "imap", "elwood.innosoft.com", Collections.<String, Object> emptyMap(), clientCallback); assertFalse(client.isComplete()); byte[] message1 = "nonce=\"\",qop=\"auth\",algorithm=md5-sess,charset=utf-8".getBytes(StandardCharsets.UTF_8); byte[] message2 = client.evaluateChallenge(message1); assertEquals("charset=utf-8,username=\"chris\",nonce=\"\",nc=00000001,cnonce=\"OA6MHXh6VqTrRk\",digest-uri=\"imap/elwood.innosoft.com\",maxbuf=65536,response=c87a63a455fed82d007a7996d49a51bc,qop=auth", new String(message2, "UTF-8")); assertFalse(client.isComplete()); byte[] message3 = "rspauth=fa4e5be53f9b154858fb82d96c93a03a".getBytes(StandardCharsets.UTF_8); byte[] message4 = client.evaluateChallenge(message3); assertEquals(null, message4); assertTrue(client.isComplete()); } /** * Test successful authentication with Unicode chars (UTF-8 encoding) * @throws Exception if something goes wrong */ @Test public void testUtf8Charset() throws Exception { mockNonce("cn\u0438\u4F60\uD83C\uDCA1"); CallbackHandler clientCallback = createClientCallbackHandler("\u0438\u4F60\uD83C\uDCA1", "\u0438\u4F60\uD83C\uDCA1".toCharArray(), null); client = Sasl.createSaslClient(new String[] { DIGEST }, null, "\u0438\u4F60\uD83C\uDCA1", "realm.\u0438\u4F60\uD83C\uDCA1.com", Collections.<String, Object> emptyMap(), clientCallback); assertFalse(client.isComplete()); byte[] message1 = "realm=\"realm.\u0438\u4F60\uD83C\uDCA1.com\",nonce=\"sn\u0438\u4F60\uD83C\uDCA1\",charset=utf-8,algorithm=md5-sess".getBytes(StandardCharsets.UTF_8); byte[] message2 = client.evaluateChallenge(message1); assertEquals("charset=utf-8,username=\"\u0438\u4F60\uD83C\uDCA1\",realm=\"realm.\u0438\u4F60\uD83C\uDCA1.com\",nonce=\"sn\u0438\u4F60\uD83C\uDCA1\",nc=00000001,cnonce=\"cn\u0438\u4F60\uD83C\uDCA1\",digest-uri=\"\u0438\u4F60\uD83C\uDCA1/realm.\u0438\u4F60\uD83C\uDCA1.com\",maxbuf=65536,response=420939e06d2d748c157c5e33499b41a9,qop=auth", new String(message2, StandardCharsets.UTF_8)); assertFalse(client.isComplete()); byte[] message3 = "rspauth=9c4d137545617ba98c11aaea939b4381".getBytes(StandardCharsets.UTF_8); byte[] message4 = client.evaluateChallenge(message3); assertEquals(null, message4); assertTrue(client.isComplete()); } /** * Test successful authentication with escaped realms delimiters * @throws Exception if something goes wrong */ @Test public void testMoreRealmsWithEscapedDelimiters() throws Exception { mockNonce("OA6MHXh6VqTrRk"); CallbackHandler clientCallback = createClientCallbackHandler("chris", "secret".toCharArray(), "first realm"); client = Sasl.createSaslClient(new String[] { DIGEST }, null, "protocol name", "server name", Collections.<String, Object> emptyMap(), clientCallback); assertFalse(client.isComplete()); byte[] message1 = "realm=\"first realm\",realm=\"second\\\\ realm\",realm=\" with spaces \",realm=\" \",nonce=\"OA9BSXrbuRhWay\",charset=utf-8,algorithm=md5-sess".getBytes(StandardCharsets.UTF_8); byte[] message2 = client.evaluateChallenge(message1); assertEquals("charset=utf-8,username=\"chris\",realm=\"first realm\",nonce=\"OA9BSXrbuRhWay\",nc=00000001,cnonce=\"OA6MHXh6VqTrRk\",digest-uri=\"protocol name/server name\",maxbuf=65536,response=bf3dd710ee08b05c663456975c156075,qop=auth", new String(message2, "UTF-8")); assertFalse(client.isComplete()); byte[] message3 = "rspauth=05a18aff49b22e373bb91af7396ce345".getBytes(StandardCharsets.UTF_8); byte[] message4 = client.evaluateChallenge(message3); assertEquals(null, message4); assertTrue(client.isComplete()); } /** * Test with wrong step three rspauth * @throws Exception if something goes wrong */ @Test public void testWrongStepThreeRspauth() throws Exception { mockNonce("OA6MHXh6VqTrRk"); CallbackHandler clientCallback = createClientCallbackHandler("chris", "secret".toCharArray(), null); client = Sasl.createSaslClient(new String[] { DIGEST }, null, "imap", "elwood.innosoft.com", Collections.<String, Object> emptyMap(), clientCallback); assertFalse(client.isComplete()); byte[] message1 = "realm=\"elwood.innosoft.com\",nonce=\"OA6MG9tEQGm2hh\",qop=\"auth\",algorithm=md5-sess,charset=utf-8".getBytes(StandardCharsets.UTF_8); byte[] message2 = client.evaluateChallenge(message1); assertEquals("charset=utf-8,username=\"chris\",realm=\"elwood.innosoft.com\",nonce=\"OA6MG9tEQGm2hh\",nc=00000001,cnonce=\"OA6MHXh6VqTrRk\",digest-uri=\"imap/elwood.innosoft.com\",maxbuf=65536,response=d388dad90d4bbd760a152321f2143af7,qop=auth", new String(message2, "UTF-8")); assertFalse(client.isComplete()); byte[] message3 = "rspauth=ab66f60335c427b5527b84dbabcdaacc".getBytes(StandardCharsets.UTF_8); try{ client.evaluateChallenge(message3); fail("Not thrown SaslException!"); } catch (SaslException e) {} assertFalse(client.isComplete()); } /** * Test QOP selection by client (Server allow auth, auth-int, client want 1.auth-conf, 2.auth-int, 3.auth) * @throws Exception if something goes wrong */ @Test public void testQopSelection1() throws Exception { mockNonce("+7HQhcJThEsqZ3gor1hThC5on8hQ3DRP2esrw+km"); Map<String, Object> clientProps = new HashMap<>(); clientProps.put(QOP_PROPERTY, "auth-conf,auth-int,auth"); CallbackHandler clientCallback = createClientCallbackHandler("user", "password".toCharArray(), null); SaslClient client = Sasl.createSaslClient(new String[] { DIGEST }, "user", "TestProtocol", "TestServer", clientProps, clientCallback); assertFalse(client.isComplete()); byte[] message1 = "realm=\"TestServer\",nonce=\"288HcNYUg60jN/kEFYT/HklRVjZA6opb2if8tsja\",qop=\"auth,auth-int\",charset=utf-8,algorithm=md5-sess".getBytes(StandardCharsets.UTF_8); byte[] message2 = client.evaluateChallenge(message1); assertEquals("charset=utf-8,username=\"user\",realm=\"TestServer\",nonce=\"288HcNYUg60jN/kEFYT/HklRVjZA6opb2if8tsja\",nc=00000001,cnonce=\"+7HQhcJThEsqZ3gor1hThC5on8hQ3DRP2esrw+km\",digest-uri=\"TestProtocol/TestServer\",maxbuf=65536,response=663997cd2a9dc34c84240430fb1be16c,qop=auth-int,authzid=\"user\"", new String(message2, "UTF-8")); assertFalse(client.isComplete()); byte[] message3 = "rspauth=b3d6f9165b0bb0972adaa5778b840c3a".getBytes(StandardCharsets.UTF_8); byte[] message4 = client.evaluateChallenge(message3); assertEquals(null, message4); assertTrue(client.isComplete()); } /** * Test QOP selection by client (Server allow auth-int, auth, client want 1.auth-conf, 2.auth, 3.auth-int) * @throws Exception if something goes wrong */ @Test public void testQopSelection2() throws Exception { mockNonce("a7YfTdcWo4L0OeurbYrT9G+01rZiNe6LSWuCSo73"); Map<String, Object> clientProps = new HashMap<>(); clientProps.put(QOP_PROPERTY, "auth-conf,auth,auth-int"); CallbackHandler clientCallback = createClientCallbackHandler("user", "password".toCharArray(), null); SaslClient client = Sasl.createSaslClient(new String[] { DIGEST }, "user", "TestProtocol", "TestServer", clientProps, clientCallback); assertFalse(client.isComplete()); byte[] message1 = "realm=\"TestServer\",nonce=\"QduN0itdkfbx8VqlrWt56ZS7uRhI2Rt3P8bqfsM/\",qop=\"auth-int,auth\",charset=utf-8,algorithm=md5-sess".getBytes(StandardCharsets.UTF_8); byte[] message2 = client.evaluateChallenge(message1); assertEquals("charset=utf-8,username=\"user\",realm=\"TestServer\",nonce=\"QduN0itdkfbx8VqlrWt56ZS7uRhI2Rt3P8bqfsM/\",nc=00000001,cnonce=\"a7YfTdcWo4L0OeurbYrT9G+01rZiNe6LSWuCSo73\",digest-uri=\"TestProtocol/TestServer\",maxbuf=65536,response=636d1e3c3d73e1bfb15f85957720ce35,qop=auth,authzid=\"user\"", new String(message2, "UTF-8")); assertFalse(client.isComplete()); byte[] message3 = "rspauth=a77854059f533745d50abb064b7df938".getBytes(StandardCharsets.UTF_8); byte[] message4 = client.evaluateChallenge(message3); assertEquals(null, message4); assertTrue(client.isComplete()); } /** * Test unsuccessful QOP selection by client (no common QOP) * @throws Exception if something goes wrong */ @Test public void testQopSelectionFail() throws Exception { Map<String, Object> clientProps = new HashMap<>(); clientProps.put(QOP_PROPERTY, "auth-conf"); CallbackHandler clientCallback = createClientCallbackHandler("user", "password".toCharArray(), null); SaslClient client = Sasl.createSaslClient(new String[]{DIGEST}, "user", "TestProtocol", "TestServer", clientProps, clientCallback); assertFalse(client.isComplete()); byte[] message1 = "realm=\"TestServer\",nonce=\"QduN0itdkfbx8VqlrWt56ZS7uRhI2Rt3P8bqfsM/\",qop=\"auth-int,auth\",charset=utf-8,algorithm=md5-sess".getBytes(StandardCharsets.UTF_8); try{ client.evaluateChallenge(message1); fail("Not thrown SaslException!"); } catch (SaslException e) {} assertFalse(client.isComplete()); } private CallbackHandler createClientCallbackHandler(String username, char[] password, String realm) throws Exception { final AuthenticationContext context = AuthenticationContext.empty() .with( MatchRule.ALL, AuthenticationConfiguration.EMPTY .useName(username) .usePassword(password) .useRealm(realm) .allowSaslMechanisms(SaslMechanismInformation.Names.DIGEST_MD5)); return ClientUtils.getCallbackHandler(new URI("doesnot://matter?"), context); } private CallbackHandler createCredentialStoreBasedClientCallbackHandler(String username, String realm, String storeName, String alias, String storeFileName, String storePassword, String keyPassword) throws Exception { final HashMap<String, String> csAttributes = new HashMap<>(); csAttributes.put("location", storeFileName); csAttributes.put("keyStoreType", "JCEKS"); final CredentialStore cs; try { cs = CredentialStore.getInstance(KeyStoreCredentialStore.KEY_STORE_CREDENTIAL_STORE); cs.initialize(csAttributes, new CredentialStore.CredentialSourceProtectionParameter( IdentityCredentials.NONE.withCredential(new PasswordCredential(ClearPassword.createRaw(ClearPassword.ALGORITHM_CLEAR, storePassword.toCharArray()))) )); } catch (Exception e) { throw new RuntimeException(e); } final AuthenticationContext context = AuthenticationContext.empty() .with( MatchRule.ALL, AuthenticationConfiguration.EMPTY .useName(username) .useCredentialStoreEntry(cs, alias) .useRealm(realm) .allowSaslMechanisms(SaslMechanismInformation.Names.DIGEST_MD5) ); return ClientUtils.getCallbackHandler(new URI("doesnot://matter"), context); } }