summaryrefslogtreecommitdiff
path: root/qpid/java/client
diff options
context:
space:
mode:
authorRobert Godfrey <rgodfrey@apache.org>2014-07-06 21:28:20 +0000
committerRobert Godfrey <rgodfrey@apache.org>2014-07-06 21:28:20 +0000
commitb6734fbbbcef795654b243a8cbcc5c39b4547fd1 (patch)
tree7a27166e09f1c1ae9699ef84df6942096c60bb2e /qpid/java/client
parent5dd593582a0d39ac2334a3690ed7d82dae2dc8ec (diff)
downloadqpid-python-b6734fbbbcef795654b243a8cbcc5c39b4547fd1.tar.gz
QPID-5878 : [Java Broker] Add SCRAM-SHA-256 SASL support
git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1608295 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'qpid/java/client')
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpid/client/security/CallbackHandlerRegistry.properties1
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.properties1
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpid/client/security/scram/AbstractScramSaslClient.java350
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpid/client/security/scram/ScramSHA1SaslClient.java309
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpid/client/security/scram/ScramSHA1SaslClientFactory.java4
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpid/client/security/scram/ScramSHA256SaslClient.java34
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpid/client/security/scram/ScramSHA256SaslClientFactory.java61
7 files changed, 452 insertions, 308 deletions
diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/security/CallbackHandlerRegistry.properties b/qpid/java/client/src/main/java/org/apache/qpid/client/security/CallbackHandlerRegistry.properties
index 7573664187..8f02ee2c38 100644
--- a/qpid/java/client/src/main/java/org/apache/qpid/client/security/CallbackHandlerRegistry.properties
+++ b/qpid/java/client/src/main/java/org/apache/qpid/client/security/CallbackHandlerRegistry.properties
@@ -32,4 +32,5 @@ AMQPLAIN.5=org.apache.qpid.client.security.UsernamePasswordCallbackHandler
PLAIN.6=org.apache.qpid.client.security.UsernamePasswordCallbackHandler
ANONYMOUS.7=org.apache.qpid.client.security.UsernamePasswordCallbackHandler
SCRAM-SHA-1.8=org.apache.qpid.client.security.UsernamePasswordCallbackHandler
+SCRAM-SHA-256.9=org.apache.qpid.client.security.UsernamePasswordCallbackHandler
diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.properties b/qpid/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.properties
index fd52935fe5..24a76982ef 100644
--- a/qpid/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.properties
+++ b/qpid/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.properties
@@ -20,3 +20,4 @@ AMQPLAIN=org.apache.qpid.client.security.amqplain.AmqPlainSaslClientFactory
CRAM-MD5-HASHED=org.apache.qpid.client.security.crammd5hashed.CRAMMD5HashedSaslClientFactory
ANONYMOUS=org.apache.qpid.client.security.anonymous.AnonymousSaslClientFactory
SCRAM-SHA-1=org.apache.qpid.client.security.scram.ScramSHA1SaslClientFactory
+SCRAM-SHA-256=org.apache.qpid.client.security.scram.ScramSHA256SaslClientFactory
diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/security/scram/AbstractScramSaslClient.java b/qpid/java/client/src/main/java/org/apache/qpid/client/security/scram/AbstractScramSaslClient.java
new file mode 100644
index 0000000000..1e67567b8b
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpid/client/security/scram/AbstractScramSaslClient.java
@@ -0,0 +1,350 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.qpid.client.security.scram;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.Charset;
+import java.security.InvalidKeyException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+import java.util.UUID;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.sasl.SaslClient;
+import javax.security.sasl.SaslException;
+import javax.xml.bind.DatatypeConverter;
+
+public abstract class AbstractScramSaslClient implements SaslClient
+{
+
+ private static final byte[] INT_1 = new byte[]{0, 0, 0, 1};
+ private static final String GS2_HEADER = "n,,";
+ private static final Charset ASCII = Charset.forName("ASCII");
+
+ private final String _digestName;
+ private final String _hmacName;
+
+ private String _username;
+ private final String _clientNonce = UUID.randomUUID().toString();
+ private String _serverNonce;
+ private byte[] _salt;
+ private int _iterationCount;
+ private String _clientFirstMessageBare;
+ private byte[] _serverSignature;
+
+ enum State
+ {
+ INITIAL,
+ CLIENT_FIRST_SENT,
+ CLIENT_PROOF_SENT,
+ COMPLETE
+ }
+
+ public final String _mechanism;
+
+ private final CallbackHandler _callbackHandler;
+
+ private State _state = State.INITIAL;
+
+ public AbstractScramSaslClient(final CallbackHandler cbh,
+ final String mechanism,
+ final String digestName,
+ final String hmacName)
+ {
+ _callbackHandler = cbh;
+ _mechanism = mechanism;
+ _digestName = digestName;
+ _hmacName = hmacName;
+
+ }
+
+ @Override
+ public String getMechanismName()
+ {
+ return _mechanism;
+ }
+
+ @Override
+ public boolean hasInitialResponse()
+ {
+ return true;
+ }
+
+ @Override
+ public byte[] evaluateChallenge(final byte[] challenge) throws SaslException
+ {
+ byte[] response;
+ switch(_state)
+ {
+ case INITIAL:
+ response = initialResponse();
+ _state = State.CLIENT_FIRST_SENT;
+ break;
+ case CLIENT_FIRST_SENT:
+ response = calculateClientProof(challenge);
+ _state = State.CLIENT_PROOF_SENT;
+ break;
+ case CLIENT_PROOF_SENT:
+ evaluateOutcome(challenge);
+ response = null;
+ _state = State.COMPLETE;
+ break;
+ default:
+ throw new SaslException("No challenge expected in state " + _state);
+ }
+ return response;
+ }
+
+ private void evaluateOutcome(final byte[] challenge) throws SaslException
+ {
+ String serverFinalMessage = new String(challenge, ASCII);
+ String[] parts = serverFinalMessage.split(",");
+ if(!parts[0].startsWith("v="))
+ {
+ throw new SaslException("Server final message did not contain verifier");
+ }
+ byte[] serverSignature = DatatypeConverter.parseBase64Binary(parts[0].substring(2));
+ if(!Arrays.equals(_serverSignature, serverSignature))
+ {
+ throw new SaslException("Server signature did not match");
+ }
+ }
+
+ private byte[] calculateClientProof(final byte[] challenge) throws SaslException
+ {
+ try
+ {
+ String serverFirstMessage = new String(challenge, ASCII);
+ String[] parts = serverFirstMessage.split(",");
+ if(parts.length < 3)
+ {
+ throw new SaslException("Server challenge '" + serverFirstMessage + "' cannot be parsed");
+ }
+ else if(parts[0].startsWith("m="))
+ {
+ throw new SaslException("Server requires mandatory extension which is not supported: " + parts[0]);
+ }
+ else if(!parts[0].startsWith("r="))
+ {
+ throw new SaslException("Server challenge '" + serverFirstMessage + "' cannot be parsed, cannot find nonce");
+ }
+ String nonce = parts[0].substring(2);
+ if(!nonce.startsWith(_clientNonce))
+ {
+ throw new SaslException("Server challenge did not use correct client nonce");
+ }
+ _serverNonce = nonce;
+ if(!parts[1].startsWith("s="))
+ {
+ throw new SaslException("Server challenge '" + serverFirstMessage + "' cannot be parsed, cannot find salt");
+ }
+ String base64Salt = parts[1].substring(2);
+ _salt = DatatypeConverter.parseBase64Binary(base64Salt);
+ if(!parts[2].startsWith("i="))
+ {
+ throw new SaslException("Server challenge '" + serverFirstMessage + "' cannot be parsed, cannot find iteration count");
+ }
+ String iterCountString = parts[2].substring(2);
+ _iterationCount = Integer.parseInt(iterCountString);
+ if(_iterationCount <= 0)
+ {
+ throw new SaslException("Iteration count " + _iterationCount + " is not a positive integer");
+ }
+ PasswordCallback passwordCallback = new PasswordCallback("Password", false);
+ _callbackHandler.handle(new Callback[] { passwordCallback });
+ byte[] passwordBytes = saslPrep(new String(passwordCallback.getPassword())).getBytes("UTF-8");
+
+ byte[] saltedPassword = generateSaltedPassword(passwordBytes);
+
+
+ String clientFinalMessageWithoutProof =
+ "c=" + DatatypeConverter.printBase64Binary(GS2_HEADER.getBytes(ASCII))
+ + ",r=" + _serverNonce;
+
+ String authMessage = _clientFirstMessageBare + "," + serverFirstMessage + "," + clientFinalMessageWithoutProof;
+
+ byte[] clientKey = computeHmac(saltedPassword, "Client Key");
+ byte[] storedKey = MessageDigest.getInstance(_digestName).digest(clientKey);
+
+ byte[] clientSignature = computeHmac(storedKey, authMessage);
+
+ byte[] clientProof = clientKey.clone();
+ for(int i = 0 ; i < clientProof.length; i++)
+ {
+ clientProof[i] ^= clientSignature[i];
+ }
+ byte[] serverKey = computeHmac(saltedPassword, "Server Key");
+ _serverSignature = computeHmac(serverKey, authMessage);
+
+ String finalMessageWithProof = clientFinalMessageWithoutProof
+ + ",p=" + DatatypeConverter.printBase64Binary(clientProof);
+ return finalMessageWithProof.getBytes();
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ throw new SaslException(e.getMessage(), e);
+ }
+ catch (IllegalArgumentException e)
+ {
+ throw new SaslException(e.getMessage(), e);
+ }
+ catch (UnsupportedCallbackException e)
+ {
+ throw new SaslException(e.getMessage(), e);
+ }
+ catch (IOException e)
+ {
+ throw new SaslException(e.getMessage(), e);
+ }
+ catch (NoSuchAlgorithmException e)
+ {
+ throw new SaslException(e.getMessage(), e);
+ }
+ }
+
+ private byte[] computeHmac(final byte[] key, final String string)
+ throws SaslException, UnsupportedEncodingException
+ {
+ Mac mac = createHmac(key);
+ mac.update(string.getBytes(ASCII));
+ return mac.doFinal();
+ }
+
+ private byte[] generateSaltedPassword(final byte[] passwordBytes) throws SaslException
+ {
+ Mac mac = createHmac(passwordBytes);
+
+ mac.update(_salt);
+ mac.update(INT_1);
+ byte[] result = mac.doFinal();
+
+ byte[] previous = null;
+ for(int i = 1; i < _iterationCount; i++)
+ {
+ mac.update(previous != null? previous: result);
+ previous = mac.doFinal();
+ for(int x = 0; x < result.length; x++)
+ {
+ result[x] ^= previous[x];
+ }
+ }
+
+ return result;
+ }
+
+ private Mac createHmac(final byte[] keyBytes)
+ throws SaslException
+ {
+ try
+ {
+ SecretKeySpec key = new SecretKeySpec(keyBytes, _hmacName);
+ Mac mac = Mac.getInstance(_hmacName);
+ mac.init(key);
+ return mac;
+ }
+ catch (NoSuchAlgorithmException e)
+ {
+ throw new SaslException(e.getMessage(), e);
+ }
+ catch (InvalidKeyException e)
+ {
+ throw new SaslException(e.getMessage(), e);
+ }
+ }
+
+
+ private byte[] initialResponse() throws SaslException
+ {
+ try
+ {
+ StringBuffer buf = new StringBuffer("n=");
+ NameCallback nameCallback = new NameCallback("Username?");
+ _callbackHandler.handle(new Callback[] { nameCallback });
+ _username = nameCallback.getName();
+ buf.append(saslPrep(_username));
+ buf.append(",r=");
+ buf.append(_clientNonce);
+ _clientFirstMessageBare = buf.toString();
+ return (GS2_HEADER + _clientFirstMessageBare).getBytes(ASCII);
+ }
+ catch (UnsupportedCallbackException e)
+ {
+ throw new SaslException(e.getMessage(), e);
+ }
+ catch (IOException e)
+ {
+ throw new SaslException(e.getMessage(), e);
+ }
+ }
+
+ private String saslPrep(String name) throws SaslException
+ {
+ // TODO - a real implementation of SaslPrep
+
+ if(!ASCII.newEncoder().canEncode(name))
+ {
+ throw new SaslException("Can only encode names and passwords which are restricted to ASCII characters");
+ }
+
+ name = name.replace("=", "=3D");
+ name = name.replace(",", "=2C");
+ return name;
+ }
+
+ @Override
+ public boolean isComplete()
+ {
+ return _state == State.COMPLETE;
+ }
+
+ @Override
+ public byte[] unwrap(final byte[] incoming, final int offset, final int len) throws SaslException
+ {
+ throw new IllegalStateException("No security layer supported");
+ }
+
+ @Override
+ public byte[] wrap(final byte[] outgoing, final int offset, final int len) throws SaslException
+ {
+ throw new IllegalStateException("No security layer supported");
+ }
+
+ @Override
+ public Object getNegotiatedProperty(final String propName)
+ {
+ return null;
+ }
+
+ @Override
+ public void dispose() throws SaslException
+ {
+
+ }
+
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/security/scram/ScramSHA1SaslClient.java b/qpid/java/client/src/main/java/org/apache/qpid/client/security/scram/ScramSHA1SaslClient.java
index 91c03e18c1..b6704e9d94 100644
--- a/qpid/java/client/src/main/java/org/apache/qpid/client/security/scram/ScramSHA1SaslClient.java
+++ b/qpid/java/client/src/main/java/org/apache/qpid/client/security/scram/ScramSHA1SaslClient.java
@@ -20,320 +20,15 @@
*/
package org.apache.qpid.client.security.scram;
-import javax.crypto.Mac;
-import javax.crypto.spec.SecretKeySpec;
-import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
-import javax.security.auth.callback.NameCallback;
-import javax.security.auth.callback.PasswordCallback;
-import javax.security.auth.callback.UnsupportedCallbackException;
-import javax.security.sasl.SaslClient;
-import javax.security.sasl.SaslException;
-import javax.xml.bind.DatatypeConverter;
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.nio.charset.Charset;
-import java.security.InvalidKeyException;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.Arrays;
-import java.util.UUID;
-public class ScramSHA1SaslClient implements SaslClient
+public class ScramSHA1SaslClient extends AbstractScramSaslClient
{
- private static final byte[] INT_1 = new byte[]{0, 0, 0, 1};
- private static final String GS2_HEADER = "n,,";
- private static final Charset ASCII = Charset.forName("ASCII");
-
- private String _username;
- private final String _clientNonce = UUID.randomUUID().toString();
- private String _serverNonce;
- private byte[] _salt;
- private int _iterationCount;
- private String _clientFirstMessageBare;
- private byte[] _serverSignature;
-
- enum State
- {
- INITIAL,
- CLIENT_FIRST_SENT,
- CLIENT_PROOF_SENT,
- COMPLETE
- }
-
public static final String MECHANISM = "SCRAM-SHA-1";
- private final CallbackHandler _callbackHandler;
-
- private State _state = State.INITIAL;
-
public ScramSHA1SaslClient(final CallbackHandler cbh)
{
- _callbackHandler = cbh;
- }
-
- @Override
- public String getMechanismName()
- {
- return MECHANISM;
- }
-
- @Override
- public boolean hasInitialResponse()
- {
- return true;
- }
-
- @Override
- public byte[] evaluateChallenge(final byte[] challenge) throws SaslException
- {
- byte[] response;
- switch(_state)
- {
- case INITIAL:
- response = initialResponse();
- _state = State.CLIENT_FIRST_SENT;
- break;
- case CLIENT_FIRST_SENT:
- response = calculateClientProof(challenge);
- _state = State.CLIENT_PROOF_SENT;
- break;
- case CLIENT_PROOF_SENT:
- evaluateOutcome(challenge);
- response = null;
- _state = State.COMPLETE;
- break;
- default:
- throw new SaslException("No challenge expected in state " + _state);
- }
- return response;
- }
-
- private void evaluateOutcome(final byte[] challenge) throws SaslException
- {
- String serverFinalMessage = new String(challenge, ASCII);
- String[] parts = serverFinalMessage.split(",");
- if(!parts[0].startsWith("v="))
- {
- throw new SaslException("Server final message did not contain verifier");
- }
- byte[] serverSignature = DatatypeConverter.parseBase64Binary(parts[0].substring(2));
- if(!Arrays.equals(_serverSignature, serverSignature))
- {
- throw new SaslException("Server signature did not match");
- }
- }
-
- private byte[] calculateClientProof(final byte[] challenge) throws SaslException
- {
- try
- {
- String serverFirstMessage = new String(challenge, ASCII);
- String[] parts = serverFirstMessage.split(",");
- if(parts.length < 3)
- {
- throw new SaslException("Server challenge '" + serverFirstMessage + "' cannot be parsed");
- }
- else if(parts[0].startsWith("m="))
- {
- throw new SaslException("Server requires mandatory extension which is not supported: " + parts[0]);
- }
- else if(!parts[0].startsWith("r="))
- {
- throw new SaslException("Server challenge '" + serverFirstMessage + "' cannot be parsed, cannot find nonce");
- }
- String nonce = parts[0].substring(2);
- if(!nonce.startsWith(_clientNonce))
- {
- throw new SaslException("Server challenge did not use correct client nonce");
- }
- _serverNonce = nonce;
- if(!parts[1].startsWith("s="))
- {
- throw new SaslException("Server challenge '" + serverFirstMessage + "' cannot be parsed, cannot find salt");
- }
- String base64Salt = parts[1].substring(2);
- _salt = DatatypeConverter.parseBase64Binary(base64Salt);
- if(!parts[2].startsWith("i="))
- {
- throw new SaslException("Server challenge '" + serverFirstMessage + "' cannot be parsed, cannot find iteration count");
- }
- String iterCountString = parts[2].substring(2);
- _iterationCount = Integer.parseInt(iterCountString);
- if(_iterationCount <= 0)
- {
- throw new SaslException("Iteration count " + _iterationCount + " is not a positive integer");
- }
- PasswordCallback passwordCallback = new PasswordCallback("Password", false);
- _callbackHandler.handle(new Callback[] { passwordCallback });
- byte[] passwordBytes = saslPrep(new String(passwordCallback.getPassword())).getBytes("UTF-8");
-
- byte[] saltedPassword = generateSaltedPassword(passwordBytes);
-
-
- String clientFinalMessageWithoutProof =
- "c=" + DatatypeConverter.printBase64Binary(GS2_HEADER.getBytes(ASCII))
- + ",r=" + _serverNonce;
-
- String authMessage = _clientFirstMessageBare + "," + serverFirstMessage + "," + clientFinalMessageWithoutProof;
-
- byte[] clientKey = computeHmacSHA1(saltedPassword, "Client Key");
- byte[] storedKey = MessageDigest.getInstance("SHA1").digest(clientKey);
-
- byte[] clientSignature = computeHmacSHA1(storedKey, authMessage);
-
- byte[] clientProof = clientKey.clone();
- for(int i = 0 ; i < clientProof.length; i++)
- {
- clientProof[i] ^= clientSignature[i];
- }
- byte[] serverKey = computeHmacSHA1(saltedPassword, "Server Key");
- _serverSignature = computeHmacSHA1(serverKey, authMessage);
-
- String finalMessageWithProof = clientFinalMessageWithoutProof
- + ",p=" + DatatypeConverter.printBase64Binary(clientProof);
- return finalMessageWithProof.getBytes();
- }
- catch (UnsupportedEncodingException e)
- {
- throw new SaslException(e.getMessage(), e);
- }
- catch (IllegalArgumentException e)
- {
- throw new SaslException(e.getMessage(), e);
- }
- catch (UnsupportedCallbackException e)
- {
- throw new SaslException(e.getMessage(), e);
- }
- catch (IOException e)
- {
- throw new SaslException(e.getMessage(), e);
- }
- catch (NoSuchAlgorithmException e)
- {
- throw new SaslException(e.getMessage(), e);
- }
- }
-
- private byte[] computeHmacSHA1(final byte[] key, final String string)
- throws SaslException, UnsupportedEncodingException
- {
- Mac mac = createSha1Hmac(key);
- mac.update(string.getBytes(ASCII));
- return mac.doFinal();
- }
-
- private byte[] generateSaltedPassword(final byte[] passwordBytes) throws SaslException
- {
- Mac mac = createSha1Hmac(passwordBytes);
-
- mac.update(_salt);
- mac.update(INT_1);
- byte[] result = mac.doFinal();
-
- byte[] previous = null;
- for(int i = 1; i < _iterationCount; i++)
- {
- mac.update(previous != null? previous: result);
- previous = mac.doFinal();
- for(int x = 0; x < result.length; x++)
- {
- result[x] ^= previous[x];
- }
- }
-
- return result;
- }
-
- private Mac createSha1Hmac(final byte[] keyBytes)
- throws SaslException
- {
- try
- {
- SecretKeySpec key = new SecretKeySpec(keyBytes, "HmacSHA1");
- Mac mac = Mac.getInstance("HmacSHA1");
- mac.init(key);
- return mac;
- }
- catch (NoSuchAlgorithmException e)
- {
- throw new SaslException(e.getMessage(), e);
- }
- catch (InvalidKeyException e)
- {
- throw new SaslException(e.getMessage(), e);
- }
- }
-
-
- private byte[] initialResponse() throws SaslException
- {
- try
- {
- StringBuffer buf = new StringBuffer("n=");
- NameCallback nameCallback = new NameCallback("Username?");
- _callbackHandler.handle(new Callback[] { nameCallback });
- _username = nameCallback.getName();
- buf.append(saslPrep(_username));
- buf.append(",r=");
- buf.append(_clientNonce);
- _clientFirstMessageBare = buf.toString();
- return (GS2_HEADER + _clientFirstMessageBare).getBytes(ASCII);
- }
- catch (UnsupportedCallbackException e)
- {
- throw new SaslException(e.getMessage(), e);
- }
- catch (IOException e)
- {
- throw new SaslException(e.getMessage(), e);
- }
- }
-
- private String saslPrep(String name) throws SaslException
- {
- // TODO - a real implementation of SaslPrep
-
- if(!ASCII.newEncoder().canEncode(name))
- {
- throw new SaslException("Can only encode names and passwords which are restricted to ASCII characters");
- }
-
- name = name.replace("=", "=3D");
- name = name.replace(",", "=2C");
- return name;
- }
-
- @Override
- public boolean isComplete()
- {
- return _state == State.COMPLETE;
- }
-
- @Override
- public byte[] unwrap(final byte[] incoming, final int offset, final int len) throws SaslException
- {
- throw new IllegalStateException("No security layer supported");
- }
-
- @Override
- public byte[] wrap(final byte[] outgoing, final int offset, final int len) throws SaslException
- {
- throw new IllegalStateException("No security layer supported");
+ super(cbh, MECHANISM, "SHA-1", "HmacSHA1");
}
-
- @Override
- public Object getNegotiatedProperty(final String propName)
- {
- return null;
- }
-
- @Override
- public void dispose() throws SaslException
- {
-
- }
-
}
diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/security/scram/ScramSHA1SaslClientFactory.java b/qpid/java/client/src/main/java/org/apache/qpid/client/security/scram/ScramSHA1SaslClientFactory.java
index 8a54abbc04..59ef236bde 100644
--- a/qpid/java/client/src/main/java/org/apache/qpid/client/security/scram/ScramSHA1SaslClientFactory.java
+++ b/qpid/java/client/src/main/java/org/apache/qpid/client/security/scram/ScramSHA1SaslClientFactory.java
@@ -20,14 +20,16 @@
*/
package org.apache.qpid.client.security.scram;
+import java.util.Map;
+
import javax.security.auth.callback.CallbackHandler;
import javax.security.sasl.SaslClient;
import javax.security.sasl.SaslClientFactory;
import javax.security.sasl.SaslException;
-import java.util.Map;
public class ScramSHA1SaslClientFactory implements SaslClientFactory
{
+
@Override
public SaslClient createSaslClient(final String[] mechanisms,
final String authorizationId,
diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/security/scram/ScramSHA256SaslClient.java b/qpid/java/client/src/main/java/org/apache/qpid/client/security/scram/ScramSHA256SaslClient.java
new file mode 100644
index 0000000000..8779c36f0d
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpid/client/security/scram/ScramSHA256SaslClient.java
@@ -0,0 +1,34 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.qpid.client.security.scram;
+
+import javax.security.auth.callback.CallbackHandler;
+
+public class ScramSHA256SaslClient extends AbstractScramSaslClient
+{
+
+ public static final String MECHANISM = "SCRAM-SHA-256";
+
+ public ScramSHA256SaslClient(final CallbackHandler cbh)
+ {
+ super(cbh, MECHANISM, "SHA-256", "HmacSHA256");
+ }
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/security/scram/ScramSHA256SaslClientFactory.java b/qpid/java/client/src/main/java/org/apache/qpid/client/security/scram/ScramSHA256SaslClientFactory.java
new file mode 100644
index 0000000000..fff762f8ba
--- /dev/null
+++ b/qpid/java/client/src/main/java/org/apache/qpid/client/security/scram/ScramSHA256SaslClientFactory.java
@@ -0,0 +1,61 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.qpid.client.security.scram;
+
+import java.util.Map;
+
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.sasl.SaslClient;
+import javax.security.sasl.SaslClientFactory;
+import javax.security.sasl.SaslException;
+
+public class ScramSHA256SaslClientFactory implements SaslClientFactory
+{
+
+ @Override
+ public SaslClient createSaslClient(final String[] mechanisms,
+ final String authorizationId,
+ final String protocol,
+ final String serverName,
+ final Map<String, ?> props,
+ final CallbackHandler cbh) throws SaslException
+ {
+ for (int i = 0; i < mechanisms.length; i++)
+ {
+ if (mechanisms[i].equals(ScramSHA256SaslClient.MECHANISM))
+ {
+ if (cbh == null)
+ {
+ throw new SaslException("CallbackHandler must not be null");
+ }
+ return new ScramSHA256SaslClient(cbh);
+ }
+
+ }
+ return null;
+ }
+
+ @Override
+ public String[] getMechanismNames(final Map<String, ?> props)
+ {
+ return new String[0];
+ }
+}