You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@activemq.apache.org by cl...@apache.org on 2021/04/08 23:58:39 UTC

[activemq-artemis] branch master updated: ARTEMIS-3106 Support for SASL-SCRAM

This is an automated email from the ASF dual-hosted git repository.

clebertsuconic pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/activemq-artemis.git


The following commit(s) were added to refs/heads/master by this push:
     new 5313a80  ARTEMIS-3106 Support for SASL-SCRAM
     new c56912c  This closes #3470
5313a80 is described below

commit 5313a800a37a4174a43289457a8934266b50f96b
Author: Christoph Läubrich <ch...@laeubi-soft.de>
AuthorDate: Thu Feb 25 06:42:17 2021 +0100

    ARTEMIS-3106 Support for SASL-SCRAM
    
    adds the implementation necessary to perform SASL-SCRAM authentication with ActiveMQ Artemis
---
 artemis-protocols/artemis-amqp-protocol/pom.xml    |    5 +
 .../amqp/connect/AMQPBrokerConnection.java         |   16 +-
 .../protocol/amqp/sasl/scram/SCRAMClientSASL.java  |   97 +
 .../protocol/amqp/sasl/scram/SCRAMServerSASL.java  |  163 ++
 .../amqp/sasl/scram/SCRAMServerSASLFactory.java    |  157 ++
 .../sasl/scram/SHA256SCRAMServerSASLFactory.java   |   30 +-
 .../sasl/scram/SHA512SCRAMServerSASLFactory.java   |   30 +-
 .../amqp/sasl/scram/ScramClientFunctionality.java  |   93 +
 .../sasl/scram/ScramClientFunctionalityImpl.java   |  215 ++
 .../amqp/sasl/scram/ScramServerFunctionality.java  |   98 +
 .../sasl/scram/ScramServerFunctionalityImpl.java   |  204 ++
 ...mq.artemis.protocol.amqp.sasl.ServerSASLFactory |    4 +-
 .../artemis/protocol/amqp/sasl/SCRAMTest.java      |  192 ++
 ...dule.java => AbstractPrincipalLoginModule.java} |   57 +-
 .../{Krb5Callback.java => DigestCallback.java}     |   27 +-
 .../jaas/{Krb5Callback.java => HmacCallback.java}  |   26 +-
 .../core/security/jaas/JaasCallbackHandler.java    |   30 +-
 .../spi/core/security/jaas/Krb5LoginModule.java    |   94 +-
 .../{Krb5Callback.java => PrincipalsCallback.java} |   23 +-
 .../{Krb5Callback.java => SCRAMLoginModule.java}   |   25 +-
 ...b5Callback.java => SCRAMMechanismCallback.java} |   26 +-
 .../security/jaas/SCRAMPropertiesLoginModule.java  |  237 +++
 .../artemis/spi/core/security/scram/SCRAM.java     |   56 +
 .../spi/core/security/scram/ScramException.java    |   44 +
 .../spi/core/security/scram/ScramUtils.java        |  264 +++
 .../spi/core/security/scram/StringPrep.java        | 2139 ++++++++++++++++++++
 .../artemis/spi/core/security/scram/UserData.java  |   53 +
 .../core/security/jaas/Krb5LoginModuleTest.java    |   13 +-
 examples/protocols/amqp/pom.xml                    |    1 +
 examples/protocols/amqp/sasl-scram/pom.xml         |   43 +
 .../protocols/amqp/sasl-scram/sasl-client/pom.xml  |   49 +
 .../amqp/sasl-scram/sasl-client/readme.md          |    3 +
 .../activemq/artemis/jms/example/QPIDClient.java   |   48 +
 .../protocols/amqp/sasl-scram/sasl-server/pom.xml  |   54 +
 .../amqp/sasl-scram/sasl-server/readme.md          |    3 +
 .../activemq/artemis/jms/example/TestServer.java   |   35 +
 .../src/main/resources/artemis-roles.properties    |   18 +
 .../src/main/resources/artemis-users.properties    |   24 +
 .../sasl-server/src/main/resources/broker.xml      |   50 +
 .../sasl-server/src/main/resources/login.conf      |   29 +
 tests/integration-tests/.gitignore                 |    2 +
 .../amqp/connect/AMQPConnectSaslTest.java          |  170 +-
 .../tests/integration/amqp/sasl/SaslScramTest.java |  117 ++
 .../test/resources/artemis-scram-roles.properties  |   18 +
 .../test/resources/artemis-scram-users.properties  |   26 +
 .../src/test/resources/broker-saslscram.xml        |   50 +
 .../src/test/resources/login.config                |   12 +
 47 files changed, 4899 insertions(+), 271 deletions(-)

diff --git a/artemis-protocols/artemis-amqp-protocol/pom.xml b/artemis-protocols/artemis-amqp-protocol/pom.xml
index 1911f25..8fb0e45 100644
--- a/artemis-protocols/artemis-amqp-protocol/pom.xml
+++ b/artemis-protocols/artemis-amqp-protocol/pom.xml
@@ -39,6 +39,11 @@
       </dependency>
       <dependency>
          <groupId>org.apache.activemq</groupId>
+         <artifactId>artemis-server</artifactId>
+         <version>${project.version}</version>
+      </dependency>
+      <dependency>
+         <groupId>org.apache.activemq</groupId>
          <artifactId>artemis-core-client</artifactId>
          <version>${project.version}</version>
       </dependency>
diff --git a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/connect/AMQPBrokerConnection.java b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/connect/AMQPBrokerConnection.java
index a95914f..2068d11 100644
--- a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/connect/AMQPBrokerConnection.java
+++ b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/connect/AMQPBrokerConnection.java
@@ -63,10 +63,12 @@ import org.apache.activemq.artemis.protocol.amqp.proton.ProtonServerSenderContex
 import org.apache.activemq.artemis.protocol.amqp.proton.SenderController;
 import org.apache.activemq.artemis.protocol.amqp.sasl.ClientSASL;
 import org.apache.activemq.artemis.protocol.amqp.sasl.ClientSASLFactory;
+import org.apache.activemq.artemis.protocol.amqp.sasl.scram.SCRAMClientSASL;
 import org.apache.activemq.artemis.spi.core.protocol.ConnectionEntry;
 import org.apache.activemq.artemis.spi.core.remoting.ClientConnectionLifeCycleListener;
 import org.apache.activemq.artemis.spi.core.remoting.ClientProtocolManager;
 import org.apache.activemq.artemis.spi.core.remoting.Connection;
+import org.apache.activemq.artemis.spi.core.security.scram.SCRAM;
 import org.apache.activemq.artemis.utils.ConfigurationHelper;
 import org.apache.activemq.artemis.utils.UUIDGenerator;
 import org.apache.qpid.proton.amqp.Symbol;
@@ -96,8 +98,8 @@ public class AMQPBrokerConnection implements ClientConnectionLifeCycleListener,
    private int retryCounter = 0;
    private boolean connecting = false;
    private volatile ScheduledFuture reconnectFuture;
-   private Set<Queue> senders = new HashSet<>();
-   private Set<Queue> receivers = new HashSet<>();
+   private final Set<Queue> senders = new HashSet<>();
+   private final Set<Queue> receivers = new HashSet<>();
 
    final Executor connectExecutor;
    final ScheduledExecutorService scheduledExecutorService;
@@ -676,7 +678,15 @@ public class AMQPBrokerConnection implements ClientConnectionLifeCycleListener,
          if (availableMechanisms.contains(EXTERNAL) && ExternalSASLMechanism.isApplicable(connection)) {
             return new ExternalSASLMechanism();
          }
-
+         if (SCRAMClientSASL.isApplicable(brokerConnectConfiguration.getUser(),
+                                          brokerConnectConfiguration.getPassword())) {
+            for (SCRAM scram : SCRAM.values()) {
+               if (availableMechanisms.contains(scram.getName())) {
+                  return new SCRAMClientSASL(scram, brokerConnectConfiguration.getUser(),
+                                             brokerConnectConfiguration.getPassword());
+               }
+            }
+         }
          if (availableMechanisms.contains(PLAIN) && PlainSASLMechanism.isApplicable(brokerConnectConfiguration.getUser(), brokerConnectConfiguration.getPassword())) {
             return new PlainSASLMechanism(brokerConnectConfiguration.getUser(), brokerConnectConfiguration.getPassword());
          }
diff --git a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/sasl/scram/SCRAMClientSASL.java b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/sasl/scram/SCRAMClientSASL.java
new file mode 100644
index 0000000..2842e90
--- /dev/null
+++ b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/sasl/scram/SCRAMClientSASL.java
@@ -0,0 +1,97 @@
+/*
+ * 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.activemq.artemis.protocol.amqp.sasl.scram;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Objects;
+import java.util.UUID;
+
+import org.apache.activemq.artemis.protocol.amqp.sasl.ClientSASL;
+import org.apache.activemq.artemis.protocol.amqp.sasl.scram.ScramClientFunctionality.State;
+import org.apache.activemq.artemis.spi.core.security.scram.SCRAM;
+import org.apache.activemq.artemis.spi.core.security.scram.ScramException;
+import org.apache.qpid.proton.codec.DecodeException;
+
+/**
+ * implements the client part of SASL-SCRAM for broker interconnect
+ */
+public class SCRAMClientSASL implements ClientSASL {
+   private final SCRAM scramType;
+   private final ScramClientFunctionalityImpl client;
+   private final String username;
+   private final String password;
+
+   /**
+    * @param scram the SCRAM mechanism to use
+    * @param username the username for authentication
+    * @param password the password for authentication
+    */
+
+   public SCRAMClientSASL(SCRAM scram, String username, String password) {
+      this(scram, username, password, UUID.randomUUID().toString());
+   }
+
+   protected SCRAMClientSASL(SCRAM scram, String username, String password, String nonce) {
+      Objects.requireNonNull(scram);
+      Objects.requireNonNull(username);
+      Objects.requireNonNull(password);
+      this.username = username;
+      this.password = password;
+      this.scramType = scram;
+      client = new ScramClientFunctionalityImpl(scram.getDigest(), scram.getHmac(), nonce);
+   }
+
+   @Override
+   public String getName() {
+      return scramType.getName();
+   }
+
+   @Override
+   public byte[] getInitialResponse() {
+      try {
+         String firstMessage = client.prepareFirstMessage(username);
+         return firstMessage.getBytes(StandardCharsets.US_ASCII);
+      } catch (ScramException e) {
+         throw new DecodeException("prepareFirstMessage failed", e);
+      }
+   }
+
+   @Override
+   public byte[] getResponse(byte[] challenge) {
+      String msg = new String(challenge, StandardCharsets.US_ASCII);
+      if (client.getState() == State.FIRST_PREPARED) {
+         try {
+            String finalMessage = client.prepareFinalMessage(password, msg);
+            return finalMessage.getBytes(StandardCharsets.US_ASCII);
+         } catch (ScramException e) {
+            throw new DecodeException("prepareFinalMessage failed", e);
+         }
+      } else if (client.getState() == State.FINAL_PREPARED) {
+         try {
+            client.checkServerFinalMessage(msg);
+         } catch (ScramException e) {
+            throw new DecodeException("checkServerFinalMessage failed", e);
+         }
+      }
+      return new byte[0];
+   }
+
+   public static boolean isApplicable(String username, String password) {
+      return username != null && username.length() > 0 && password != null && password.length() > 0;
+   }
+
+}
diff --git a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/sasl/scram/SCRAMServerSASL.java b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/sasl/scram/SCRAMServerSASL.java
new file mode 100644
index 0000000..2c6826e
--- /dev/null
+++ b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/sasl/scram/SCRAMServerSASL.java
@@ -0,0 +1,163 @@
+/*
+ * 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.activemq.artemis.protocol.amqp.sasl.scram;
+
+import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
+import java.security.NoSuchAlgorithmException;
+import java.util.Collections;
+import java.util.UUID;
+
+import javax.security.auth.Subject;
+import javax.security.auth.login.LoginException;
+
+import org.apache.activemq.artemis.protocol.amqp.sasl.SASLResult;
+import org.apache.activemq.artemis.protocol.amqp.sasl.ServerSASL;
+import org.apache.activemq.artemis.spi.core.security.jaas.UserPrincipal;
+import org.apache.activemq.artemis.spi.core.security.scram.SCRAM;
+import org.apache.activemq.artemis.spi.core.security.scram.ScramException;
+import org.apache.activemq.artemis.spi.core.security.scram.UserData;
+
+public abstract class SCRAMServerSASL implements ServerSASL {
+
+   protected final ScramServerFunctionality scram;
+   protected final SCRAM mechanism;
+   private SASLResult result;
+
+   public SCRAMServerSASL(SCRAM mechanism) throws NoSuchAlgorithmException {
+      this(mechanism, UUID.randomUUID().toString());
+   }
+
+   protected SCRAMServerSASL(SCRAM mechanism, String nonce) throws NoSuchAlgorithmException {
+      this.mechanism = mechanism;
+      this.scram = new ScramServerFunctionalityImpl(mechanism.getDigest(), mechanism.getHmac(), nonce);
+   }
+
+   @Override
+   public String getName() {
+      return mechanism.getName();
+   }
+
+   @Override
+   public byte[] processSASL(byte[] bytes) {
+      String message = new String(bytes, StandardCharsets.US_ASCII);
+      try {
+         switch (scram.getState()) {
+            case INITIAL: {
+               String userName = scram.handleClientFirstMessage(message);
+               UserData userData = aquireUserData(userName);
+               result = new SCRAMSASLResult(userName, scram, createSaslSubject(userName, userData));
+               String challenge = scram.prepareFirstMessage(userData);
+               return challenge.getBytes(StandardCharsets.US_ASCII);
+            }
+            case PREPARED_FIRST: {
+               String finalMessage = scram.prepareFinalMessage(message);
+               return finalMessage.getBytes(StandardCharsets.US_ASCII);
+            }
+            default:
+               result = new SCRAMFailedSASLResult();
+               break;
+         }
+      } catch (GeneralSecurityException | ScramException | RuntimeException e) {
+         result = new SCRAMFailedSASLResult();
+         failed(e);
+      }
+      return null;
+   }
+
+   protected abstract UserData aquireUserData(String userName) throws LoginException;
+
+   protected abstract void failed(Exception e);
+
+   protected Subject createSaslSubject(String userName, UserData userData) {
+      UserPrincipal userPrincipal = new UserPrincipal(userName);
+      Subject saslSubject = new Subject(true, Collections.singleton(userPrincipal), Collections.singleton(userData),
+                                        Collections.emptySet());
+      return saslSubject;
+   }
+
+   @Override
+   public SASLResult result() {
+      if (result instanceof SCRAMSASLResult) {
+         return scram.isEnded() ? result : null;
+      }
+      return result;
+   }
+
+   public boolean isEnded() {
+      return scram.isEnded();
+   }
+
+   private static final class SCRAMSASLResult implements SASLResult {
+
+      private final String userName;
+      private final ScramServerFunctionality scram;
+      private final Subject subject;
+
+      SCRAMSASLResult(String userName, ScramServerFunctionality scram, Subject subject) {
+         this.userName = userName;
+         this.scram = scram;
+         this.subject = subject;
+      }
+
+      @Override
+      public String getUser() {
+         return userName;
+      }
+
+      @Override
+      public Subject getSubject() {
+         return subject;
+      }
+
+      @Override
+      public boolean isSuccess() {
+         return userName != null && scram.isEnded() && scram.isSuccessful();
+      }
+
+      @Override
+      public String toString() {
+         return "SCRAMSASLResult: userName = " + userName + ", state = " + scram.getState();
+      }
+
+   }
+
+   private static final class SCRAMFailedSASLResult implements SASLResult {
+
+      @Override
+      public String getUser() {
+         return null;
+      }
+
+      @Override
+      public Subject getSubject() {
+         return null;
+      }
+
+      @Override
+      public boolean isSuccess() {
+         return false;
+      }
+
+      @Override
+      public String toString() {
+         return "SCRAMFailedSASLResult";
+      }
+
+   }
+
+}
\ No newline at end of file
diff --git a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/sasl/scram/SCRAMServerSASLFactory.java b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/sasl/scram/SCRAMServerSASLFactory.java
new file mode 100644
index 0000000..67ec428
--- /dev/null
+++ b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/sasl/scram/SCRAMServerSASLFactory.java
@@ -0,0 +1,157 @@
+/*
+ * 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.activemq.artemis.protocol.amqp.sasl.scram;
+
+import java.io.IOException;
+import java.security.NoSuchAlgorithmException;
+import java.util.Iterator;
+
+import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+
+import org.apache.activemq.artemis.core.server.ActiveMQServer;
+import org.apache.activemq.artemis.protocol.amqp.broker.AmqpInterceptor;
+import org.apache.activemq.artemis.protocol.amqp.broker.ProtonProtocolManager;
+import org.apache.activemq.artemis.protocol.amqp.sasl.ServerSASL;
+import org.apache.activemq.artemis.protocol.amqp.sasl.ServerSASLFactory;
+import org.apache.activemq.artemis.spi.core.protocol.ProtocolManager;
+import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection;
+import org.apache.activemq.artemis.spi.core.remoting.Connection;
+import org.apache.activemq.artemis.spi.core.security.jaas.DigestCallback;
+import org.apache.activemq.artemis.spi.core.security.jaas.HmacCallback;
+import org.apache.activemq.artemis.spi.core.security.jaas.SCRAMMechanismCallback;
+import org.apache.activemq.artemis.spi.core.security.scram.SCRAM;
+import org.apache.activemq.artemis.spi.core.security.scram.UserData;
+import org.jboss.logging.Logger;
+
+/**
+ * abstract class that implements the SASL-SCRAM authentication scheme, concrete implementations
+ * must supply the {@link SCRAM} type to use and be register via SPI
+ */
+public abstract class SCRAMServerSASLFactory implements ServerSASLFactory {
+
+   private final Logger logger = Logger.getLogger(getClass());
+   private final SCRAM scramType;
+
+   public SCRAMServerSASLFactory(SCRAM scram) {
+      this.scramType = scram;
+   }
+
+   @Override
+   public String getMechanism() {
+      return scramType.getName();
+   }
+
+   @Override
+   public boolean isDefaultPermitted() {
+      return false;
+   }
+
+   @Override
+   public ServerSASL create(ActiveMQServer server, ProtocolManager<AmqpInterceptor> manager, Connection connection,
+                            RemotingConnection remotingConnection) {
+      try {
+         if (manager instanceof ProtonProtocolManager) {
+            String loginConfigScope = ((ProtonProtocolManager) manager).getSaslLoginConfigScope();
+            return new JAASSCRAMServerSASL(scramType, loginConfigScope, logger);
+         }
+      } catch (NoSuchAlgorithmException e) {
+         // can't be used then...
+      }
+      return null;
+   }
+
+   private static final class JAASSCRAMServerSASL extends SCRAMServerSASL {
+
+      private final String loginConfigScope;
+      private LoginContext loginContext = null;
+      private Subject loginSubject;
+      private final Logger logger;
+
+      JAASSCRAMServerSASL(SCRAM scram, String loginConfigScope, Logger logger) throws NoSuchAlgorithmException {
+         super(scram);
+         this.loginConfigScope = loginConfigScope;
+         this.logger = logger;
+      }
+
+      @Override
+      protected UserData aquireUserData(String userName) throws LoginException {
+         loginContext = new LoginContext(loginConfigScope, new CallbackHandler() {
+
+            @Override
+            public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
+               for (Callback callback : callbacks) {
+                  if (callback instanceof NameCallback) {
+                     ((NameCallback) callback).setName(userName);
+                  } else if (callback instanceof SCRAMMechanismCallback) {
+                     ((SCRAMMechanismCallback) callback).setMechanism(mechanism.getName());
+                  } else if (callback instanceof DigestCallback) {
+                     ((DigestCallback) callback).setDigest(scram.getDigest());
+                  } else if (callback instanceof HmacCallback) {
+                     ((HmacCallback) callback).setHmac(scram.getHmac());
+                  } else {
+                     throw new UnsupportedCallbackException(callback, "Unrecognized Callback " +
+                              callback.getClass().getSimpleName());
+                  }
+               }
+            }
+         });
+         loginContext.login();
+         loginSubject = loginContext.getSubject();
+         Iterator<UserData> credentials = loginSubject.getPublicCredentials(UserData.class).iterator();
+         if (credentials.hasNext()) {
+            return credentials.next();
+         }
+         throw new LoginException("can't aquire user data through configured login config scope (" + loginConfigScope +
+                  ")");
+      }
+
+      @Override
+      protected Subject createSaslSubject(String userName, UserData userData) {
+         if (loginSubject != null) {
+            return new Subject(true, loginSubject.getPrincipals(), loginSubject.getPublicCredentials(),
+                                              loginSubject.getPrivateCredentials());
+         }
+         return super.createSaslSubject(userName, userData);
+      }
+
+      @Override
+      public void done() {
+         if (loginContext != null) {
+            try {
+               loginContext.logout();
+            } catch (LoginException e1) {
+               // we can't do anything useful then...
+            }
+         }
+         loginContext = null;
+         loginSubject = null;
+      }
+
+      @Override
+      protected void failed(Exception e) {
+         logger.warn("SASL-SCRAM Authentication failed", e);
+      }
+
+   }
+
+}
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/Krb5Callback.java b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/sasl/scram/SHA256SCRAMServerSASLFactory.java
similarity index 57%
copy from artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/Krb5Callback.java
copy to artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/sasl/scram/SHA256SCRAMServerSASLFactory.java
index 9306d5f..f7a1308 100644
--- a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/Krb5Callback.java
+++ b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/sasl/scram/SHA256SCRAMServerSASLFactory.java
@@ -14,33 +14,21 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.activemq.artemis.spi.core.security.jaas;
+package org.apache.activemq.artemis.protocol.amqp.sasl.scram;
 
-import javax.security.auth.callback.Callback;
-import java.security.Principal;
+import org.apache.activemq.artemis.spi.core.security.scram.SCRAM;
 
 /**
- * A Callback for kerberos peer principal.
+ * provides SASL SRAM-SHA256
  */
-public class Krb5Callback implements Callback {
+public class SHA256SCRAMServerSASLFactory extends SCRAMServerSASLFactory {
 
-   Principal peerPrincipal;
-
-   /**
-    * Setter for peer Principal.
-    *
-    * @param principal The certificates to be returned.
-    */
-   public void setPeerPrincipal(Principal principal) {
-      peerPrincipal = principal;
+   public SHA256SCRAMServerSASLFactory() {
+      super(SCRAM.SHA256);
    }
 
-   /**
-    * Getter for peer Principal.
-    *
-    * @return The principal being carried.
-    */
-   public Principal getPeerPrincipal() {
-      return peerPrincipal;
+   @Override
+   public int getPrecedence() {
+      return 256;
    }
 }
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/Krb5Callback.java b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/sasl/scram/SHA512SCRAMServerSASLFactory.java
similarity index 57%
copy from artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/Krb5Callback.java
copy to artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/sasl/scram/SHA512SCRAMServerSASLFactory.java
index 9306d5f..93c4008 100644
--- a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/Krb5Callback.java
+++ b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/sasl/scram/SHA512SCRAMServerSASLFactory.java
@@ -14,33 +14,21 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.activemq.artemis.spi.core.security.jaas;
+package org.apache.activemq.artemis.protocol.amqp.sasl.scram;
 
-import javax.security.auth.callback.Callback;
-import java.security.Principal;
+import org.apache.activemq.artemis.spi.core.security.scram.SCRAM;
 
 /**
- * A Callback for kerberos peer principal.
+ * provides SASL SRAM-SHA512
  */
-public class Krb5Callback implements Callback {
+public class SHA512SCRAMServerSASLFactory extends SCRAMServerSASLFactory {
 
-   Principal peerPrincipal;
-
-   /**
-    * Setter for peer Principal.
-    *
-    * @param principal The certificates to be returned.
-    */
-   public void setPeerPrincipal(Principal principal) {
-      peerPrincipal = principal;
+   public SHA512SCRAMServerSASLFactory() {
+      super(SCRAM.SHA512);
    }
 
-   /**
-    * Getter for peer Principal.
-    *
-    * @return The principal being carried.
-    */
-   public Principal getPeerPrincipal() {
-      return peerPrincipal;
+   @Override
+   public int getPrecedence() {
+      return 512;
    }
 }
diff --git a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/sasl/scram/ScramClientFunctionality.java b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/sasl/scram/ScramClientFunctionality.java
new file mode 100644
index 0000000..6a243f3
--- /dev/null
+++ b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/sasl/scram/ScramClientFunctionality.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2016 Ognyan Bankov
+ * <p>
+ * All rights reserved. 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.activemq.artemis.protocol.amqp.sasl.scram;
+
+import org.apache.activemq.artemis.spi.core.security.scram.ScramException;
+
+/**
+ * Provides building blocks for creating SCRAM authentication client
+ */
+@SuppressWarnings("unused")
+public interface ScramClientFunctionality {
+   /**
+    * Prepares the first client message
+    * @param username Username of the user
+    * @return First client message
+    * @throws ScramException if username contains prohibited characters
+    */
+   String prepareFirstMessage(String username) throws ScramException;
+
+   /**
+    * Prepares client's final message
+    * @param password User password
+    * @param serverFirstMessage Server's first message
+    * @return Client's final message
+    * @throws ScramException if there is an error processing server's message, i.e. it violates the
+    *            protocol
+    */
+   String prepareFinalMessage(String password, String serverFirstMessage) throws ScramException;
+
+   /**
+    * Checks if the server's final message is valid
+    * @param serverFinalMessage Server's final message
+    * @throws ScramException if there is an error processing server's message, i.e. it violates the
+    *            protocol
+    */
+   void checkServerFinalMessage(String serverFinalMessage) throws ScramException;
+
+   /**
+    * Checks if authentication is successful. You can call this method only if authentication is
+    * completed. Ensure that using {@link #isEnded()}
+    * @return true if successful, false otherwise
+    */
+   boolean isSuccessful();
+
+   /**
+    * Checks if authentication is completed, either successfully or not. Authentication is completed
+    * if {@link #getState()} returns ENDED.
+    * @return true if authentication has ended
+    */
+   boolean isEnded();
+
+   /**
+    * Gets the state of the authentication procedure
+    * @return Current state
+    */
+   State getState();
+
+   /**
+    * State of the authentication procedure
+    */
+   enum State {
+               /**
+                * Initial state
+                */
+               INITIAL,
+               /**
+                * State after first message is prepared
+                */
+               FIRST_PREPARED,
+               /**
+                * State after final message is prepared
+                */
+               FINAL_PREPARED,
+               /**
+                * Authentication is completes, either successfully or not
+                */
+               ENDED
+   }
+}
diff --git a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/sasl/scram/ScramClientFunctionalityImpl.java b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/sasl/scram/ScramClientFunctionalityImpl.java
new file mode 100644
index 0000000..1f566e6
--- /dev/null
+++ b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/sasl/scram/ScramClientFunctionalityImpl.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright 2016 Ognyan Bankov
+ * <p>
+ * All rights reserved. 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.activemq.artemis.protocol.amqp.sasl.scram;
+
+import java.nio.charset.Charset;
+import java.security.InvalidKeyException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+import java.util.Base64;
+import java.util.UUID;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.crypto.Mac;
+
+import org.apache.activemq.artemis.spi.core.security.scram.ScramException;
+import org.apache.activemq.artemis.spi.core.security.scram.ScramUtils;
+import org.apache.activemq.artemis.spi.core.security.scram.StringPrep;
+
+/**
+ * Provides building blocks for creating SCRAM authentication client
+ */
+@SuppressWarnings("unused")
+public class ScramClientFunctionalityImpl implements ScramClientFunctionality {
+   private static final Pattern SERVER_FIRST_MESSAGE = Pattern.compile("r=([^,]*),s=([^,]*),i=(.*)$");
+   private static final Pattern SERVER_FINAL_MESSAGE = Pattern.compile("v=([^,]*)$");
+
+   private static final String GS2_HEADER = "n,,";
+   private static final Charset ASCII = Charset.forName("ASCII");
+
+   private final String mDigestName;
+   private final String mHmacName;
+   private final String mClientNonce;
+   private String mClientFirstMessageBare;
+
+   private final boolean mIsSuccessful = false;
+   private byte[] mSaltedPassword;
+   private String mAuthMessage;
+
+   private State mState = State.INITIAL;
+
+   /**
+    * Create new ScramClientFunctionalityImpl
+    * @param digestName Digest to be used
+    * @param hmacName HMAC to be used
+    */
+   public ScramClientFunctionalityImpl(String digestName, String hmacName) {
+      this(digestName, hmacName, UUID.randomUUID().toString());
+   }
+
+   /**
+    * Create new ScramClientFunctionalityImpl
+    * @param digestName Digest to be used
+    * @param hmacName HMAC to be used
+    * @param clientNonce Client nonce to be used
+    */
+   public ScramClientFunctionalityImpl(String digestName, String hmacName, String clientNonce) {
+      if (ScramUtils.isNullOrEmpty(digestName)) {
+         throw new NullPointerException("digestName cannot be null or empty");
+      }
+      if (ScramUtils.isNullOrEmpty(hmacName)) {
+         throw new NullPointerException("hmacName cannot be null or empty");
+      }
+      if (ScramUtils.isNullOrEmpty(clientNonce)) {
+         throw new NullPointerException("clientNonce cannot be null or empty");
+      }
+
+      mDigestName = digestName;
+      mHmacName = hmacName;
+      mClientNonce = clientNonce;
+   }
+
+   /**
+    * Prepares first client message You may want to use
+    * {@link StringPrep#isContainingProhibitedCharacters(String)} in order to check if the username
+    * contains only valid characters
+    * @param username Username
+    * @return prepared first message
+    * @throws ScramException if <code>username</code> contains prohibited characters
+    */
+   @Override
+   public String prepareFirstMessage(String username) throws ScramException {
+      if (mState != State.INITIAL) {
+         throw new IllegalStateException("You can call this method only once");
+      }
+
+      try {
+         mClientFirstMessageBare = "n=" + StringPrep.prepAsQueryString(username) + ",r=" + mClientNonce;
+         mState = State.FIRST_PREPARED;
+         return GS2_HEADER + mClientFirstMessageBare;
+      } catch (StringPrep.StringPrepError e) {
+         mState = State.ENDED;
+         throw new ScramException("Username contains prohibited character");
+      }
+   }
+
+   @Override
+   public String prepareFinalMessage(String password, String serverFirstMessage) throws ScramException {
+      if (mState != State.FIRST_PREPARED) {
+         throw new IllegalStateException("You can call this method once only after " + "calling prepareFirstMessage()");
+      }
+
+      Matcher m = SERVER_FIRST_MESSAGE.matcher(serverFirstMessage);
+      if (!m.matches()) {
+         mState = State.ENDED;
+         return null;
+      }
+
+      String nonce = m.group(1);
+
+      if (!nonce.startsWith(mClientNonce)) {
+         mState = State.ENDED;
+         return null;
+      }
+
+      String salt = m.group(2);
+      String iterationCountString = m.group(3);
+      int iterations = Integer.parseInt(iterationCountString);
+      if (iterations <= 0) {
+         mState = State.ENDED;
+         return null;
+      }
+
+      try {
+         mSaltedPassword = ScramUtils.generateSaltedPassword(password, Base64.getDecoder().decode(salt), iterations,
+                                                             Mac.getInstance(mHmacName));
+
+         String clientFinalMessageWithoutProof =
+                  "c=" + Base64.getEncoder().encodeToString(GS2_HEADER.getBytes(ASCII)) + ",r=" + nonce;
+
+         mAuthMessage = mClientFirstMessageBare + "," + serverFirstMessage + "," + clientFinalMessageWithoutProof;
+
+         byte[] clientKey = ScramUtils.computeHmac(mSaltedPassword, mHmacName, "Client Key");
+         byte[] storedKey = MessageDigest.getInstance(mDigestName).digest(clientKey);
+
+         byte[] clientSignature = ScramUtils.computeHmac(storedKey, mHmacName, mAuthMessage);
+
+         byte[] clientProof = clientKey.clone();
+         for (int i = 0; i < clientProof.length; i++) {
+            clientProof[i] ^= clientSignature[i];
+         }
+
+         mState = State.FINAL_PREPARED;
+         return clientFinalMessageWithoutProof + ",p=" + Base64.getEncoder().encodeToString(clientProof);
+      } catch (InvalidKeyException | NoSuchAlgorithmException e) {
+         mState = State.ENDED;
+         throw new ScramException(e);
+      }
+   }
+
+   @Override
+   public void checkServerFinalMessage(String serverFinalMessage) throws ScramException {
+      if (mState != State.FINAL_PREPARED) {
+         throw new IllegalStateException("You can call this method only once after " + "calling prepareFinalMessage()");
+      }
+
+      Matcher m = SERVER_FINAL_MESSAGE.matcher(serverFinalMessage);
+      if (!m.matches()) {
+         mState = State.ENDED;
+         throw new ScramException("invalid message format");
+      }
+
+      byte[] serverSignature = Base64.getDecoder().decode(m.group(1));
+
+      mState = State.ENDED;
+      if (!Arrays.equals(serverSignature, getExpectedServerSignature())) {
+         throw new ScramException("Server signature missmatch");
+      }
+   }
+
+   @Override
+   public boolean isSuccessful() {
+      if (mState == State.ENDED) {
+         return mIsSuccessful;
+      } else {
+         throw new IllegalStateException("You cannot call this method before authentication is ended. " +
+                  "Use isEnded() to check that");
+      }
+   }
+
+   @Override
+   public boolean isEnded() {
+      return mState == State.ENDED;
+   }
+
+   @Override
+   public State getState() {
+      return mState;
+   }
+
+   private byte[] getExpectedServerSignature() throws ScramException {
+      try {
+         byte[] serverKey = ScramUtils.computeHmac(mSaltedPassword, mHmacName, "Server Key");
+         return ScramUtils.computeHmac(serverKey, mHmacName, mAuthMessage);
+      } catch (InvalidKeyException | NoSuchAlgorithmException e) {
+         mState = State.ENDED;
+         throw new ScramException(e);
+      }
+   }
+}
diff --git a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/sasl/scram/ScramServerFunctionality.java b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/sasl/scram/ScramServerFunctionality.java
new file mode 100644
index 0000000..b78575c
--- /dev/null
+++ b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/sasl/scram/ScramServerFunctionality.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2016 Ognyan Bankov
+ * <p>
+ * All rights reserved. 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.activemq.artemis.protocol.amqp.sasl.scram;
+
+import java.security.MessageDigest;
+
+import javax.crypto.Mac;
+
+import org.apache.activemq.artemis.spi.core.security.scram.ScramException;
+import org.apache.activemq.artemis.spi.core.security.scram.UserData;
+
+/**
+ * Provides building blocks for creating SCRAM authentication server
+ */
+public interface ScramServerFunctionality {
+   /**
+    * Handles client's first message
+    * @param message Client's first message
+    * @return username extracted from the client message
+    * @throws ScramException
+    */
+   String handleClientFirstMessage(String message) throws ScramException;
+
+   /**
+    * Prepares server's first message
+    * @param userData user data needed to prepare the message
+    * @return Server's first message
+    */
+   String prepareFirstMessage(UserData userData);
+
+   /**
+    * Prepares server's final message
+    * @param clientFinalMessage Client's final message
+    * @return Server's final message
+    * @throws ScramException
+    */
+   String prepareFinalMessage(String clientFinalMessage) throws ScramException;
+
+   /**
+    * Checks if authentication is completed, either successfully or not. Authentication is completed
+    * if {@link #getState()} returns ENDED.
+    * @return true if authentication has ended
+    */
+   boolean isSuccessful();
+
+   /**
+    * Checks if authentication is completed, either successfully or not. Authentication is completed
+    * if {@link #getState()} returns ENDED.
+    * @return true if authentication has ended
+    */
+   boolean isEnded();
+
+   /**
+    * Gets the state of the authentication procedure
+    * @return Current state
+    */
+   State getState();
+
+   /**
+    * State of the authentication procedure
+    */
+   enum State {
+               /**
+                * Initial state
+                */
+               INITIAL,
+               /**
+                * First client message is handled (username is extracted)
+                */
+               FIRST_CLIENT_MESSAGE_HANDLED,
+               /**
+                * First server message is prepared
+                */
+               PREPARED_FIRST,
+               /**
+                * Authentication is completes, either successfully or not
+                */
+               ENDED
+   }
+
+   MessageDigest getDigest();
+
+   Mac getHmac();
+}
diff --git a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/sasl/scram/ScramServerFunctionalityImpl.java b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/sasl/scram/ScramServerFunctionalityImpl.java
new file mode 100644
index 0000000..07cc0e1
--- /dev/null
+++ b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/sasl/scram/ScramServerFunctionalityImpl.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright 2016 Ognyan Bankov
+ * <p>
+ * All rights reserved. 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.activemq.artemis.protocol.amqp.sasl.scram;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+import java.util.Base64;
+import java.util.UUID;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.crypto.Mac;
+
+import org.apache.activemq.artemis.spi.core.security.scram.ScramException;
+import org.apache.activemq.artemis.spi.core.security.scram.ScramUtils;
+import org.apache.activemq.artemis.spi.core.security.scram.UserData;
+
+/**
+ * Provides building blocks for creating SCRAM authentication server
+ */
+public class ScramServerFunctionalityImpl implements ScramServerFunctionality {
+   private static final Pattern CLIENT_FIRST_MESSAGE =
+            Pattern.compile("^(([pny])=?([^,]*),([^,]*),)(m?=?[^,]*,?n=([^,]*),r=([^,]*),?.*)$");
+   private static final Pattern CLIENT_FINAL_MESSAGE = Pattern.compile("(c=([^,]*),r=([^,]*)),p=(.*)$");
+
+   private final String mServerPartNonce;
+
+   private boolean mIsSuccessful = false;
+   private State mState = State.INITIAL;
+   private String mClientFirstMessageBare;
+   private String mNonce;
+   private String mServerFirstMessage;
+   private UserData mUserData;
+   private final MessageDigest digest;
+   private final Mac hmac;
+
+   /**
+    * Creates new ScramServerFunctionalityImpl
+    * @param digestName Digest to be used
+    * @param hmacName HMAC to be used
+    * @throws NoSuchAlgorithmException
+    */
+   public ScramServerFunctionalityImpl(String digestName, String hmacName) throws NoSuchAlgorithmException {
+      this(digestName, hmacName, UUID.randomUUID().toString());
+   }
+
+   /**
+    * /** Creates new ScramServerFunctionalityImpl
+    * @param digestName Digest to be used
+    * @param hmacName HMAC to be used
+    * @param serverPartNonce Server's part of the nonce
+    * @throws NoSuchAlgorithmException
+    */
+   public ScramServerFunctionalityImpl(String digestName, String hmacName,
+                                       String serverPartNonce) throws NoSuchAlgorithmException {
+      if (ScramUtils.isNullOrEmpty(digestName)) {
+         throw new NullPointerException("digestName cannot be null or empty");
+      }
+      if (ScramUtils.isNullOrEmpty(hmacName)) {
+         throw new NullPointerException("hmacName cannot be null or empty");
+      }
+      if (ScramUtils.isNullOrEmpty(serverPartNonce)) {
+         throw new NullPointerException("serverPartNonce cannot be null or empty");
+      }
+      digest = MessageDigest.getInstance(digestName);
+      hmac = Mac.getInstance(hmacName);
+      mServerPartNonce = serverPartNonce;
+   }
+
+   /**
+    * Handles client's first message
+    * @param message Client's first message
+    * @return username extracted from the client message
+    * @throws ScramException
+    */
+   @Override
+   public String handleClientFirstMessage(String message) throws ScramException {
+      Matcher m = CLIENT_FIRST_MESSAGE.matcher(message);
+      if (!m.matches()) {
+         mState = State.ENDED;
+         throw new ScramException("Invalid message received");
+      }
+
+      mClientFirstMessageBare = m.group(5);
+      String username = m.group(6);
+      String clientNonce = m.group(7);
+      mNonce = clientNonce + mServerPartNonce;
+
+      mState = State.FIRST_CLIENT_MESSAGE_HANDLED;
+
+      return username;
+   }
+
+   @Override
+   public String prepareFirstMessage(UserData userData) {
+      mUserData = userData;
+      mState = State.PREPARED_FIRST;
+      mServerFirstMessage = String.format("r=%s,s=%s,i=%d", mNonce, userData.salt, userData.iterations);
+
+      return mServerFirstMessage;
+   }
+
+   @Override
+   public String prepareFinalMessage(String clientFinalMessage) throws ScramException {
+      String finalMessage = prepareFinalMessageUnchecked(clientFinalMessage);
+      if (!mIsSuccessful) {
+         throw new ScramException("client credentials missmatch");
+      }
+      return finalMessage;
+   }
+
+   public String prepareFinalMessageUnchecked(String clientFinalMessage) throws ScramException {
+      mState = State.ENDED;
+      Matcher m = CLIENT_FINAL_MESSAGE.matcher(clientFinalMessage);
+      if (!m.matches()) {
+         throw new ScramException("Invalid message received");
+      }
+
+      String clientFinalMessageWithoutProof = m.group(1);
+      String clientNonce = m.group(3);
+      String proof = m.group(4);
+
+      if (!mNonce.equals(clientNonce)) {
+         throw new ScramException("Nonce mismatch");
+      }
+
+      String authMessage = mClientFirstMessageBare + "," + mServerFirstMessage + "," + clientFinalMessageWithoutProof;
+
+      byte[] storedKeyArr = Base64.getDecoder().decode(mUserData.storedKey);
+      byte[] clientSignature = ScramUtils.computeHmac(storedKeyArr, hmac, authMessage);
+      byte[] serverSignature =
+               ScramUtils.computeHmac(Base64.getDecoder().decode(mUserData.serverKey), hmac, authMessage);
+      byte[] clientKey = clientSignature.clone();
+      byte[] decodedProof = Base64.getDecoder().decode(proof);
+      for (int i = 0; i < clientKey.length; i++) {
+         clientKey[i] ^= decodedProof[i];
+      }
+
+      byte[] resultKey = digest.digest(clientKey);
+      mIsSuccessful = Arrays.equals(storedKeyArr, resultKey);
+      return "v=" + Base64.getEncoder().encodeToString(serverSignature);
+   }
+
+   @Override
+   public boolean isSuccessful() {
+      if (mState == State.ENDED) {
+         return mIsSuccessful;
+      } else {
+         throw new IllegalStateException("You cannot call this method before authentication is ended. " +
+                  "Use isEnded() to check that");
+      }
+   }
+
+   @Override
+   public boolean isEnded() {
+      return mState == State.ENDED;
+   }
+
+   @Override
+   public State getState() {
+      return mState;
+   }
+
+   @Override
+   public MessageDigest getDigest() {
+      try {
+         return (MessageDigest) digest.clone();
+      } catch (CloneNotSupportedException cns) {
+         try {
+            return MessageDigest.getInstance(digest.getAlgorithm());
+         } catch (NoSuchAlgorithmException nsa) {
+            throw new AssertionError(nsa);
+         }
+      }
+   }
+
+   @Override
+   public Mac getHmac() {
+      try {
+         return (Mac) hmac.clone();
+      } catch (CloneNotSupportedException cns) {
+         try {
+            return Mac.getInstance(hmac.getAlgorithm());
+         } catch (NoSuchAlgorithmException nsa) {
+            throw new AssertionError(nsa);
+         }
+      }
+   }
+}
diff --git a/artemis-protocols/artemis-amqp-protocol/src/main/resources/META-INF/services/org.apache.activemq.artemis.protocol.amqp.sasl.ServerSASLFactory b/artemis-protocols/artemis-amqp-protocol/src/main/resources/META-INF/services/org.apache.activemq.artemis.protocol.amqp.sasl.ServerSASLFactory
index eb2c112..c8f54d2 100644
--- a/artemis-protocols/artemis-amqp-protocol/src/main/resources/META-INF/services/org.apache.activemq.artemis.protocol.amqp.sasl.ServerSASLFactory
+++ b/artemis-protocols/artemis-amqp-protocol/src/main/resources/META-INF/services/org.apache.activemq.artemis.protocol.amqp.sasl.ServerSASLFactory
@@ -1,4 +1,6 @@
 org.apache.activemq.artemis.protocol.amqp.sasl.AnonymousServerSASLFactory
 org.apache.activemq.artemis.protocol.amqp.sasl.PlainServerSASLFactory
 org.apache.activemq.artemis.protocol.amqp.sasl.GSSAPIServerSASLFactory
-org.apache.activemq.artemis.protocol.amqp.sasl.ExternalServerSASLFactory
\ No newline at end of file
+org.apache.activemq.artemis.protocol.amqp.sasl.ExternalServerSASLFactory
+org.apache.activemq.artemis.protocol.amqp.sasl.scram.SHA256SCRAMServerSASLFactory
+org.apache.activemq.artemis.protocol.amqp.sasl.scram.SHA512SCRAMServerSASLFactory
\ No newline at end of file
diff --git a/artemis-protocols/artemis-amqp-protocol/src/test/java/org/apache/activemq/artemis/protocol/amqp/sasl/SCRAMTest.java b/artemis-protocols/artemis-amqp-protocol/src/test/java/org/apache/activemq/artemis/protocol/amqp/sasl/SCRAMTest.java
new file mode 100644
index 0000000..f646d6e
--- /dev/null
+++ b/artemis-protocols/artemis-amqp-protocol/src/test/java/org/apache/activemq/artemis/protocol/amqp/sasl/SCRAMTest.java
@@ -0,0 +1,192 @@
+/*
+ * 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.activemq.artemis.protocol.amqp.sasl;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.crypto.Mac;
+import javax.security.auth.login.LoginException;
+
+import org.apache.activemq.artemis.protocol.amqp.sasl.scram.SCRAMClientSASL;
+import org.apache.activemq.artemis.protocol.amqp.sasl.scram.SCRAMServerSASL;
+import org.apache.activemq.artemis.protocol.amqp.sasl.scram.ScramServerFunctionalityImpl;
+import org.apache.activemq.artemis.spi.core.security.scram.SCRAM;
+import org.apache.activemq.artemis.spi.core.security.scram.ScramException;
+import org.apache.activemq.artemis.spi.core.security.scram.ScramUtils;
+import org.apache.activemq.artemis.spi.core.security.scram.UserData;
+import org.apache.qpid.proton.codec.DecodeException;
+import org.hamcrest.core.IsInstanceOf;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/**
+ * test cases for the SASL-SCRAM
+ */
+@RunWith(Parameterized.class)
+public class SCRAMTest {
+
+   /**
+    *
+    */
+   private final SCRAM mechanism;
+   private static final byte[] SALT = new byte[32];
+   private static final String SNONCE = "server";
+   private static final String CNONCE = "client";
+   private static final String USERNAME = "test";
+   private static final String PASSWORD = "123";
+
+   @Parameters(name = "{0}")
+   public static List<Object[]> data() {
+      List<Object[]> list = new ArrayList<>();
+      for (SCRAM scram : SCRAM.values()) {
+         list.add(new Object[] {scram});
+      }
+      return list;
+   }
+
+   public SCRAMTest(SCRAM mechanism) {
+      this.mechanism = mechanism;
+   }
+
+   @Test
+   public void testSuccess() throws NoSuchAlgorithmException {
+      TestSCRAMServerSASL serverSASL = new TestSCRAMServerSASL(mechanism, USERNAME, PASSWORD);
+      TestSCRAMClientSASL clientSASL = new TestSCRAMClientSASL(mechanism, USERNAME, PASSWORD);
+      byte[] clientFirst = clientSASL.getInitialResponse();
+      assertNotNull(clientFirst);
+      byte[] serverFirst = serverSASL.processSASL(clientFirst);
+      assertNotNull(serverFirst);
+      assertNull(serverSASL.result());
+      byte[] clientFinal = clientSASL.getResponse(serverFirst);
+      assertNotNull(clientFinal);
+      assertFalse(clientFinal.length == 0);
+      byte[] serverFinal = serverSASL.processSASL(clientFinal);
+      assertNotNull(serverFinal);
+      assertNotNull(serverSASL.result());
+      assertNotNull(serverSASL.result().getSubject());
+      assertEquals(USERNAME, serverSASL.result().getUser());
+      assertNull(serverSASL.exception);
+      assertTrue(serverSASL.result().isSuccess());
+      byte[] clientCheck = clientSASL.getResponse(serverFinal);
+      assertNotNull(clientCheck);
+      assertTrue(clientCheck.length == 0);
+   }
+
+   @Test
+   public void testWrongClientPassword() throws NoSuchAlgorithmException {
+      TestSCRAMServerSASL serverSASL = new TestSCRAMServerSASL(mechanism, USERNAME, PASSWORD);
+      TestSCRAMClientSASL clientSASL = new TestSCRAMClientSASL(mechanism, USERNAME, "xyz");
+      byte[] clientFirst = clientSASL.getInitialResponse();
+      assertNotNull(clientFirst);
+      byte[] serverFirst = serverSASL.processSASL(clientFirst);
+      assertNotNull(serverFirst);
+      assertNull(serverSASL.result());
+      byte[] clientFinal = clientSASL.getResponse(serverFirst);
+      assertNotNull(clientFinal);
+      assertFalse(clientFinal.length == 0);
+      byte[] serverFinal = serverSASL.processSASL(clientFinal);
+      assertNull(serverFinal);
+      assertNotNull(serverSASL.result());
+      assertFalse(serverSASL.result().isSuccess());
+      assertThat(serverSASL.exception, IsInstanceOf.instanceOf(ScramException.class));
+   }
+
+   @Test(expected = DecodeException.class)
+   public void testServerTryTrickClient() throws NoSuchAlgorithmException, ScramException {
+      TestSCRAMClientSASL clientSASL = new TestSCRAMClientSASL(mechanism, USERNAME, PASSWORD);
+      ScramServerFunctionalityImpl bad =
+               new ScramServerFunctionalityImpl(mechanism.getDigest(), mechanism.getHmac(), SNONCE);
+      byte[] clientFirst = clientSASL.getInitialResponse();
+      assertNotNull(clientFirst);
+      bad.handleClientFirstMessage(new String(clientFirst, StandardCharsets.US_ASCII));
+      byte[] serverFirst =
+               bad.prepareFirstMessage(generateUserData(mechanism, "bad")).getBytes(StandardCharsets.US_ASCII);
+      byte[] clientFinal = clientSASL.getResponse(serverFirst);
+      assertNotNull(clientFinal);
+      assertFalse(clientFinal.length == 0);
+      byte[] serverFinal = bad.prepareFinalMessageUnchecked(new String(clientFinal, StandardCharsets.US_ASCII))
+                              .getBytes(StandardCharsets.US_ASCII);
+      clientSASL.getResponse(serverFinal);
+   }
+
+   private static UserData generateUserData(SCRAM mechanism, String password) throws NoSuchAlgorithmException,
+                                                                              ScramException {
+      MessageDigest digest = MessageDigest.getInstance(mechanism.getDigest());
+      Mac hmac = Mac.getInstance(mechanism.getHmac());
+      ScramUtils.NewPasswordStringData data =
+               ScramUtils.byteArrayToStringData(ScramUtils.newPassword(password, SALT, 4096, digest, hmac));
+      return new UserData(data.salt, data.iterations, data.serverKey, data.storedKey);
+   }
+
+
+   private static final class TestSCRAMClientSASL extends SCRAMClientSASL {
+
+      TestSCRAMClientSASL(SCRAM scram, String username, String password) {
+         super(scram, username, password, CNONCE);
+      }
+
+   }
+
+   private static final class TestSCRAMServerSASL extends SCRAMServerSASL {
+
+      private Exception exception;
+      private final String username;
+      private final String password;
+
+      TestSCRAMServerSASL(SCRAM mechanism, String username, String password) throws NoSuchAlgorithmException {
+         super(mechanism, SNONCE);
+         this.username = username;
+         this.password = password;
+      }
+
+      @Override
+      public void done() {
+         // nothing to do
+      }
+
+      @Override
+      protected UserData aquireUserData(String userName) throws LoginException {
+         if (!this.username.equals(userName)) {
+            throw new LoginException("invalid username");
+         }
+         try {
+            return generateUserData(mechanism, password);
+         } catch (Exception e) {
+            throw new LoginException(e.getMessage());
+         }
+      }
+
+      @Override
+      protected void failed(Exception e) {
+         this.exception = e;
+      }
+
+   }
+}
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/Krb5LoginModule.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/AbstractPrincipalLoginModule.java
similarity index 67%
copy from artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/Krb5LoginModule.java
copy to artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/AbstractPrincipalLoginModule.java
index 1047e08..d9c7447 100644
--- a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/Krb5LoginModule.java
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/AbstractPrincipalLoginModule.java
@@ -16,36 +16,35 @@
  */
 package org.apache.activemq.artemis.spi.core.security.jaas;
 
-import org.jboss.logging.Logger;
+import java.io.IOException;
+import java.security.Principal;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
 
 import javax.security.auth.Subject;
 import javax.security.auth.callback.Callback;
 import javax.security.auth.callback.CallbackHandler;
 import javax.security.auth.callback.UnsupportedCallbackException;
 import javax.security.auth.login.LoginException;
-import java.io.IOException;
-import java.security.Principal;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
+
+import org.jboss.logging.Logger;
 
 /**
- * populate a subject with kerberos credential from the handler
+ * Abstract login module that uses an external authenticated principal
  */
-public class Krb5LoginModule implements AuditLoginModule {
-
-   private static final Logger logger = Logger.getLogger(Krb5LoginModule.class);
+public abstract class AbstractPrincipalLoginModule implements AuditLoginModule {
+   private final Logger logger = Logger.getLogger(getClass());
 
    private Subject subject;
-   private final List<Principal> principals = new LinkedList<>();
+   private final List<Principal> authenticatedPrincipals = new LinkedList<>();
    private CallbackHandler callbackHandler;
    private boolean loginSucceeded;
-   private Principal principal;
+   private Principal[] principals;
 
    @Override
-   public void initialize(Subject subject,
-                          CallbackHandler callbackHandler,
-                          Map<String, ?> sharedState,
+   public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState,
                           Map<String, ?> options) {
       this.subject = subject;
       this.callbackHandler = callbackHandler;
@@ -55,22 +54,22 @@ public class Krb5LoginModule implements AuditLoginModule {
    public boolean login() throws LoginException {
       Callback[] callbacks = new Callback[1];
 
-      callbacks[0] = new Krb5Callback();
+      callbacks[0] = new PrincipalsCallback();
       try {
          callbackHandler.handle(callbacks);
-         principal = ((Krb5Callback)callbacks[0]).getPeerPrincipal();
-         if (principal != null) {
-            principals.add(principal);
+         principals = ((PrincipalsCallback) callbacks[0]).getPeerPrincipals();
+         if (principals != null) {
+            authenticatedPrincipals.addAll(Arrays.asList(principals));
          }
       } catch (IOException ioe) {
          throw new LoginException(ioe.getMessage());
       } catch (UnsupportedCallbackException uce) {
          throw new LoginException(uce.getMessage() + " not available to obtain information from user");
       }
-      if (!principals.isEmpty()) {
+      if (!authenticatedPrincipals.isEmpty()) {
          loginSucceeded = true;
       }
-      logger.debug("login " + principals);
+      logger.debug("login " + authenticatedPrincipals);
       return loginSucceeded;
    }
 
@@ -78,8 +77,8 @@ public class Krb5LoginModule implements AuditLoginModule {
    public boolean commit() throws LoginException {
       boolean result = loginSucceeded;
       if (result) {
-         principals.add(new UserPrincipal(principals.get(0).getName()));
-         subject.getPrincipals().addAll(principals);
+         authenticatedPrincipals.add(new UserPrincipal(authenticatedPrincipals.get(0).getName()));
+         subject.getPrincipals().addAll(authenticatedPrincipals);
       }
 
       clear();
@@ -91,7 +90,11 @@ public class Krb5LoginModule implements AuditLoginModule {
 
    @Override
    public boolean abort() throws LoginException {
-      registerFailureForAudit(principal != null ? principal.getName() : null);
+      if (principals != null) {
+         for (Principal principal : authenticatedPrincipals) {
+            registerFailureForAudit(principal.getName());
+         }
+      }
       clear();
 
       logger.debug("abort");
@@ -100,14 +103,14 @@ public class Krb5LoginModule implements AuditLoginModule {
    }
 
    private void clear() {
-      principal = null;
+      principals = null;
       loginSucceeded = false;
    }
 
    @Override
    public boolean logout() throws LoginException {
-      subject.getPrincipals().removeAll(principals);
-      principals.clear();
+      subject.getPrincipals().removeAll(authenticatedPrincipals);
+      authenticatedPrincipals.clear();
       clear();
 
       logger.debug("logout");
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/Krb5Callback.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/DigestCallback.java
similarity index 66%
copy from artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/Krb5Callback.java
copy to artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/DigestCallback.java
index 9306d5f..865b266 100644
--- a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/Krb5Callback.java
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/DigestCallback.java
@@ -16,31 +16,30 @@
  */
 package org.apache.activemq.artemis.spi.core.security.jaas;
 
+import java.security.MessageDigest;
+
 import javax.security.auth.callback.Callback;
-import java.security.Principal;
 
 /**
- * A Callback for kerberos peer principal.
+ * Callback to obtain a {@link MessageDigest} for login purpose
  */
-public class Krb5Callback implements Callback {
+public class DigestCallback implements Callback {
 
-   Principal peerPrincipal;
+   private MessageDigest digest;
 
    /**
-    * Setter for peer Principal.
-    *
-    * @param principal The certificates to be returned.
+    * set the digest to use
+    * @param digest the digest
     */
-   public void setPeerPrincipal(Principal principal) {
-      peerPrincipal = principal;
+   public void setDigest(MessageDigest digest) {
+      this.digest = digest;
    }
 
    /**
-    * Getter for peer Principal.
-    *
-    * @return The principal being carried.
+    * @return the digest or <code>null</code> if not known
     */
-   public Principal getPeerPrincipal() {
-      return peerPrincipal;
+   public MessageDigest getDigest() {
+      return digest;
    }
+
 }
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/Krb5Callback.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/HmacCallback.java
similarity index 66%
copy from artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/Krb5Callback.java
copy to artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/HmacCallback.java
index 9306d5f..8d8f9ce 100644
--- a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/Krb5Callback.java
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/HmacCallback.java
@@ -16,31 +16,29 @@
  */
 package org.apache.activemq.artemis.spi.core.security.jaas;
 
+import javax.crypto.Mac;
 import javax.security.auth.callback.Callback;
-import java.security.Principal;
 
 /**
- * A Callback for kerberos peer principal.
+ * Callback for obtaining information about a used H{@link Mac}
  */
-public class Krb5Callback implements Callback {
+public class HmacCallback implements Callback {
 
-   Principal peerPrincipal;
+   private Mac hmac;
 
    /**
-    * Setter for peer Principal.
-    *
-    * @param principal The certificates to be returned.
+    * set the Hmac to use
+    * @param hmac
     */
-   public void setPeerPrincipal(Principal principal) {
-      peerPrincipal = principal;
+   public void setHmac(Mac hmac) {
+      this.hmac = hmac;
    }
 
    /**
-    * Getter for peer Principal.
-    *
-    * @return The principal being carried.
+    * @return the Hmac or <code>null</code> if non could be obtained
     */
-   public Principal getPeerPrincipal() {
-      return peerPrincipal;
+   public Mac getHmac() {
+      return hmac;
    }
+
 }
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/JaasCallbackHandler.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/JaasCallbackHandler.java
index a765f45..e0c6989 100644
--- a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/JaasCallbackHandler.java
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/JaasCallbackHandler.java
@@ -16,7 +16,12 @@
  */
 package org.apache.activemq.artemis.spi.core.security.jaas;
 
-import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection;
+import static org.apache.activemq.artemis.core.remoting.CertificateUtil.getCertsFromConnection;
+import static org.apache.activemq.artemis.core.remoting.CertificateUtil.getPeerPrincipalFromConnection;
+
+import java.io.IOException;
+import java.security.Principal;
+import java.util.Set;
 
 import javax.security.auth.Subject;
 import javax.security.auth.callback.Callback;
@@ -25,11 +30,8 @@ import javax.security.auth.callback.NameCallback;
 import javax.security.auth.callback.PasswordCallback;
 import javax.security.auth.callback.UnsupportedCallbackException;
 import javax.security.auth.kerberos.KerberosPrincipal;
-import java.io.IOException;
-import java.security.Principal;
 
-import static org.apache.activemq.artemis.core.remoting.CertificateUtil.getCertsFromConnection;
-import static org.apache.activemq.artemis.core.remoting.CertificateUtil.getPeerPrincipalFromConnection;
+import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection;
 
 /**
  * A JAAS username password CallbackHandler.
@@ -67,18 +69,26 @@ public class JaasCallbackHandler implements CallbackHandler {
             CertificateCallback certCallback = (CertificateCallback) callback;
 
             certCallback.setCertificates(getCertsFromConnection(remotingConnection));
-         } else if (callback instanceof Krb5Callback) {
-            Krb5Callback krb5Callback = (Krb5Callback) callback;
+         } else if (callback instanceof PrincipalsCallback) {
+            PrincipalsCallback principalsCallback = (PrincipalsCallback) callback;
 
             Subject peerSubject = remotingConnection.getSubject();
             if (peerSubject != null) {
-               for (Principal principal : peerSubject.getPrivateCredentials(KerberosPrincipal.class)) {
-                  krb5Callback.setPeerPrincipal(principal);
+               for (KerberosPrincipal principal : peerSubject.getPrivateCredentials(KerberosPrincipal.class)) {
+                  principalsCallback.setPeerPrincipals(new Principal[] {principal});
+                  return;
+               }
+               Set<Principal> principals = peerSubject.getPrincipals();
+               if (principals.size() > 0) {
+                  principalsCallback.setPeerPrincipals(principals.toArray(new Principal[0]));
                   return;
                }
             }
 
-            krb5Callback.setPeerPrincipal(getPeerPrincipalFromConnection(remotingConnection));
+            Principal peerPrincipalFromConnection = getPeerPrincipalFromConnection(remotingConnection);
+            if (peerPrincipalFromConnection != null) {
+               principalsCallback.setPeerPrincipals(new Principal[] {peerPrincipalFromConnection});
+            }
          } else {
             throw new UnsupportedCallbackException(callback);
          }
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/Krb5LoginModule.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/Krb5LoginModule.java
index 1047e08..a3cecb1 100644
--- a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/Krb5LoginModule.java
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/Krb5LoginModule.java
@@ -16,102 +16,10 @@
  */
 package org.apache.activemq.artemis.spi.core.security.jaas;
 
-import org.jboss.logging.Logger;
-
-import javax.security.auth.Subject;
-import javax.security.auth.callback.Callback;
-import javax.security.auth.callback.CallbackHandler;
-import javax.security.auth.callback.UnsupportedCallbackException;
-import javax.security.auth.login.LoginException;
-import java.io.IOException;
-import java.security.Principal;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-
 /**
  * populate a subject with kerberos credential from the handler
  */
-public class Krb5LoginModule implements AuditLoginModule {
-
-   private static final Logger logger = Logger.getLogger(Krb5LoginModule.class);
-
-   private Subject subject;
-   private final List<Principal> principals = new LinkedList<>();
-   private CallbackHandler callbackHandler;
-   private boolean loginSucceeded;
-   private Principal principal;
-
-   @Override
-   public void initialize(Subject subject,
-                          CallbackHandler callbackHandler,
-                          Map<String, ?> sharedState,
-                          Map<String, ?> options) {
-      this.subject = subject;
-      this.callbackHandler = callbackHandler;
-   }
-
-   @Override
-   public boolean login() throws LoginException {
-      Callback[] callbacks = new Callback[1];
-
-      callbacks[0] = new Krb5Callback();
-      try {
-         callbackHandler.handle(callbacks);
-         principal = ((Krb5Callback)callbacks[0]).getPeerPrincipal();
-         if (principal != null) {
-            principals.add(principal);
-         }
-      } catch (IOException ioe) {
-         throw new LoginException(ioe.getMessage());
-      } catch (UnsupportedCallbackException uce) {
-         throw new LoginException(uce.getMessage() + " not available to obtain information from user");
-      }
-      if (!principals.isEmpty()) {
-         loginSucceeded = true;
-      }
-      logger.debug("login " + principals);
-      return loginSucceeded;
-   }
-
-   @Override
-   public boolean commit() throws LoginException {
-      boolean result = loginSucceeded;
-      if (result) {
-         principals.add(new UserPrincipal(principals.get(0).getName()));
-         subject.getPrincipals().addAll(principals);
-      }
-
-      clear();
-
-      logger.debug("commit, result: " + result);
-
-      return result;
-   }
-
-   @Override
-   public boolean abort() throws LoginException {
-      registerFailureForAudit(principal != null ? principal.getName() : null);
-      clear();
-
-      logger.debug("abort");
-
-      return true;
-   }
-
-   private void clear() {
-      principal = null;
-      loginSucceeded = false;
-   }
-
-   @Override
-   public boolean logout() throws LoginException {
-      subject.getPrincipals().removeAll(principals);
-      principals.clear();
-      clear();
+public class Krb5LoginModule extends AbstractPrincipalLoginModule {
 
-      logger.debug("logout");
 
-      return true;
-   }
 }
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/Krb5Callback.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/PrincipalsCallback.java
similarity index 74%
copy from artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/Krb5Callback.java
copy to artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/PrincipalsCallback.java
index 9306d5f..821f6fb 100644
--- a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/Krb5Callback.java
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/PrincipalsCallback.java
@@ -16,31 +16,30 @@
  */
 package org.apache.activemq.artemis.spi.core.security.jaas;
 
-import javax.security.auth.callback.Callback;
 import java.security.Principal;
 
+import javax.security.auth.callback.Callback;
+
 /**
- * A Callback for kerberos peer principal.
+ * A Callback for getting the peer principals.
  */
-public class Krb5Callback implements Callback {
+public class PrincipalsCallback implements Callback {
 
-   Principal peerPrincipal;
+   Principal[] peerPrincipals;
 
    /**
-    * Setter for peer Principal.
-    *
+    * Setter for peer Principals.
     * @param principal The certificates to be returned.
     */
-   public void setPeerPrincipal(Principal principal) {
-      peerPrincipal = principal;
+   public void setPeerPrincipals(Principal[] principal) {
+      peerPrincipals = principal;
    }
 
    /**
-    * Getter for peer Principal.
-    *
+    * Getter for peer Principals.
     * @return The principal being carried.
     */
-   public Principal getPeerPrincipal() {
-      return peerPrincipal;
+   public Principal[] getPeerPrincipals() {
+      return peerPrincipals;
    }
 }
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/Krb5Callback.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/SCRAMLoginModule.java
similarity index 60%
copy from artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/Krb5Callback.java
copy to artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/SCRAMLoginModule.java
index 9306d5f..4da5200 100644
--- a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/Krb5Callback.java
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/SCRAMLoginModule.java
@@ -16,31 +16,10 @@
  */
 package org.apache.activemq.artemis.spi.core.security.jaas;
 
-import javax.security.auth.callback.Callback;
-import java.security.Principal;
-
 /**
- * A Callback for kerberos peer principal.
+ * Handles the actual login after channel authentication has succeed
  */
-public class Krb5Callback implements Callback {
-
-   Principal peerPrincipal;
+public class SCRAMLoginModule extends AbstractPrincipalLoginModule {
 
-   /**
-    * Setter for peer Principal.
-    *
-    * @param principal The certificates to be returned.
-    */
-   public void setPeerPrincipal(Principal principal) {
-      peerPrincipal = principal;
-   }
 
-   /**
-    * Getter for peer Principal.
-    *
-    * @return The principal being carried.
-    */
-   public Principal getPeerPrincipal() {
-      return peerPrincipal;
-   }
 }
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/Krb5Callback.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/SCRAMMechanismCallback.java
similarity index 66%
rename from artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/Krb5Callback.java
rename to artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/SCRAMMechanismCallback.java
index 9306d5f..de66108 100644
--- a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/Krb5Callback.java
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/SCRAMMechanismCallback.java
@@ -17,30 +17,28 @@
 package org.apache.activemq.artemis.spi.core.security.jaas;
 
 import javax.security.auth.callback.Callback;
-import java.security.Principal;
 
 /**
- * A Callback for kerberos peer principal.
+ * callback to obtain the a mechanism used in a SASL-SCRAM authentication
  */
-public class Krb5Callback implements Callback {
+public class SCRAMMechanismCallback implements Callback {
 
-   Principal peerPrincipal;
+   private String name;
 
    /**
-    * Setter for peer Principal.
-    *
-    * @param principal The certificates to be returned.
+    * sets the name of the mechanism
+    * @param name the name of the mechanism
     */
-   public void setPeerPrincipal(Principal principal) {
-      peerPrincipal = principal;
+   public void setMechanism(String name) {
+      this.name = name;
+
    }
 
    /**
-    * Getter for peer Principal.
-    *
-    * @return The principal being carried.
+    * @return the name of the mechanism
     */
-   public Principal getPeerPrincipal() {
-      return peerPrincipal;
+   public String getMechanism() {
+      return name;
    }
+
 }
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/SCRAMPropertiesLoginModule.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/SCRAMPropertiesLoginModule.java
new file mode 100644
index 0000000..7f52f58
--- /dev/null
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/SCRAMPropertiesLoginModule.java
@@ -0,0 +1,237 @@
+/*
+ * 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.activemq.artemis.spi.core.security.jaas;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.MessageDigest;
+import java.security.Principal;
+import java.security.SecureRandom;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import javax.crypto.Mac;
+import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.auth.login.LoginException;
+
+import org.apache.activemq.artemis.spi.core.security.scram.SCRAM;
+import org.apache.activemq.artemis.spi.core.security.scram.ScramException;
+import org.apache.activemq.artemis.spi.core.security.scram.ScramUtils;
+import org.apache.activemq.artemis.spi.core.security.scram.StringPrep;
+import org.apache.activemq.artemis.spi.core.security.scram.StringPrep.StringPrepError;
+import org.apache.activemq.artemis.spi.core.security.scram.UserData;
+import org.apache.activemq.artemis.utils.PasswordMaskingUtil;
+
+/**
+ * Login modules that uses properties files similar to the {@link PropertiesLoginModule}. It can
+ * either store the username-password in plain text or in an encrypted/hashed form. the
+ * {@link #main(String[])} method provides a way to prepare unencrypted data to be encrypted/hashed.
+ */
+public class SCRAMPropertiesLoginModule extends PropertiesLoader implements AuditLoginModule {
+
+   /**
+    *
+    */
+   private static final String SEPARATOR_MECHANISM = "|";
+   private static final String SEPARATOR_PARAMETER = ":";
+   private static final int MIN_ITERATIONS = 4096;
+   private static final SecureRandom RANDOM_GENERATOR = new SecureRandom();
+   private Subject subject;
+   private CallbackHandler callbackHandler;
+   private Properties users;
+   private Map<String, Set<String>> roles;
+   private UserData userData;
+   private String user;
+   private final Set<Principal> principals = new HashSet<>();
+
+   @Override
+   public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState,
+                          Map<String, ?> options) {
+      this.subject = subject;
+      this.callbackHandler = callbackHandler;
+
+      init(options);
+      users = load(PropertiesLoginModule.USER_FILE_PROP_NAME, "user", options).getProps();
+      roles = load(PropertiesLoginModule.ROLE_FILE_PROP_NAME, "role", options).invertedPropertiesValuesMap();
+
+   }
+
+   @Override
+   public boolean login() throws LoginException {
+      NameCallback nameCallback = new NameCallback("Username: ");
+      executeCallbacks(nameCallback);
+      user = nameCallback.getName();
+      SCRAMMechanismCallback mechanismCallback = new SCRAMMechanismCallback();
+      executeCallbacks(mechanismCallback);
+      SCRAM scram = getTypeByString(mechanismCallback.getMechanism());
+      if (user == null) {
+         userData = generateUserData(null); // generate random user data
+      } else {
+         String password = users.getProperty(user + SEPARATOR_MECHANISM + scram.name());
+         if (password == null) {
+            // fallback for probably unencoded user/password or a single encoded entry
+            password = users.getProperty(user);
+         }
+         if (PasswordMaskingUtil.isEncMasked(password)) {
+            String[] unwrap = PasswordMaskingUtil.unwrap(password).split(SEPARATOR_PARAMETER);
+            userData = new UserData(unwrap[0], Integer.parseInt(unwrap[1]), unwrap[2], unwrap[3]);
+         } else {
+            userData = generateUserData(password);
+         }
+      }
+      return true;
+   }
+
+   private UserData generateUserData(String plainTextPassword) throws LoginException {
+      if (plainTextPassword == null) {
+         // if the user is not available (or the password) generate a random password here so an
+         // attacker can't
+         // distinguish between a missing username and a wrong password
+         byte[] randomPassword = new byte[256];
+         RANDOM_GENERATOR.nextBytes(randomPassword);
+         plainTextPassword = new String(randomPassword);
+      }
+      DigestCallback digestCallback = new DigestCallback();
+      HmacCallback hmacCallback = new HmacCallback();
+      executeCallbacks(digestCallback, hmacCallback);
+      byte[] salt = generateSalt();
+      try {
+         ScramUtils.NewPasswordStringData data =
+                  ScramUtils.byteArrayToStringData(ScramUtils.newPassword(plainTextPassword, salt, 4096,
+                                                                          digestCallback.getDigest(),
+                                                                          hmacCallback.getHmac()));
+         return new UserData(data.salt, data.iterations, data.serverKey, data.storedKey);
+      } catch (ScramException e) {
+         throw new LoginException();
+      }
+   }
+
+   private static byte[] generateSalt() {
+      byte[] salt = new byte[32];
+      RANDOM_GENERATOR.nextBytes(salt);
+      return salt;
+   }
+
+   private void executeCallbacks(Callback... callbacks) throws LoginException {
+      try {
+         callbackHandler.handle(callbacks);
+      } catch (UnsupportedCallbackException | IOException e) {
+         throw new LoginException();
+      }
+   }
+
+   @Override
+   public boolean commit() throws LoginException {
+      if (userData == null) {
+         throw new LoginException();
+      }
+      subject.getPublicCredentials().add(userData);
+      Set<UserPrincipal> authenticatedUsers = subject.getPrincipals(UserPrincipal.class);
+      UserPrincipal principal = new UserPrincipal(user);
+      principals.add(principal);
+      authenticatedUsers.add(principal);
+      for (UserPrincipal userPrincipal : authenticatedUsers) {
+         Set<String> matchedRoles = roles.get(userPrincipal.getName());
+         if (matchedRoles != null) {
+            for (String entry : matchedRoles) {
+               principals.add(new RolePrincipal(entry));
+            }
+         }
+      }
+      subject.getPrincipals().addAll(principals);
+      return true;
+   }
+
+   @Override
+   public boolean abort() throws LoginException {
+      return true;
+   }
+
+   @Override
+   public boolean logout() throws LoginException {
+      subject.getPrincipals().removeAll(principals);
+      principals.clear();
+      subject.getPublicCredentials().remove(userData);
+      userData = null;
+      return true;
+   }
+
+   /**
+    * Main method that could be used to encrypt given credentials for use in properties files
+    * @param args username password type [iterations]
+    * @throws GeneralSecurityException if any security mechanism is not available on this JVM
+    * @throws ScramException if invalid data is supplied
+    * @throws StringPrepError if username can't be encoded according to SASL StringPrep
+    * @throws IOException if writing as properties failed
+    */
+   public static void main(String[] args) throws GeneralSecurityException, ScramException, StringPrepError,
+                                          IOException {
+      if (args.length < 2) {
+         System.out.println("Usage: " + SCRAMPropertiesLoginModule.class.getSimpleName() +
+                  " <username> <password> [<iterations>]");
+         System.out.println("\ttype: " + getSupportedTypes());
+         System.out.println("\titerations desired number of iteration (min value: " + MIN_ITERATIONS + ")");
+         return;
+      }
+      String username = args[0];
+      String password = args[1];
+      Properties properties = new Properties();
+      String encodedUser = StringPrep.prepAsQueryString(username);
+      for (SCRAM scram : SCRAM.values()) {
+         MessageDigest digest = MessageDigest.getInstance(scram.getDigest());
+         Mac hmac = Mac.getInstance(scram.getHmac());
+         byte[] salt = generateSalt();
+         int iterations;
+         if (args.length > 2) {
+            iterations = Integer.parseInt(args[2]);
+            if (iterations < MIN_ITERATIONS) {
+               throw new IllegalArgumentException("minimum of " + MIN_ITERATIONS + " required!");
+            }
+         } else {
+            iterations = MIN_ITERATIONS;
+         }
+         ScramUtils.NewPasswordStringData data =
+                  ScramUtils.byteArrayToStringData(ScramUtils.newPassword(password, salt, iterations, digest, hmac));
+         String encodedPassword = PasswordMaskingUtil.wrap(data.salt + SEPARATOR_PARAMETER + data.iterations +
+                  SEPARATOR_PARAMETER + data.serverKey + SEPARATOR_PARAMETER + data.storedKey);
+         properties.setProperty(encodedUser + SEPARATOR_MECHANISM + scram.name(), encodedPassword);
+      }
+      properties.store(System.out,
+                       "Insert the lines stating with '" + encodedUser + "' into the desired user properties file");
+   }
+
+   private static SCRAM getTypeByString(String type) {
+      SCRAM scram = Arrays.stream(SCRAM.values())
+                          .filter(v -> v.getName().equals(type))
+                          .findFirst()
+                          .orElseThrow(() -> new IllegalArgumentException("unkown type " + type +
+                                   ", supported ones are " + getSupportedTypes()));
+      return scram;
+   }
+
+   private static String getSupportedTypes() {
+      return String.join(", ", Arrays.stream(SCRAM.values()).map(SCRAM::getName).toArray(String[]::new));
+   }
+
+}
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/scram/SCRAM.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/scram/SCRAM.java
new file mode 100644
index 0000000..1da4c42
--- /dev/null
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/scram/SCRAM.java
@@ -0,0 +1,56 @@
+/*
+ * 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.activemq.artemis.spi.core.security.scram;
+
+/**
+ * Defines sets of known SCRAM types with methods to fetch matching digest and hmac names
+ */
+public enum SCRAM {
+                   // ordered by precedence
+                   SHA512,
+                   SHA256;
+
+   public String getName() {
+      switch (this) {
+         case SHA256:
+            return "SCRAM-SHA-256";
+         case SHA512:
+            return "SCRAM-SHA-512";
+      }
+      throw new UnsupportedOperationException();
+   }
+
+   public String getDigest() {
+      switch (this) {
+         case SHA256:
+            return "SHA-256";
+         case SHA512:
+            return "SHA-512";
+      }
+      throw new UnsupportedOperationException();
+   }
+
+   public String getHmac() {
+      switch (this) {
+         case SHA256:
+            return "HmacSHA256";
+         case SHA512:
+            return "HmacSHA512";
+      }
+      throw new UnsupportedOperationException();
+   }
+}
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/scram/ScramException.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/scram/ScramException.java
new file mode 100644
index 0000000..e6ea177
--- /dev/null
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/scram/ScramException.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2016 Ognyan Bankov
+ * <p>
+ * All rights reserved. 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.activemq.artemis.spi.core.security.scram;
+
+import java.security.GeneralSecurityException;
+
+/**
+ * Indicates error while processing SCRAM sequence
+ */
+public class ScramException extends Exception {
+   /**
+    * Creates new ScramException
+    * @param message Exception message
+    */
+   public ScramException(String message) {
+      super(message);
+   }
+
+   public ScramException(String message, GeneralSecurityException e) {
+      super(message, e);
+   }
+
+   /**
+    * Creates new ScramException
+    * @param cause Throwable
+    */
+   public ScramException(Throwable cause) {
+      super(cause);
+   }
+}
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/scram/ScramUtils.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/scram/ScramUtils.java
new file mode 100644
index 0000000..8a98a05
--- /dev/null
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/scram/ScramUtils.java
@@ -0,0 +1,264 @@
+/*
+ * Copyright 2016 Ognyan Bankov
+ * <p>
+ * All rights reserved. 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.activemq.artemis.spi.core.security.scram;
+
+import java.nio.charset.StandardCharsets;
+import java.security.InvalidKeyException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Base64;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+
+/**
+ * Provides static methods for working with SCRAM/SASL
+ */
+public class ScramUtils {
+   private static final byte[] INT_1 = new byte[] {0, 0, 0, 1};
+
+   private ScramUtils() {
+      throw new AssertionError("non-instantiable utility class");
+   }
+
+   /**
+    * Generates salted password.
+    * @param password Clear form password, i.e. what user typed
+    * @param salt Salt to be used
+    * @param iterationsCount Iterations for 'salting'
+    * @param mac HMAC to be used
+    * @return salted password
+    * @throws ScramException
+    */
+   public static byte[] generateSaltedPassword(final String password, byte[] salt, int iterationsCount,
+                                               Mac mac) throws ScramException {
+      SecretKeySpec key = new SecretKeySpec(password.getBytes(StandardCharsets.US_ASCII), mac.getAlgorithm());
+      try {
+         mac.init(key);
+      } catch (InvalidKeyException e) {
+         throw new ScramException("Incompatible key", e);
+      }
+      mac.update(salt);
+      mac.update(INT_1);
+      byte[] result = mac.doFinal();
+
+      byte[] previous = null;
+      for (int i = 1; i < iterationsCount; i++) {
+         mac.update(previous != null ? previous : result);
+         previous = mac.doFinal();
+         for (int x = 0; x < result.length; x++) {
+            result[x] ^= previous[x];
+         }
+      }
+
+      return result;
+   }
+
+   /**
+    * Creates HMAC
+    * @param keyBytes key
+    * @param hmacName HMAC name
+    * @return Mac
+    * @throws InvalidKeyException if internal error occur while working with SecretKeySpec
+    * @throws NoSuchAlgorithmException if hmacName is not supported by the java
+    */
+   public static Mac createHmac(final byte[] keyBytes, String hmacName) throws NoSuchAlgorithmException,
+                                                                        InvalidKeyException {
+
+      Mac mac = Mac.getInstance(hmacName);
+      SecretKeySpec key = new SecretKeySpec(keyBytes, hmacName);
+      mac.init(key);
+      return mac;
+   }
+
+   /**
+    * Computes HMAC byte array for given string
+    * @param key key
+    * @param hmacName HMAC name
+    * @param string string for which HMAC will be computed
+    * @return computed HMAC
+    * @throws InvalidKeyException if internal error occur while working with SecretKeySpec
+    * @throws NoSuchAlgorithmException if hmacName is not supported by the java
+    */
+   public static byte[] computeHmac(final byte[] key, String hmacName, final String string) throws InvalidKeyException,
+                                                                                            NoSuchAlgorithmException {
+
+      Mac mac = createHmac(key, hmacName);
+      mac.update(string.getBytes(StandardCharsets.US_ASCII));
+      return mac.doFinal();
+   }
+
+   public static byte[] computeHmac(final byte[] key, Mac hmac, final String string) throws ScramException {
+
+      try {
+         hmac.init(new SecretKeySpec(key, hmac.getAlgorithm()));
+      } catch (InvalidKeyException e) {
+         throw new ScramException("invalid key", e);
+      }
+      hmac.update(string.getBytes(StandardCharsets.US_ASCII));
+      return hmac.doFinal();
+   }
+
+   /**
+    * Checks if string is null or empty
+    * @param string String to be tested
+    * @return true if the string is null or empty, false otherwise
+    */
+   public static boolean isNullOrEmpty(String string) {
+      return string == null || string.length() == 0; // string.isEmpty() in Java 6
+   }
+
+   /**
+    * Computes the data associated with new password like salted password, keys, etc
+    * <p>
+    * This method is supposed to be used by a server when user provides new clear form password. We
+    * don't want to save it that way so we generate salted password and store it along with other
+    * data required by the SCRAM mechanism
+    * @param passwordClearText Clear form password, i.e. as provided by the user
+    * @param salt Salt to be used
+    * @param iterations Iterations for 'salting'
+    * @param mac HMAC name to be used
+    * @param messageDigest Digest name to be used
+    * @return new password data while working with SecretKeySpec
+    * @throws ScramException
+    */
+   public static NewPasswordByteArrayData newPassword(String passwordClearText, byte[] salt, int iterations,
+                                                      MessageDigest messageDigest, Mac mac) throws ScramException {
+      byte[] saltedPassword = ScramUtils.generateSaltedPassword(passwordClearText, salt, iterations, mac);
+
+      byte[] clientKey = ScramUtils.computeHmac(saltedPassword, mac, "Client Key");
+      byte[] storedKey = messageDigest.digest(clientKey);
+      byte[] serverKey = ScramUtils.computeHmac(saltedPassword, mac, "Server Key");
+
+      return new NewPasswordByteArrayData(saltedPassword, salt, clientKey, storedKey, serverKey, iterations);
+   }
+
+   /**
+    * Transforms NewPasswordByteArrayData into NewPasswordStringData into database friendly (string)
+    * representation Uses Base64 to encode the byte arrays into strings
+    * @param ba Byte array data
+    * @return String data
+    */
+   public static NewPasswordStringData byteArrayToStringData(NewPasswordByteArrayData ba) {
+      return new NewPasswordStringData(Base64.getEncoder().encodeToString(ba.saltedPassword),
+                                       Base64.getEncoder().encodeToString(ba.salt),
+                                       Base64.getEncoder().encodeToString(ba.clientKey),
+                                       Base64.getEncoder().encodeToString(ba.storedKey),
+                                       Base64.getEncoder().encodeToString(ba.serverKey), ba.iterations);
+   }
+
+   /**
+    * New password data in database friendly format, i.e. Base64 encoded strings
+    */
+   @SuppressWarnings("unused")
+   public static class NewPasswordStringData {
+      /**
+       * Salted password
+       */
+      public final String saltedPassword;
+      /**
+       * Used salt
+       */
+      public final String salt;
+      /**
+       * Client key
+       */
+      public final String clientKey;
+      /**
+       * Stored key
+       */
+      public final String storedKey;
+      /**
+       * Server key
+       */
+      public final String serverKey;
+      /**
+       * Iterations for slating
+       */
+      public final int iterations;
+
+      /**
+       * Creates new NewPasswordStringData
+       * @param saltedPassword Salted password
+       * @param salt Used salt
+       * @param clientKey Client key
+       * @param storedKey Stored key
+       * @param serverKey Server key
+       * @param iterations Iterations for slating
+       */
+      public NewPasswordStringData(String saltedPassword, String salt, String clientKey, String storedKey,
+                                   String serverKey, int iterations) {
+         this.saltedPassword = saltedPassword;
+         this.salt = salt;
+         this.clientKey = clientKey;
+         this.storedKey = storedKey;
+         this.serverKey = serverKey;
+         this.iterations = iterations;
+      }
+   }
+
+   /**
+    * New password data in byte array format
+    */
+   @SuppressWarnings("unused")
+   public static class NewPasswordByteArrayData {
+      /**
+       * Salted password
+       */
+      public final byte[] saltedPassword;
+      /**
+       * Used salt
+       */
+      public final byte[] salt;
+      /**
+       * Client key
+       */
+      public final byte[] clientKey;
+      /**
+       * Stored key
+       */
+      public final byte[] storedKey;
+      /**
+       * Server key
+       */
+      public final byte[] serverKey;
+      /**
+       * Iterations for slating
+       */
+      public final int iterations;
+
+      /**
+       * Creates new NewPasswordByteArrayData
+       * @param saltedPassword Salted password
+       * @param salt Used salt
+       * @param clientKey Client key
+       * @param storedKey Stored key
+       * @param serverKey Server key
+       * @param iterations Iterations for slating
+       */
+      public NewPasswordByteArrayData(byte[] saltedPassword, byte[] salt, byte[] clientKey, byte[] storedKey,
+                                      byte[] serverKey, int iterations) {
+
+         this.saltedPassword = saltedPassword;
+         this.salt = salt;
+         this.clientKey = clientKey;
+         this.storedKey = storedKey;
+         this.serverKey = serverKey;
+         this.iterations = iterations;
+      }
+   }
+}
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/scram/StringPrep.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/scram/StringPrep.java
new file mode 100644
index 0000000..8c73752
--- /dev/null
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/scram/StringPrep.java
@@ -0,0 +1,2139 @@
+/**
+ * Copyright 2011 Glenn Maynard
+ * <p>
+ * All rights reserved. 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.activemq.artemis.spi.core.security.scram;
+
+import java.text.Normalizer;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+/**
+ * rfc3454 StringPrep, with an implementation of rfc4013 SASLPrep.
+ * <p>
+ * StringPrep case folding is unimplemented, as it's not required by SASLPrep.
+ */
+public class StringPrep {
+   /**
+    * A representation of sets of character classes.
+    */
+   protected static class CharClass {
+      // Each character class is a set of [start,end] tuples; each tuple is represented
+      // in the mapping as mapping[start] = (end-start+1).
+      // Invariants:
+      // - tupleStart is in ascending order.
+      // - All values in tupleCount are >= 1 (no empty ranges).
+      // - tupleStart.size() == tupleCount.size().
+      // - There will be no overlapping ranges.
+      //
+      // TreeMap would work well for this, but it was missing basic operations like lowerEntry
+      // until JDK1.6, which we don't want to depend on.
+      private final ArrayList<Integer> tupleStart = new ArrayList<>();
+      private final ArrayList<Integer> tupleCount = new ArrayList<>();
+
+      static CharClass fromList(int[] charMap) {
+         SortedMap<Integer, Integer> mapping = new TreeMap<>();
+         for (int element : charMap)
+            mapping.put(element, 1);
+
+         return new CharClass(mapping);
+      }
+
+      static CharClass fromRanges(int[] charMap) {
+         // There must be an even number of tuples in RANGES tables.
+         if ((charMap.length % 2) != 0)
+            throw new IllegalArgumentException("Invalid character list size");
+
+         SortedMap<Integer, Integer> mapping = new TreeMap<>();
+         for (int i = 0; i < charMap.length; i += 2) {
+            int start = charMap[i];
+            int end = charMap[i + 1];
+            int count = end - start + 1;
+            mapping.put(start, count);
+         }
+
+         return new CharClass(mapping);
+      }
+
+      static CharClass fromClasses(CharClass... classes) {
+         SortedMap<Integer, Integer> mapping = new TreeMap<>();
+         for (CharClass charClass : classes) {
+            for (int i = 0; i < charClass.tupleStart.size(); ++i) {
+               int start = charClass.tupleStart.get(i);
+               int count = charClass.tupleCount.get(i);
+               mapping.put(start, count);
+            }
+         }
+
+         return new CharClass(mapping);
+      }
+
+      private CharClass(SortedMap<Integer, Integer> mappings) {
+         for (Map.Entry<Integer, Integer> pair : mappings.entrySet()) {
+            int start = pair.getKey();
+            int count = pair.getValue();
+
+            // Coalesce overlapping ranges.
+            if (tupleStart.size() > 0) {
+               int prevIndex = tupleStart.size() - 1;
+               int prevStart = tupleStart.get(prevIndex);
+               int prevCount = tupleCount.get(prevIndex);
+               // If the previous tuple is (0,1), and this tuple is (1,5), then
+               // coalesce into (0,6). If ths previous tuple is (0,3) and this
+               // tuple is (1,3), coalesce into (0,4).
+               if (prevStart + prevCount >= start) {
+                  int endPos = start + count;
+                  int newCount = endPos - prevStart;
+                  tupleCount.set(prevIndex, newCount);
+                  continue;
+               }
+            }
+
+            tupleStart.add(start);
+            tupleCount.add(count);
+         }
+      }
+
+      public boolean isCharInClass(int c) {
+         // Find the first entry in tupleStart which is <= c. Java's binarySearch
+         // API is a bit braindamaged (it was written only considering search-and-insert),
+         // so we have to jump some hoops to get this.
+         int pos = Collections.binarySearch(tupleStart, c);
+         if (pos >= 0) {
+            // If pos >= 0, tupleStart[pos] == c. The value is the start of a range,
+            // so it's included in the class.
+            return true;
+            // while(pos > 0 && tupleStart.get(pos-1) == c)
+            // --pos;
+         }
+         // -pos - 1 is the lowest index where tupleStart[pos] > c. If this is the
+         // first entry, then c is below all entries in the class.
+         pos = -pos - 1;
+         if (pos == 0)
+            return false;
+         --pos;
+
+         // tupleStart[pos] is <= c.
+         int start = tupleStart.get(pos);
+         int count = tupleCount.get(pos);
+         return start <= c && c < start + count;
+
+      }
+   }
+
+   /** A.1 Unassigned code points in Unicode 3.2 */
+   static final CharClass A1 = CharClass.fromRanges(new int[] {0x0221,
+                                                               0x0221,
+                                                               0x0234,
+                                                               0x024F,
+                                                               0x02AE,
+                                                               0x02AF,
+                                                               0x02EF,
+                                                               0x02FF,
+                                                               0x0350,
+                                                               0x035F,
+                                                               0x0370,
+                                                               0x0373,
+                                                               0x0376,
+                                                               0x0379,
+                                                               0x037B,
+                                                               0x037D,
+                                                               0x037F,
+                                                               0x0383,
+                                                               0x038B,
+                                                               0x038B,
+                                                               0x038D,
+                                                               0x038D,
+                                                               0x03A2,
+                                                               0x03A2,
+                                                               0x03CF,
+                                                               0x03CF,
+                                                               0x03F7,
+                                                               0x03FF,
+                                                               0x0487,
+                                                               0x0487,
+                                                               0x04CF,
+                                                               0x04CF,
+                                                               0x04F6,
+                                                               0x04F7,
+                                                               0x04FA,
+                                                               0x04FF,
+                                                               0x0510,
+                                                               0x0530,
+                                                               0x0557,
+                                                               0x0558,
+                                                               0x0560,
+                                                               0x0560,
+                                                               0x0588,
+                                                               0x0588,
+                                                               0x058B,
+                                                               0x0590,
+                                                               0x05A2,
+                                                               0x05A2,
+                                                               0x05BA,
+                                                               0x05BA,
+                                                               0x05C5,
+                                                               0x05CF,
+                                                               0x05EB,
+                                                               0x05EF,
+                                                               0x05F5,
+                                                               0x060B,
+                                                               0x060D,
+                                                               0x061A,
+                                                               0x061C,
+                                                               0x061E,
+                                                               0x0620,
+                                                               0x0620,
+                                                               0x063B,
+                                                               0x063F,
+                                                               0x0656,
+                                                               0x065F,
+                                                               0x06EE,
+                                                               0x06EF,
+                                                               0x06FF,
+                                                               0x06FF,
+                                                               0x070E,
+                                                               0x070E,
+                                                               0x072D,
+                                                               0x072F,
+                                                               0x074B,
+                                                               0x077F,
+                                                               0x07B2,
+                                                               0x0900,
+                                                               0x0904,
+                                                               0x0904,
+                                                               0x093A,
+                                                               0x093B,
+                                                               0x094E,
+                                                               0x094F,
+                                                               0x0955,
+                                                               0x0957,
+                                                               0x0971,
+                                                               0x0980,
+                                                               0x0984,
+                                                               0x0984,
+                                                               0x098D,
+                                                               0x098E,
+                                                               0x0991,
+                                                               0x0992,
+                                                               0x09A9,
+                                                               0x09A9,
+                                                               0x09B1,
+                                                               0x09B1,
+                                                               0x09B3,
+                                                               0x09B5,
+                                                               0x09BA,
+                                                               0x09BB,
+                                                               0x09BD,
+                                                               0x09BD,
+                                                               0x09C5,
+                                                               0x09C6,
+                                                               0x09C9,
+                                                               0x09CA,
+                                                               0x09CE,
+                                                               0x09D6,
+                                                               0x09D8,
+                                                               0x09DB,
+                                                               0x09DE,
+                                                               0x09DE,
+                                                               0x09E4,
+                                                               0x09E5,
+                                                               0x09FB,
+                                                               0x0A01,
+                                                               0x0A03,
+                                                               0x0A04,
+                                                               0x0A0B,
+                                                               0x0A0E,
+                                                               0x0A11,
+                                                               0x0A12,
+                                                               0x0A29,
+                                                               0x0A29,
+                                                               0x0A31,
+                                                               0x0A31,
+                                                               0x0A34,
+                                                               0x0A34,
+                                                               0x0A37,
+                                                               0x0A37,
+                                                               0x0A3A,
+                                                               0x0A3B,
+                                                               0x0A3D,
+                                                               0x0A3D,
+                                                               0x0A43,
+                                                               0x0A46,
+                                                               0x0A49,
+                                                               0x0A4A,
+                                                               0x0A4E,
+                                                               0x0A58,
+                                                               0x0A5D,
+                                                               0x0A5D,
+                                                               0x0A5F,
+                                                               0x0A65,
+                                                               0x0A75,
+                                                               0x0A80,
+                                                               0x0A84,
+                                                               0x0A84,
+                                                               0x0A8C,
+                                                               0x0A8C,
+                                                               0x0A8E,
+                                                               0x0A8E,
+                                                               0x0A92,
+                                                               0x0A92,
+                                                               0x0AA9,
+                                                               0x0AA9,
+                                                               0x0AB1,
+                                                               0x0AB1,
+                                                               0x0AB4,
+                                                               0x0AB4,
+                                                               0x0ABA,
+                                                               0x0ABB,
+                                                               0x0AC6,
+                                                               0x0AC6,
+                                                               0x0ACA,
+                                                               0x0ACA,
+                                                               0x0ACE,
+                                                               0x0ACF,
+                                                               0x0AD1,
+                                                               0x0ADF,
+                                                               0x0AE1,
+                                                               0x0AE5,
+                                                               0x0AF0,
+                                                               0x0B00,
+                                                               0x0B04,
+                                                               0x0B04,
+                                                               0x0B0D,
+                                                               0x0B0E,
+                                                               0x0B11,
+                                                               0x0B12,
+                                                               0x0B29,
+                                                               0x0B29,
+                                                               0x0B31,
+                                                               0x0B31,
+                                                               0x0B34,
+                                                               0x0B35,
+                                                               0x0B3A,
+                                                               0x0B3B,
+                                                               0x0B44,
+                                                               0x0B46,
+                                                               0x0B49,
+                                                               0x0B4A,
+                                                               0x0B4E,
+                                                               0x0B55,
+                                                               0x0B58,
+                                                               0x0B5B,
+                                                               0x0B5E,
+                                                               0x0B5E,
+                                                               0x0B62,
+                                                               0x0B65,
+                                                               0x0B71,
+                                                               0x0B81,
+                                                               0x0B84,
+                                                               0x0B84,
+                                                               0x0B8B,
+                                                               0x0B8D,
+                                                               0x0B91,
+                                                               0x0B91,
+                                                               0x0B96,
+                                                               0x0B98,
+                                                               0x0B9B,
+                                                               0x0B9B,
+                                                               0x0B9D,
+                                                               0x0B9D,
+                                                               0x0BA0,
+                                                               0x0BA2,
+                                                               0x0BA5,
+                                                               0x0BA7,
+                                                               0x0BAB,
+                                                               0x0BAD,
+                                                               0x0BB6,
+                                                               0x0BB6,
+                                                               0x0BBA,
+                                                               0x0BBD,
+                                                               0x0BC3,
+                                                               0x0BC5,
+                                                               0x0BC9,
+                                                               0x0BC9,
+                                                               0x0BCE,
+                                                               0x0BD6,
+                                                               0x0BD8,
+                                                               0x0BE6,
+                                                               0x0BF3,
+                                                               0x0C00,
+                                                               0x0C04,
+                                                               0x0C04,
+                                                               0x0C0D,
+                                                               0x0C0D,
+                                                               0x0C11,
+                                                               0x0C11,
+                                                               0x0C29,
+                                                               0x0C29,
+                                                               0x0C34,
+                                                               0x0C34,
+                                                               0x0C3A,
+                                                               0x0C3D,
+                                                               0x0C45,
+                                                               0x0C45,
+                                                               0x0C49,
+                                                               0x0C49,
+                                                               0x0C4E,
+                                                               0x0C54,
+                                                               0x0C57,
+                                                               0x0C5F,
+                                                               0x0C62,
+                                                               0x0C65,
+                                                               0x0C70,
+                                                               0x0C81,
+                                                               0x0C84,
+                                                               0x0C84,
+                                                               0x0C8D,
+                                                               0x0C8D,
+                                                               0x0C91,
+                                                               0x0C91,
+                                                               0x0CA9,
+                                                               0x0CA9,
+                                                               0x0CB4,
+                                                               0x0CB4,
+                                                               0x0CBA,
+                                                               0x0CBD,
+                                                               0x0CC5,
+                                                               0x0CC5,
+                                                               0x0CC9,
+                                                               0x0CC9,
+                                                               0x0CCE,
+                                                               0x0CD4,
+                                                               0x0CD7,
+                                                               0x0CDD,
+                                                               0x0CDF,
+                                                               0x0CDF,
+                                                               0x0CE2,
+                                                               0x0CE5,
+                                                               0x0CF0,
+                                                               0x0D01,
+                                                               0x0D04,
+                                                               0x0D04,
+                                                               0x0D0D,
+                                                               0x0D0D,
+                                                               0x0D11,
+                                                               0x0D11,
+                                                               0x0D29,
+                                                               0x0D29,
+                                                               0x0D3A,
+                                                               0x0D3D,
+                                                               0x0D44,
+                                                               0x0D45,
+                                                               0x0D49,
+                                                               0x0D49,
+                                                               0x0D4E,
+                                                               0x0D56,
+                                                               0x0D58,
+                                                               0x0D5F,
+                                                               0x0D62,
+                                                               0x0D65,
+                                                               0x0D70,
+                                                               0x0D81,
+                                                               0x0D84,
+                                                               0x0D84,
+                                                               0x0D97,
+                                                               0x0D99,
+                                                               0x0DB2,
+                                                               0x0DB2,
+                                                               0x0DBC,
+                                                               0x0DBC,
+                                                               0x0DBE,
+                                                               0x0DBF,
+                                                               0x0DC7,
+                                                               0x0DC9,
+                                                               0x0DCB,
+                                                               0x0DCE,
+                                                               0x0DD5,
+                                                               0x0DD5,
+                                                               0x0DD7,
+                                                               0x0DD7,
+                                                               0x0DE0,
+                                                               0x0DF1,
+                                                               0x0DF5,
+                                                               0x0E00,
+                                                               0x0E3B,
+                                                               0x0E3E,
+                                                               0x0E5C,
+                                                               0x0E80,
+                                                               0x0E83,
+                                                               0x0E83,
+                                                               0x0E85,
+                                                               0x0E86,
+                                                               0x0E89,
+                                                               0x0E89,
+                                                               0x0E8B,
+                                                               0x0E8C,
+                                                               0x0E8E,
+                                                               0x0E93,
+                                                               0x0E98,
+                                                               0x0E98,
+                                                               0x0EA0,
+                                                               0x0EA0,
+                                                               0x0EA4,
+                                                               0x0EA4,
+                                                               0x0EA6,
+                                                               0x0EA6,
+                                                               0x0EA8,
+                                                               0x0EA9,
+                                                               0x0EAC,
+                                                               0x0EAC,
+                                                               0x0EBA,
+                                                               0x0EBA,
+                                                               0x0EBE,
+                                                               0x0EBF,
+                                                               0x0EC5,
+                                                               0x0EC5,
+                                                               0x0EC7,
+                                                               0x0EC7,
+                                                               0x0ECE,
+                                                               0x0ECF,
+                                                               0x0EDA,
+                                                               0x0EDB,
+                                                               0x0EDE,
+                                                               0x0EFF,
+                                                               0x0F48,
+                                                               0x0F48,
+                                                               0x0F6B,
+                                                               0x0F70,
+                                                               0x0F8C,
+                                                               0x0F8F,
+                                                               0x0F98,
+                                                               0x0F98,
+                                                               0x0FBD,
+                                                               0x0FBD,
+                                                               0x0FCD,
+                                                               0x0FCE,
+                                                               0x0FD0,
+                                                               0x0FFF,
+                                                               0x1022,
+                                                               0x1022,
+                                                               0x1028,
+                                                               0x1028,
+                                                               0x102B,
+                                                               0x102B,
+                                                               0x1033,
+                                                               0x1035,
+                                                               0x103A,
+                                                               0x103F,
+                                                               0x105A,
+                                                               0x109F,
+                                                               0x10C6,
+                                                               0x10CF,
+                                                               0x10F9,
+                                                               0x10FA,
+                                                               0x10FC,
+                                                               0x10FF,
+                                                               0x115A,
+                                                               0x115E,
+                                                               0x11A3,
+                                                               0x11A7,
+                                                               0x11FA,
+                                                               0x11FF,
+                                                               0x1207,
+                                                               0x1207,
+                                                               0x1247,
+                                                               0x1247,
+                                                               0x1249,
+                                                               0x1249,
+                                                               0x124E,
+                                                               0x124F,
+                                                               0x1257,
+                                                               0x1257,
+                                                               0x1259,
+                                                               0x1259,
+                                                               0x125E,
+                                                               0x125F,
+                                                               0x1287,
+                                                               0x1287,
+                                                               0x1289,
+                                                               0x1289,
+                                                               0x128E,
+                                                               0x128F,
+                                                               0x12AF,
+                                                               0x12AF,
+                                                               0x12B1,
+                                                               0x12B1,
+                                                               0x12B6,
+                                                               0x12B7,
+                                                               0x12BF,
+                                                               0x12BF,
+                                                               0x12C1,
+                                                               0x12C1,
+                                                               0x12C6,
+                                                               0x12C7,
+                                                               0x12CF,
+                                                               0x12CF,
+                                                               0x12D7,
+                                                               0x12D7,
+                                                               0x12EF,
+                                                               0x12EF,
+                                                               0x130F,
+                                                               0x130F,
+                                                               0x1311,
+                                                               0x1311,
+                                                               0x1316,
+                                                               0x1317,
+                                                               0x131F,
+                                                               0x131F,
+                                                               0x1347,
+                                                               0x1347,
+                                                               0x135B,
+                                                               0x1360,
+                                                               0x137D,
+                                                               0x139F,
+                                                               0x13F5,
+                                                               0x1400,
+                                                               0x1677,
+                                                               0x167F,
+                                                               0x169D,
+                                                               0x169F,
+                                                               0x16F1,
+                                                               0x16FF,
+                                                               0x170D,
+                                                               0x170D,
+                                                               0x1715,
+                                                               0x171F,
+                                                               0x1737,
+                                                               0x173F,
+                                                               0x1754,
+                                                               0x175F,
+                                                               0x176D,
+                                                               0x176D,
+                                                               0x1771,
+                                                               0x1771,
+                                                               0x1774,
+                                                               0x177F,
+                                                               0x17DD,
+                                                               0x17DF,
+                                                               0x17EA,
+                                                               0x17FF,
+                                                               0x180F,
+                                                               0x180F,
+                                                               0x181A,
+                                                               0x181F,
+                                                               0x1878,
+                                                               0x187F,
+                                                               0x18AA,
+                                                               0x1DFF,
+                                                               0x1E9C,
+                                                               0x1E9F,
+                                                               0x1EFA,
+                                                               0x1EFF,
+                                                               0x1F16,
+                                                               0x1F17,
+                                                               0x1F1E,
+                                                               0x1F1F,
+                                                               0x1F46,
+                                                               0x1F47,
+                                                               0x1F4E,
+                                                               0x1F4F,
+                                                               0x1F58,
+                                                               0x1F58,
+                                                               0x1F5A,
+                                                               0x1F5A,
+                                                               0x1F5C,
+                                                               0x1F5C,
+                                                               0x1F5E,
+                                                               0x1F5E,
+                                                               0x1F7E,
+                                                               0x1F7F,
+                                                               0x1FB5,
+                                                               0x1FB5,
+                                                               0x1FC5,
+                                                               0x1FC5,
+                                                               0x1FD4,
+                                                               0x1FD5,
+                                                               0x1FDC,
+                                                               0x1FDC,
+                                                               0x1FF0,
+                                                               0x1FF1,
+                                                               0x1FF5,
+                                                               0x1FF5,
+                                                               0x1FFF,
+                                                               0x1FFF,
+                                                               0x2053,
+                                                               0x2056,
+                                                               0x2058,
+                                                               0x205E,
+                                                               0x2064,
+                                                               0x2069,
+                                                               0x2072,
+                                                               0x2073,
+                                                               0x208F,
+                                                               0x209F,
+                                                               0x20B2,
+                                                               0x20CF,
+                                                               0x20EB,
+                                                               0x20FF,
+                                                               0x213B,
+                                                               0x213C,
+                                                               0x214C,
+                                                               0x2152,
+                                                               0x2184,
+                                                               0x218F,
+                                                               0x23CF,
+                                                               0x23FF,
+                                                               0x2427,
+                                                               0x243F,
+                                                               0x244B,
+                                                               0x245F,
+                                                               0x24FF,
+                                                               0x24FF,
+                                                               0x2614,
+                                                               0x2615,
+                                                               0x2618,
+                                                               0x2618,
+                                                               0x267E,
+                                                               0x267F,
+                                                               0x268A,
+                                                               0x2700,
+                                                               0x2705,
+                                                               0x2705,
+                                                               0x270A,
+                                                               0x270B,
+                                                               0x2728,
+                                                               0x2728,
+                                                               0x274C,
+                                                               0x274C,
+                                                               0x274E,
+                                                               0x274E,
+                                                               0x2753,
+                                                               0x2755,
+                                                               0x2757,
+                                                               0x2757,
+                                                               0x275F,
+                                                               0x2760,
+                                                               0x2795,
+                                                               0x2797,
+                                                               0x27B0,
+                                                               0x27B0,
+                                                               0x27BF,
+                                                               0x27CF,
+                                                               0x27EC,
+                                                               0x27EF,
+                                                               0x2B00,
+                                                               0x2E7F,
+                                                               0x2E9A,
+                                                               0x2E9A,
+                                                               0x2EF4,
+                                                               0x2EFF,
+                                                               0x2FD6,
+                                                               0x2FEF,
+                                                               0x2FFC,
+                                                               0x2FFF,
+                                                               0x3040,
+                                                               0x3040,
+                                                               0x3097,
+                                                               0x3098,
+                                                               0x3100,
+                                                               0x3104,
+                                                               0x312D,
+                                                               0x3130,
+                                                               0x318F,
+                                                               0x318F,
+                                                               0x31B8,
+                                                               0x31EF,
+                                                               0x321D,
+                                                               0x321F,
+                                                               0x3244,
+                                                               0x3250,
+                                                               0x327C,
+                                                               0x327E,
+                                                               0x32CC,
+                                                               0x32CF,
+                                                               0x32FF,
+                                                               0x32FF,
+                                                               0x3377,
+                                                               0x337A,
+                                                               0x33DE,
+                                                               0x33DF,
+                                                               0x33FF,
+                                                               0x33FF,
+                                                               0x4DB6,
+                                                               0x4DFF,
+                                                               0x9FA6,
+                                                               0x9FFF,
+                                                               0xA48D,
+                                                               0xA48F,
+                                                               0xA4C7,
+                                                               0xABFF,
+                                                               0xD7A4,
+                                                               0xD7FF,
+                                                               0xFA2E,
+                                                               0xFA2F,
+                                                               0xFA6B,
+                                                               0xFAFF,
+                                                               0xFB07,
+                                                               0xFB12,
+                                                               0xFB18,
+                                                               0xFB1C,
+                                                               0xFB37,
+                                                               0xFB37,
+                                                               0xFB3D,
+                                                               0xFB3D,
+                                                               0xFB3F,
+                                                               0xFB3F,
+                                                               0xFB42,
+                                                               0xFB42,
+                                                               0xFB45,
+                                                               0xFB45,
+                                                               0xFBB2,
+                                                               0xFBD2,
+                                                               0xFD40,
+                                                               0xFD4F,
+                                                               0xFD90,
+                                                               0xFD91,
+                                                               0xFDC8,
+                                                               0xFDCF,
+                                                               0xFDFD,
+                                                               0xFDFF,
+                                                               0xFE10,
+                                                               0xFE1F,
+                                                               0xFE24,
+                                                               0xFE2F,
+                                                               0xFE47,
+                                                               0xFE48,
+                                                               0xFE53,
+                                                               0xFE53,
+                                                               0xFE67,
+                                                               0xFE67,
+                                                               0xFE6C,
+                                                               0xFE6F,
+                                                               0xFE75,
+                                                               0xFE75,
+                                                               0xFEFD,
+                                                               0xFEFE,
+                                                               0xFF00,
+                                                               0xFF00,
+                                                               0xFFBF,
+                                                               0xFFC1,
+                                                               0xFFC8,
+                                                               0xFFC9,
+                                                               0xFFD0,
+                                                               0xFFD1,
+                                                               0xFFD8,
+                                                               0xFFD9,
+                                                               0xFFDD,
+                                                               0xFFDF,
+                                                               0xFFE7,
+                                                               0xFFE7,
+                                                               0xFFEF,
+                                                               0xFFF8,
+                                                               0x10000,
+                                                               0x102FF,
+                                                               0x1031F,
+                                                               0x1031F,
+                                                               0x10324,
+                                                               0x1032F,
+                                                               0x1034B,
+                                                               0x103FF,
+                                                               0x10426,
+                                                               0x10427,
+                                                               0x1044E,
+                                                               0x1CFFF,
+                                                               0x1D0F6,
+                                                               0x1D0FF,
+                                                               0x1D127,
+                                                               0x1D129,
+                                                               0x1D1DE,
+                                                               0x1D3FF,
+                                                               0x1D455,
+                                                               0x1D455,
+                                                               0x1D49D,
+                                                               0x1D49D,
+                                                               0x1D4A0,
+                                                               0x1D4A1,
+                                                               0x1D4A3,
+                                                               0x1D4A4,
+                                                               0x1D4A7,
+                                                               0x1D4A8,
+                                                               0x1D4AD,
+                                                               0x1D4AD,
+                                                               0x1D4BA,
+                                                               0x1D4BA,
+                                                               0x1D4BC,
+                                                               0x1D4BC,
+                                                               0x1D4C1,
+                                                               0x1D4C1,
+                                                               0x1D4C4,
+                                                               0x1D4C4,
+                                                               0x1D506,
+                                                               0x1D506,
+                                                               0x1D50B,
+                                                               0x1D50C,
+                                                               0x1D515,
+                                                               0x1D515,
+                                                               0x1D51D,
+                                                               0x1D51D,
+                                                               0x1D53A,
+                                                               0x1D53A,
+                                                               0x1D53F,
+                                                               0x1D53F,
+                                                               0x1D545,
+                                                               0x1D545,
+                                                               0x1D547,
+                                                               0x1D549,
+                                                               0x1D551,
+                                                               0x1D551,
+                                                               0x1D6A4,
+                                                               0x1D6A7,
+                                                               0x1D7CA,
+                                                               0x1D7CD,
+                                                               0x1D800,
+                                                               0x1FFFD,
+                                                               0x2A6D7,
+                                                               0x2F7FF,
+                                                               0x2FA1E,
+                                                               0x2FFFD,
+                                                               0x30000,
+                                                               0x3FFFD,
+                                                               0x40000,
+                                                               0x4FFFD,
+                                                               0x50000,
+                                                               0x5FFFD,
+                                                               0x60000,
+                                                               0x6FFFD,
+                                                               0x70000,
+                                                               0x7FFFD,
+                                                               0x80000,
+                                                               0x8FFFD,
+                                                               0x90000,
+                                                               0x9FFFD,
+                                                               0xA0000,
+                                                               0xAFFFD,
+                                                               0xB0000,
+                                                               0xBFFFD,
+                                                               0xC0000,
+                                                               0xCFFFD,
+                                                               0xD0000,
+                                                               0xDFFFD,
+                                                               0xE0000,
+                                                               0xE0000,
+                                                               0xE0002,
+                                                               0xE001F,
+                                                               0xE0080,
+                                                               0xEFFFD,});
+
+   /** B.1 Commonly mapped to nothing */
+   static final CharClass B1 = CharClass.fromList(new int[] {0x00AD,
+                                                             0x034F,
+                                                             0x1806,
+                                                             0x180B,
+                                                             0x180C,
+                                                             0x180D,
+                                                             0x200B,
+                                                             0x200C,
+                                                             0x200D,
+                                                             0x2060,
+                                                             0xFE00,
+                                                             0xFE01,
+                                                             0xFE02,
+                                                             0xFE03,
+                                                             0xFE04,
+                                                             0xFE05,
+                                                             0xFE06,
+                                                             0xFE07,
+                                                             0xFE08,
+                                                             0xFE09,
+                                                             0xFE0A,
+                                                             0xFE0B,
+                                                             0xFE0C,
+                                                             0xFE0D,
+                                                             0xFE0E,
+                                                             0xFE0F,
+                                                             0xFEFF,});
+
+   /** C.1.1 ASCII space characters */
+   static final CharClass C11 = CharClass.fromList(new int[] {0x0020});
+
+   /** C.1.2 Non-ASCII space characters */
+   static final CharClass C12 = CharClass.fromList(new int[] {0x00A0,
+                                                              0x1680,
+                                                              0x2000,
+                                                              0x2001,
+                                                              0x2002,
+                                                              0x2003,
+                                                              0x2004,
+                                                              0x2005,
+                                                              0x2006,
+                                                              0x2007,
+                                                              0x2008,
+                                                              0x2009,
+                                                              0x200A,
+                                                              0x200B,
+                                                              0x202F,
+                                                              0x205F,
+                                                              0x3000,});
+
+   /** C.2.1 ASCII control characters */
+   static final CharClass C21 = CharClass.fromList(new int[] {0x0000,
+                                                              0x0001,
+                                                              0x0002,
+                                                              0x0003,
+                                                              0x0004,
+                                                              0x0005,
+                                                              0x0006,
+                                                              0x0007,
+                                                              0x0008,
+                                                              0x0009,
+                                                              0x000A,
+                                                              0x000B,
+                                                              0x000C,
+                                                              0x000D,
+                                                              0x000E,
+                                                              0x000F,
+                                                              0x0010,
+                                                              0x0011,
+                                                              0x0012,
+                                                              0x0013,
+                                                              0x0014,
+                                                              0x0015,
+                                                              0x0016,
+                                                              0x0017,
+                                                              0x0018,
+                                                              0x0019,
+                                                              0x001A,
+                                                              0x001B,
+                                                              0x001C,
+                                                              0x001D,
+                                                              0x001E,
+                                                              0x001F,
+                                                              0x007F});
+
+   /** C.2.2 Non-ASCII control characters */
+   static final CharClass C22 = CharClass.fromList(new int[] {0x0080,
+                                                              0x0081,
+                                                              0x0082,
+                                                              0x0083,
+                                                              0x0084,
+                                                              0x0085,
+                                                              0x0086,
+                                                              0x0087,
+                                                              0x0088,
+                                                              0x0089,
+                                                              0x008A,
+                                                              0x008B,
+                                                              0x008C,
+                                                              0x008D,
+                                                              0x008E,
+                                                              0x008F,
+                                                              0x0090,
+                                                              0x0091,
+                                                              0x0092,
+                                                              0x0093,
+                                                              0x0094,
+                                                              0x0095,
+                                                              0x0096,
+                                                              0x0097,
+                                                              0x0098,
+                                                              0x0099,
+                                                              0x009A,
+                                                              0x009B,
+                                                              0x009C,
+                                                              0x009D,
+                                                              0x009E,
+                                                              0x009F,
+                                                              0x06DD,
+                                                              0x070F,
+                                                              0x180E,
+                                                              0x200C,
+                                                              0x200D,
+                                                              0x2028,
+                                                              0x2029,
+                                                              0x2060,
+                                                              0x2061,
+                                                              0x2062,
+                                                              0x2063,
+                                                              0x206A,
+                                                              0x206B,
+                                                              0x206C,
+                                                              0x206D,
+                                                              0x206E,
+                                                              0x206F,
+                                                              0xFEFF,
+                                                              0xFFF9,
+                                                              0xFFFA,
+                                                              0xFFFB,
+                                                              0xFFFC,
+                                                              0x1D173,
+                                                              0x1D174,
+                                                              0x1D175,
+                                                              0x1D176,
+                                                              0x1D177,
+                                                              0x1D178,
+                                                              0x1D179,
+                                                              0x1D17A,});
+
+   /** C.3 Private use */
+   static final CharClass C3 = CharClass.fromRanges(new int[] {0xE000, 0xF8FF, 0xF0000, 0xFFFFD, 0x100000, 0x10FFFD,});
+
+   /** C.4 Non-character code points */
+   static final CharClass C4 = CharClass.fromRanges(new int[] {0xFDD0,
+                                                               0xFDEF,
+                                                               0xFFFE,
+                                                               0xFFFF,
+                                                               0x1FFFE,
+                                                               0x1FFFF,
+                                                               0x2FFFE,
+                                                               0x2FFFF,
+                                                               0x3FFFE,
+                                                               0x3FFFF,
+                                                               0x4FFFE,
+                                                               0x4FFFF,
+                                                               0x5FFFE,
+                                                               0x5FFFF,
+                                                               0x6FFFE,
+                                                               0x6FFFF,
+                                                               0x7FFFE,
+                                                               0x7FFFF,
+                                                               0x8FFFE,
+                                                               0x8FFFF,
+                                                               0x9FFFE,
+                                                               0x9FFFF,
+                                                               0xAFFFE,
+                                                               0xAFFFF,
+                                                               0xBFFFE,
+                                                               0xBFFFF,
+                                                               0xCFFFE,
+                                                               0xCFFFF,
+                                                               0xDFFFE,
+                                                               0xDFFFF,
+                                                               0xEFFFE,
+                                                               0xEFFFF,
+                                                               0xFFFFE,
+                                                               0xFFFFF,
+                                                               0x10FFFE,
+                                                               0x10FFFF,});
+
+   /** C.5 Surrogate codes */
+   static final CharClass C5 = CharClass.fromRanges(new int[] {0xD800, 0xDFFF,});
+
+   /** C.6 Inappropriate for plain text */
+   static final CharClass C6 = CharClass.fromList(new int[] {0xFFF9, 0xFFFA, 0xFFFB, 0xFFFC, 0xFFFD,});
+
+   /** C.7 Inappropriate for canonical representation */
+   static final CharClass C7 = CharClass.fromList(new int[] {0x2FF0,
+                                                             0x2FF1,
+                                                             0x2FF2,
+                                                             0x2FF3,
+                                                             0x2FF4,
+                                                             0x2FF5,
+                                                             0x2FF6,
+                                                             0x2FF7,
+                                                             0x2FF8,
+                                                             0x2FF9,
+                                                             0x2FFA,
+                                                             0x2FFB,});
+
+   /** C.8 Change display properties or are deprecated */
+   static final CharClass C8 = CharClass.fromList(new int[] {0x0340,
+                                                             0x0341,
+                                                             0x200E,
+                                                             0x200F,
+                                                             0x202A,
+                                                             0x202B,
+                                                             0x202C,
+                                                             0x202D,
+                                                             0x202E,
+                                                             0x206A,
+                                                             0x206B,
+                                                             0x206C,
+                                                             0x206D,
+                                                             0x206E,
+                                                             0x206F,});
+
+   /** C.9 Tagging characters (tuples) */
+   static final CharClass C9 = CharClass.fromRanges(new int[] {0xE0001, 0xE0001, 0xE0020, 0xE007F,});
+
+   /** D.1 Characters with bidirectional property "R" or "AL" */
+   static final CharClass D1 = CharClass.fromRanges(new int[] {0x05BE,
+                                                               0x05BE,
+                                                               0x05C0,
+                                                               0x05C0,
+                                                               0x05C3,
+                                                               0x05C3,
+                                                               0x05D0,
+                                                               0x05EA,
+                                                               0x05F0,
+                                                               0x05F4,
+                                                               0x061B,
+                                                               0x061B,
+                                                               0x061F,
+                                                               0x061F,
+                                                               0x0621,
+                                                               0x063A,
+                                                               0x0640,
+                                                               0x064A,
+                                                               0x066D,
+                                                               0x066F,
+                                                               0x0671,
+                                                               0x06D5,
+                                                               0x06DD,
+                                                               0x06DD,
+                                                               0x06E5,
+                                                               0x06E6,
+                                                               0x06FA,
+                                                               0x06FE,
+                                                               0x0700,
+                                                               0x070D,
+                                                               0x0710,
+                                                               0x0710,
+                                                               0x0712,
+                                                               0x072C,
+                                                               0x0780,
+                                                               0x07A5,
+                                                               0x07B1,
+                                                               0x07B1,
+                                                               0x200F,
+                                                               0x200F,
+                                                               0xFB1D,
+                                                               0xFB1D,
+                                                               0xFB1F,
+                                                               0xFB28,
+                                                               0xFB2A,
+                                                               0xFB36,
+                                                               0xFB38,
+                                                               0xFB3C,
+                                                               0xFB3E,
+                                                               0xFB3E,
+                                                               0xFB40,
+                                                               0xFB41,
+                                                               0xFB43,
+                                                               0xFB44,
+                                                               0xFB46,
+                                                               0xFBB1,
+                                                               0xFBD3,
+                                                               0xFD3D,
+                                                               0xFD50,
+                                                               0xFD8F,
+                                                               0xFD92,
+                                                               0xFDC7,
+                                                               0xFDF0,
+                                                               0xFDFC,
+                                                               0xFE70,
+                                                               0xFE74,
+                                                               0xFE76,
+                                                               0xFEFC,});
+
+   /** D.2 Characters with bidirectional property "L" */
+   static final CharClass D2 = CharClass.fromRanges(new int[] {0x0041,
+                                                               0x005A,
+                                                               0x0061,
+                                                               0x007A,
+                                                               0x00AA,
+                                                               0x00AA,
+                                                               0x00B5,
+                                                               0x00B5,
+                                                               0x00BA,
+                                                               0x00BA,
+                                                               0x00C0,
+                                                               0x00D6,
+                                                               0x00D8,
+                                                               0x00F6,
+                                                               0x00F8,
+                                                               0x0220,
+                                                               0x0222,
+                                                               0x0233,
+                                                               0x0250,
+                                                               0x02AD,
+                                                               0x02B0,
+                                                               0x02B8,
+                                                               0x02BB,
+                                                               0x02C1,
+                                                               0x02D0,
+                                                               0x02D1,
+                                                               0x02E0,
+                                                               0x02E4,
+                                                               0x02EE,
+                                                               0x02EE,
+                                                               0x037A,
+                                                               0x037A,
+                                                               0x0386,
+                                                               0x0386,
+                                                               0x0388,
+                                                               0x038A,
+                                                               0x038C,
+                                                               0x038C,
+                                                               0x038E,
+                                                               0x03A1,
+                                                               0x03A3,
+                                                               0x03CE,
+                                                               0x03D0,
+                                                               0x03F5,
+                                                               0x0400,
+                                                               0x0482,
+                                                               0x048A,
+                                                               0x04CE,
+                                                               0x04D0,
+                                                               0x04F5,
+                                                               0x04F8,
+                                                               0x04F9,
+                                                               0x0500,
+                                                               0x050F,
+                                                               0x0531,
+                                                               0x0556,
+                                                               0x0559,
+                                                               0x055F,
+                                                               0x0561,
+                                                               0x0587,
+                                                               0x0589,
+                                                               0x0589,
+                                                               0x0903,
+                                                               0x0903,
+                                                               0x0905,
+                                                               0x0939,
+                                                               0x093D,
+                                                               0x0940,
+                                                               0x0949,
+                                                               0x094C,
+                                                               0x0950,
+                                                               0x0950,
+                                                               0x0958,
+                                                               0x0961,
+                                                               0x0964,
+                                                               0x0970,
+                                                               0x0982,
+                                                               0x0983,
+                                                               0x0985,
+                                                               0x098C,
+                                                               0x098F,
+                                                               0x0990,
+                                                               0x0993,
+                                                               0x09A8,
+                                                               0x09AA,
+                                                               0x09B0,
+                                                               0x09B2,
+                                                               0x09B2,
+                                                               0x09B6,
+                                                               0x09B9,
+                                                               0x09BE,
+                                                               0x09C0,
+                                                               0x09C7,
+                                                               0x09C8,
+                                                               0x09CB,
+                                                               0x09CC,
+                                                               0x09D7,
+                                                               0x09D7,
+                                                               0x09DC,
+                                                               0x09DD,
+                                                               0x09DF,
+                                                               0x09E1,
+                                                               0x09E6,
+                                                               0x09F1,
+                                                               0x09F4,
+                                                               0x09FA,
+                                                               0x0A05,
+                                                               0x0A0A,
+                                                               0x0A0F,
+                                                               0x0A10,
+                                                               0x0A13,
+                                                               0x0A28,
+                                                               0x0A2A,
+                                                               0x0A30,
+                                                               0x0A32,
+                                                               0x0A33,
+                                                               0x0A35,
+                                                               0x0A36,
+                                                               0x0A38,
+                                                               0x0A39,
+                                                               0x0A3E,
+                                                               0x0A40,
+                                                               0x0A59,
+                                                               0x0A5C,
+                                                               0x0A5E,
+                                                               0x0A5E,
+                                                               0x0A66,
+                                                               0x0A6F,
+                                                               0x0A72,
+                                                               0x0A74,
+                                                               0x0A83,
+                                                               0x0A83,
+                                                               0x0A85,
+                                                               0x0A8B,
+                                                               0x0A8D,
+                                                               0x0A8D,
+                                                               0x0A8F,
+                                                               0x0A91,
+                                                               0x0A93,
+                                                               0x0AA8,
+                                                               0x0AAA,
+                                                               0x0AB0,
+                                                               0x0AB2,
+                                                               0x0AB3,
+                                                               0x0AB5,
+                                                               0x0AB9,
+                                                               0x0ABD,
+                                                               0x0AC0,
+                                                               0x0AC9,
+                                                               0x0AC9,
+                                                               0x0ACB,
+                                                               0x0ACC,
+                                                               0x0AD0,
+                                                               0x0AD0,
+                                                               0x0AE0,
+                                                               0x0AE0,
+                                                               0x0AE6,
+                                                               0x0AEF,
+                                                               0x0B02,
+                                                               0x0B03,
+                                                               0x0B05,
+                                                               0x0B0C,
+                                                               0x0B0F,
+                                                               0x0B10,
+                                                               0x0B13,
+                                                               0x0B28,
+                                                               0x0B2A,
+                                                               0x0B30,
+                                                               0x0B32,
+                                                               0x0B33,
+                                                               0x0B36,
+                                                               0x0B39,
+                                                               0x0B3D,
+                                                               0x0B3E,
+                                                               0x0B40,
+                                                               0x0B40,
+                                                               0x0B47,
+                                                               0x0B48,
+                                                               0x0B4B,
+                                                               0x0B4C,
+                                                               0x0B57,
+                                                               0x0B57,
+                                                               0x0B5C,
+                                                               0x0B5D,
+                                                               0x0B5F,
+                                                               0x0B61,
+                                                               0x0B66,
+                                                               0x0B70,
+                                                               0x0B83,
+                                                               0x0B83,
+                                                               0x0B85,
+                                                               0x0B8A,
+                                                               0x0B8E,
+                                                               0x0B90,
+                                                               0x0B92,
+                                                               0x0B95,
+                                                               0x0B99,
+                                                               0x0B9A,
+                                                               0x0B9C,
+                                                               0x0B9C,
+                                                               0x0B9E,
+                                                               0x0B9F,
+                                                               0x0BA3,
+                                                               0x0BA4,
+                                                               0x0BA8,
+                                                               0x0BAA,
+                                                               0x0BAE,
+                                                               0x0BB5,
+                                                               0x0BB7,
+                                                               0x0BB9,
+                                                               0x0BBE,
+                                                               0x0BBF,
+                                                               0x0BC1,
+                                                               0x0BC2,
+                                                               0x0BC6,
+                                                               0x0BC8,
+                                                               0x0BCA,
+                                                               0x0BCC,
+                                                               0x0BD7,
+                                                               0x0BD7,
+                                                               0x0BE7,
+                                                               0x0BF2,
+                                                               0x0C01,
+                                                               0x0C03,
+                                                               0x0C05,
+                                                               0x0C0C,
+                                                               0x0C0E,
+                                                               0x0C10,
+                                                               0x0C12,
+                                                               0x0C28,
+                                                               0x0C2A,
+                                                               0x0C33,
+                                                               0x0C35,
+                                                               0x0C39,
+                                                               0x0C41,
+                                                               0x0C44,
+                                                               0x0C60,
+                                                               0x0C61,
+                                                               0x0C66,
+                                                               0x0C6F,
+                                                               0x0C82,
+                                                               0x0C83,
+                                                               0x0C85,
+                                                               0x0C8C,
+                                                               0x0C8E,
+                                                               0x0C90,
+                                                               0x0C92,
+                                                               0x0CA8,
+                                                               0x0CAA,
+                                                               0x0CB3,
+                                                               0x0CB5,
+                                                               0x0CB9,
+                                                               0x0CBE,
+                                                               0x0CBE,
+                                                               0x0CC0,
+                                                               0x0CC4,
+                                                               0x0CC7,
+                                                               0x0CC8,
+                                                               0x0CCA,
+                                                               0x0CCB,
+                                                               0x0CD5,
+                                                               0x0CD6,
+                                                               0x0CDE,
+                                                               0x0CDE,
+                                                               0x0CE0,
+                                                               0x0CE1,
+                                                               0x0CE6,
+                                                               0x0CEF,
+                                                               0x0D02,
+                                                               0x0D03,
+                                                               0x0D05,
+                                                               0x0D0C,
+                                                               0x0D0E,
+                                                               0x0D10,
+                                                               0x0D12,
+                                                               0x0D28,
+                                                               0x0D2A,
+                                                               0x0D39,
+                                                               0x0D3E,
+                                                               0x0D40,
+                                                               0x0D46,
+                                                               0x0D48,
+                                                               0x0D4A,
+                                                               0x0D4C,
+                                                               0x0D57,
+                                                               0x0D57,
+                                                               0x0D60,
+                                                               0x0D61,
+                                                               0x0D66,
+                                                               0x0D6F,
+                                                               0x0D82,
+                                                               0x0D83,
+                                                               0x0D85,
+                                                               0x0D96,
+                                                               0x0D9A,
+                                                               0x0DB1,
+                                                               0x0DB3,
+                                                               0x0DBB,
+                                                               0x0DBD,
+                                                               0x0DBD,
+                                                               0x0DC0,
+                                                               0x0DC6,
+                                                               0x0DCF,
+                                                               0x0DD1,
+                                                               0x0DD8,
+                                                               0x0DDF,
+                                                               0x0DF2,
+                                                               0x0DF4,
+                                                               0x0E01,
+                                                               0x0E30,
+                                                               0x0E32,
+                                                               0x0E33,
+                                                               0x0E40,
+                                                               0x0E46,
+                                                               0x0E4F,
+                                                               0x0E5B,
+                                                               0x0E81,
+                                                               0x0E82,
+                                                               0x0E84,
+                                                               0x0E84,
+                                                               0x0E87,
+                                                               0x0E88,
+                                                               0x0E8A,
+                                                               0x0E8A,
+                                                               0x0E8D,
+                                                               0x0E8D,
+                                                               0x0E94,
+                                                               0x0E97,
+                                                               0x0E99,
+                                                               0x0E9F,
+                                                               0x0EA1,
+                                                               0x0EA3,
+                                                               0x0EA5,
+                                                               0x0EA5,
+                                                               0x0EA7,
+                                                               0x0EA7,
+                                                               0x0EAA,
+                                                               0x0EAB,
+                                                               0x0EAD,
+                                                               0x0EB0,
+                                                               0x0EB2,
+                                                               0x0EB3,
+                                                               0x0EBD,
+                                                               0x0EBD,
+                                                               0x0EC0,
+                                                               0x0EC4,
+                                                               0x0EC6,
+                                                               0x0EC6,
+                                                               0x0ED0,
+                                                               0x0ED9,
+                                                               0x0EDC,
+                                                               0x0EDD,
+                                                               0x0F00,
+                                                               0x0F17,
+                                                               0x0F1A,
+                                                               0x0F34,
+                                                               0x0F36,
+                                                               0x0F36,
+                                                               0x0F38,
+                                                               0x0F38,
+                                                               0x0F3E,
+                                                               0x0F47,
+                                                               0x0F49,
+                                                               0x0F6A,
+                                                               0x0F7F,
+                                                               0x0F7F,
+                                                               0x0F85,
+                                                               0x0F85,
+                                                               0x0F88,
+                                                               0x0F8B,
+                                                               0x0FBE,
+                                                               0x0FC5,
+                                                               0x0FC7,
+                                                               0x0FCC,
+                                                               0x0FCF,
+                                                               0x0FCF,
+                                                               0x1000,
+                                                               0x1021,
+                                                               0x1023,
+                                                               0x1027,
+                                                               0x1029,
+                                                               0x102A,
+                                                               0x102C,
+                                                               0x102C,
+                                                               0x1031,
+                                                               0x1031,
+                                                               0x1038,
+                                                               0x1038,
+                                                               0x1040,
+                                                               0x1057,
+                                                               0x10A0,
+                                                               0x10C5,
+                                                               0x10D0,
+                                                               0x10F8,
+                                                               0x10FB,
+                                                               0x10FB,
+                                                               0x1100,
+                                                               0x1159,
+                                                               0x115F,
+                                                               0x11A2,
+                                                               0x11A8,
+                                                               0x11F9,
+                                                               0x1200,
+                                                               0x1206,
+                                                               0x1208,
+                                                               0x1246,
+                                                               0x1248,
+                                                               0x1248,
+                                                               0x124A,
+                                                               0x124D,
+                                                               0x1250,
+                                                               0x1256,
+                                                               0x1258,
+                                                               0x1258,
+                                                               0x125A,
+                                                               0x125D,
+                                                               0x1260,
+                                                               0x1286,
+                                                               0x1288,
+                                                               0x1288,
+                                                               0x128A,
+                                                               0x128D,
+                                                               0x1290,
+                                                               0x12AE,
+                                                               0x12B0,
+                                                               0x12B0,
+                                                               0x12B2,
+                                                               0x12B5,
+                                                               0x12B8,
+                                                               0x12BE,
+                                                               0x12C0,
+                                                               0x12C0,
+                                                               0x12C2,
+                                                               0x12C5,
+                                                               0x12C8,
+                                                               0x12CE,
+                                                               0x12D0,
+                                                               0x12D6,
+                                                               0x12D8,
+                                                               0x12EE,
+                                                               0x12F0,
+                                                               0x130E,
+                                                               0x1310,
+                                                               0x1310,
+                                                               0x1312,
+                                                               0x1315,
+                                                               0x1318,
+                                                               0x131E,
+                                                               0x1320,
+                                                               0x1346,
+                                                               0x1348,
+                                                               0x135A,
+                                                               0x1361,
+                                                               0x137C,
+                                                               0x13A0,
+                                                               0x13F4,
+                                                               0x1401,
+                                                               0x1676,
+                                                               0x1681,
+                                                               0x169A,
+                                                               0x16A0,
+                                                               0x16F0,
+                                                               0x1700,
+                                                               0x170C,
+                                                               0x170E,
+                                                               0x1711,
+                                                               0x1720,
+                                                               0x1731,
+                                                               0x1735,
+                                                               0x1736,
+                                                               0x1740,
+                                                               0x1751,
+                                                               0x1760,
+                                                               0x176C,
+                                                               0x176E,
+                                                               0x1770,
+                                                               0x1780,
+                                                               0x17B6,
+                                                               0x17BE,
+                                                               0x17C5,
+                                                               0x17C7,
+                                                               0x17C8,
+                                                               0x17D4,
+                                                               0x17DA,
+                                                               0x17DC,
+                                                               0x17DC,
+                                                               0x17E0,
+                                                               0x17E9,
+                                                               0x1810,
+                                                               0x1819,
+                                                               0x1820,
+                                                               0x1877,
+                                                               0x1880,
+                                                               0x18A8,
+                                                               0x1E00,
+                                                               0x1E9B,
+                                                               0x1EA0,
+                                                               0x1EF9,
+                                                               0x1F00,
+                                                               0x1F15,
+                                                               0x1F18,
+                                                               0x1F1D,
+                                                               0x1F20,
+                                                               0x1F45,
+                                                               0x1F48,
+                                                               0x1F4D,
+                                                               0x1F50,
+                                                               0x1F57,
+                                                               0x1F59,
+                                                               0x1F59,
+                                                               0x1F5B,
+                                                               0x1F5B,
+                                                               0x1F5D,
+                                                               0x1F5D,
+                                                               0x1F5F,
+                                                               0x1F7D,
+                                                               0x1F80,
+                                                               0x1FB4,
+                                                               0x1FB6,
+                                                               0x1FBC,
+                                                               0x1FBE,
+                                                               0x1FBE,
+                                                               0x1FC2,
+                                                               0x1FC4,
+                                                               0x1FC6,
+                                                               0x1FCC,
+                                                               0x1FD0,
+                                                               0x1FD3,
+                                                               0x1FD6,
+                                                               0x1FDB,
+                                                               0x1FE0,
+                                                               0x1FEC,
+                                                               0x1FF2,
+                                                               0x1FF4,
+                                                               0x1FF6,
+                                                               0x1FFC,
+                                                               0x200E,
+                                                               0x200E,
+                                                               0x2071,
+                                                               0x2071,
+                                                               0x207F,
+                                                               0x207F,
+                                                               0x2102,
+                                                               0x2102,
+                                                               0x2107,
+                                                               0x2107,
+                                                               0x210A,
+                                                               0x2113,
+                                                               0x2115,
+                                                               0x2115,
+                                                               0x2119,
+                                                               0x211D,
+                                                               0x2124,
+                                                               0x2124,
+                                                               0x2126,
+                                                               0x2126,
+                                                               0x2128,
+                                                               0x2128,
+                                                               0x212A,
+                                                               0x212D,
+                                                               0x212F,
+                                                               0x2131,
+                                                               0x2133,
+                                                               0x2139,
+                                                               0x213D,
+                                                               0x213F,
+                                                               0x2145,
+                                                               0x2149,
+                                                               0x2160,
+                                                               0x2183,
+                                                               0x2336,
+                                                               0x237A,
+                                                               0x2395,
+                                                               0x2395,
+                                                               0x249C,
+                                                               0x24E9,
+                                                               0x3005,
+                                                               0x3007,
+                                                               0x3021,
+                                                               0x3029,
+                                                               0x3031,
+                                                               0x3035,
+                                                               0x3038,
+                                                               0x303C,
+                                                               0x3041,
+                                                               0x3096,
+                                                               0x309D,
+                                                               0x309F,
+                                                               0x30A1,
+                                                               0x30FA,
+                                                               0x30FC,
+                                                               0x30FF,
+                                                               0x3105,
+                                                               0x312C,
+                                                               0x3131,
+                                                               0x318E,
+                                                               0x3190,
+                                                               0x31B7,
+                                                               0x31F0,
+                                                               0x321C,
+                                                               0x3220,
+                                                               0x3243,
+                                                               0x3260,
+                                                               0x327B,
+                                                               0x327F,
+                                                               0x32B0,
+                                                               0x32C0,
+                                                               0x32CB,
+                                                               0x32D0,
+                                                               0x32FE,
+                                                               0x3300,
+                                                               0x3376,
+                                                               0x337B,
+                                                               0x33DD,
+                                                               0x33E0,
+                                                               0x33FE,
+                                                               0x3400,
+                                                               0x4DB5,
+                                                               0x4E00,
+                                                               0x9FA5,
+                                                               0xA000,
+                                                               0xA48C,
+                                                               0xAC00,
+                                                               0xD7A3,
+                                                               0xD800,
+                                                               0xFA2D,
+                                                               0xFA30,
+                                                               0xFA6A,
+                                                               0xFB00,
+                                                               0xFB06,
+                                                               0xFB13,
+                                                               0xFB17,
+                                                               0xFF21,
+                                                               0xFF3A,
+                                                               0xFF41,
+                                                               0xFF5A,
+                                                               0xFF66,
+                                                               0xFFBE,
+                                                               0xFFC2,
+                                                               0xFFC7,
+                                                               0xFFCA,
+                                                               0xFFCF,
+                                                               0xFFD2,
+                                                               0xFFD7,
+                                                               0xFFDA,
+                                                               0xFFDC,
+                                                               0x10300,
+                                                               0x1031E,
+                                                               0x10320,
+                                                               0x10323,
+                                                               0x10330,
+                                                               0x1034A,
+                                                               0x10400,
+                                                               0x10425,
+                                                               0x10428,
+                                                               0x1044D,
+                                                               0x1D000,
+                                                               0x1D0F5,
+                                                               0x1D100,
+                                                               0x1D126,
+                                                               0x1D12A,
+                                                               0x1D166,
+                                                               0x1D16A,
+                                                               0x1D172,
+                                                               0x1D183,
+                                                               0x1D184,
+                                                               0x1D18C,
+                                                               0x1D1A9,
+                                                               0x1D1AE,
+                                                               0x1D1DD,
+                                                               0x1D400,
+                                                               0x1D454,
+                                                               0x1D456,
+                                                               0x1D49C,
+                                                               0x1D49E,
+                                                               0x1D49F,
+                                                               0x1D4A2,
+                                                               0x1D4A2,
+                                                               0x1D4A5,
+                                                               0x1D4A6,
+                                                               0x1D4A9,
+                                                               0x1D4AC,
+                                                               0x1D4AE,
+                                                               0x1D4B9,
+                                                               0x1D4BB,
+                                                               0x1D4BB,
+                                                               0x1D4BD,
+                                                               0x1D4C0,
+                                                               0x1D4C2,
+                                                               0x1D4C3,
+                                                               0x1D4C5,
+                                                               0x1D505,
+                                                               0x1D507,
+                                                               0x1D50A,
+                                                               0x1D50D,
+                                                               0x1D514,
+                                                               0x1D516,
+                                                               0x1D51C,
+                                                               0x1D51E,
+                                                               0x1D539,
+                                                               0x1D53B,
+                                                               0x1D53E,
+                                                               0x1D540,
+                                                               0x1D544,
+                                                               0x1D546,
+                                                               0x1D546,
+                                                               0x1D54A,
+                                                               0x1D550,
+                                                               0x1D552,
+                                                               0x1D6A3,
+                                                               0x1D6A8,
+                                                               0x1D7C9,
+                                                               0x20000,
+                                                               0x2A6D6,
+                                                               0x2F800,
+                                                               0x2FA1D,
+                                                               0xF0000,
+                                                               0xFFFFD,
+                                                               0x100000,
+                                                               0x10FFFD,});
+
+   /** rfc4013 2.3. Prohibited Output */
+   static final CharClass saslProhibited = CharClass.fromClasses(C12, C21, C22, C3, C4, C5, C6, C7, C8, C9);
+
+   /** A prohibited string has been passed to StringPrep. */
+   public abstract static class StringPrepError extends Exception {
+      /**
+       *
+       */
+      private static final long serialVersionUID = 1L;
+
+      protected StringPrepError(String message) {
+         super(message);
+      }
+   }
+
+   /** A prohibited character was detected. */
+   @SuppressWarnings({"WeakerAccess", "JavaDoc"})
+   public static class StringPrepProhibitedCharacter extends StringPrepError {
+      /**
+       *
+       */
+      private static final long serialVersionUID = 1L;
+
+      StringPrepProhibitedCharacter() {
+         super("String contains a prohibited character");
+      }
+
+      protected StringPrepProhibitedCharacter(String s) {
+         super(s);
+      }
+   }
+
+   /** A prohibited unassigned codepoint was detected. */
+   @SuppressWarnings("JavaDoc")
+   public static class StringPrepUnassignedCodepoint extends StringPrepProhibitedCharacter {
+      /**
+       *
+       */
+      private static final long serialVersionUID = 1L;
+
+      StringPrepUnassignedCodepoint() {
+         super("String contains an unassigned codepoint");
+      }
+   }
+
+   /** RTL verification has failed, according to rfc3454 section 6. */
+   @SuppressWarnings({"unused", "JavaDoc"})
+   public static class StringPrepRTLError extends StringPrepError {
+      /**
+       *
+       */
+      private static final long serialVersionUID = 1L;
+
+      StringPrepRTLError() {
+         super("Invalid RTL string");
+      }
+   }
+
+   public static class StringPrepRTLErrorBothRALandL extends StringPrepRTLError {
+
+      /**
+       *
+       */
+      private static final long serialVersionUID = 1L;
+   }
+
+   public static class StringPrepRTLErrorRALWithoutPrefix extends StringPrepRTLError {
+
+      /**
+       *
+       */
+      private static final long serialVersionUID = 1L;
+   }
+
+   public static class StringPrepRTLErrorRALWithoutSuffix extends StringPrepRTLError {
+
+      /**
+       *
+       */
+      private static final long serialVersionUID = 1L;
+   }
+
+   /**
+    * Replace each character of {@code s} which is in the {@link CharClass} mapFrom with the string
+    * {@code mapTo}.
+    */
+   static String applyMapTo(String s, CharClass mapFrom, String mapTo) {
+      StringBuilder result = new StringBuilder();
+      for (int i = 0; i < s.length();) {
+         int c = Character.codePointAt(s, i);
+         int charCount = Character.charCount(c);
+         if (mapFrom.isCharInClass(c))
+            result.append(mapTo);
+         else
+            result.append(s, i, i + charCount);
+         i += charCount;
+      }
+
+      return result.toString();
+   }
+
+   /**
+    * Return the first character index in s which is in {@link CharClass}, or -1 if no character is
+    * in the class.
+    */
+   static int containsCharacterInClass(String s, CharClass charClass) {
+      for (int i = 0; i < s.length();) {
+         int c = Character.codePointAt(s, i);
+         if (charClass.isCharInClass(c))
+            return i;
+
+         i += Character.charCount(c);
+      }
+      return -1;
+   }
+
+   /**
+    * Perform RTL verification according to rfc3454 section 6. On failure, throw a subclass of
+    * {@link StringPrepRTLError}.
+    */
+   protected static void verifyRTL(String s) throws StringPrepRTLError {
+      int containsRAL = containsCharacterInClass(s, D1);
+      if (containsRAL != -1) {
+         // 2) If a string contains any RandALCat character, the string MUST NOT
+         // contain any LCat character.
+         int containsL = containsCharacterInClass(s, D2);
+         if (containsL != -1)
+            throw new StringPrepRTLErrorBothRALandL();
+         // 3) If a string contains any RandALCat character, a RandALCat
+         // character MUST be the first character of the string
+         if (containsRAL != 0)
+            throw new StringPrepRTLErrorRALWithoutPrefix();
+
+         // ... and a RandALCat character MUST be the last character of the string.
+         if (!D1.isCharInClass(s.charAt(s.length() - 1)))
+            throw new StringPrepRTLErrorRALWithoutSuffix();
+      }
+   }
+
+   /** Apply SASLPrep and return the result. {@code} is treated as a stored string. */
+   public static String prepAsStoredString(String s) throws StringPrepError {
+      s = prepAsQueryString(s);
+
+      // rfc3454: 7. Unassigned Code Points in Stringprep Profiles
+      // Stored strings using the profile MUST NOT contain any unassigned code points.
+      // rfc4013: 2.5. Unassigned Code Points
+      // This profile specifies the [StringPrep, A.1] table as its list of unassigned
+      // code points.
+      int containsUnassignedCodepoint = containsCharacterInClass(s, A1);
+      if (containsUnassignedCodepoint != -1)
+         throw new StringPrepUnassignedCodepoint();
+      return s;
+   }
+
+   /** Apply SASLPrep and return the result. {@code} is treated as a query string. */
+   public static String prepAsQueryString(String s) throws StringPrepError {
+      // 1) Map
+      // rfc4013: 2.1. Mapping
+      // Note that applying the mapping this way works here because we only
+      // map to nothing or space. A StringPrep mapping that maps strings to
+      // another string (eg. case folding) can't be applied sequentially like
+      // this.
+      s = applyMapTo(s, B1, "");
+      s = applyMapTo(s, C12, " ");
+
+      // 2) Normalize
+      // rfc4013: 2.2. Normalization
+      s = Normalizer.normalize(s, Normalizer.Form.NFKC);
+
+      // 3) Prohibit
+      int idx = containsCharacterInClass(s, saslProhibited);
+      if (idx != -1)
+         throw new StringPrepProhibitedCharacter();
+
+      // 4) Check bidi
+      verifyRTL(s);
+
+      return s;
+   }
+
+   public static boolean isContainingProhibitedCharacters(String s) {
+      int idx = containsCharacterInClass(s, saslProhibited);
+      return idx != -1;
+   }
+}
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/scram/UserData.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/scram/UserData.java
new file mode 100644
index 0000000..4f6104d
--- /dev/null
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/scram/UserData.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2016 Ognyan Bankov
+ * <p>
+ * All rights reserved. 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.activemq.artemis.spi.core.security.scram;
+
+/**
+ * Wrapper for user data needed for the SCRAM authentication
+ */
+public class UserData {
+   /**
+    * Salt
+    */
+   public final String salt;
+   /**
+    * Iterations used to salt the password
+    */
+   public final int iterations;
+   /**
+    * Server key
+    */
+   public final String serverKey;
+   /**
+    * Stored key
+    */
+   public final String storedKey;
+
+   /**
+    * Creates new UserData
+    * @param salt Salt
+    * @param iterations Iterations for salting
+    * @param serverKey Server key
+    * @param storedKey Stored key
+    */
+   public UserData(String salt, int iterations, String serverKey, String storedKey) {
+      this.salt = salt;
+      this.iterations = iterations;
+      this.serverKey = serverKey;
+      this.storedKey = storedKey;
+   }
+}
diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/spi/core/security/jaas/Krb5LoginModuleTest.java b/artemis-server/src/test/java/org/apache/activemq/artemis/spi/core/security/jaas/Krb5LoginModuleTest.java
index d8039a9..7a7a2e2 100644
--- a/artemis-server/src/test/java/org/apache/activemq/artemis/spi/core/security/jaas/Krb5LoginModuleTest.java
+++ b/artemis-server/src/test/java/org/apache/activemq/artemis/spi/core/security/jaas/Krb5LoginModuleTest.java
@@ -16,17 +16,18 @@
  */
 package org.apache.activemq.artemis.spi.core.security.jaas;
 
-import org.junit.Test;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.security.Principal;
 
 import javax.security.auth.Subject;
 import javax.security.auth.callback.Callback;
 import javax.security.auth.callback.CallbackHandler;
 import javax.security.auth.callback.UnsupportedCallbackException;
 
-import java.io.IOException;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
+import org.junit.Test;
 
 public class Krb5LoginModuleTest {
 
@@ -52,7 +53,7 @@ public class Krb5LoginModuleTest {
       underTest.initialize(subject, new CallbackHandler() {
          @Override
          public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
-            ((Krb5Callback) callbacks[0]).setPeerPrincipal(new UserPrincipal("A"));
+            ((PrincipalsCallback) callbacks[0]).setPeerPrincipals(new Principal[] {new UserPrincipal("A")});
          }
       }, null, null);
 
diff --git a/examples/protocols/amqp/pom.xml b/examples/protocols/amqp/pom.xml
index 83d4ca5..b9b1bbe 100644
--- a/examples/protocols/amqp/pom.xml
+++ b/examples/protocols/amqp/pom.xml
@@ -51,6 +51,7 @@ under the License.
             <module>proton-clustered-cpp</module>
             <module>queue</module>
             <module>proton-ruby</module>
+            <module>sasl-scram</module>
          </modules>
       </profile>
       <profile>
diff --git a/examples/protocols/amqp/sasl-scram/pom.xml b/examples/protocols/amqp/sasl-scram/pom.xml
new file mode 100644
index 0000000..a7b34e8
--- /dev/null
+++ b/examples/protocols/amqp/sasl-scram/pom.xml
@@ -0,0 +1,43 @@
+<?xml version='1.0'?>
+<!--
+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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.activemq.examples.amqp</groupId>
+        <artifactId>amqp</artifactId>
+        <version>2.18.0-SNAPSHOT</version>
+    </parent>
+    <properties>
+        <activemq.basedir>${project.basedir}/../../../..</activemq.basedir>
+    </properties>
+
+    <artifactId>sasl-scram</artifactId>
+    <packaging>pom</packaging>
+    <name>ActiveMQ Artemis SASL-SCRAM Example</name>
+
+    <modules>
+        <module>sasl-client</module>
+        <module>sasl-server</module>
+    </modules>
+</project>
\ No newline at end of file
diff --git a/examples/protocols/amqp/sasl-scram/sasl-client/pom.xml b/examples/protocols/amqp/sasl-scram/sasl-client/pom.xml
new file mode 100644
index 0000000..c05fde8
--- /dev/null
+++ b/examples/protocols/amqp/sasl-scram/sasl-client/pom.xml
@@ -0,0 +1,49 @@
+<?xml version='1.0'?>
+<!--
+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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.activemq.examples.amqp</groupId>
+        <artifactId>sasl-scram</artifactId>
+        <version>2.18.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>sasl-scram-client</artifactId>
+    <packaging>jar</packaging>
+    <name>ActiveMQ Artemis SASL-SCRAM-Client Example</name>
+
+    <properties>
+        <activemq.basedir>${project.basedir}/../../../../..</activemq.basedir>
+        <artemis-version>${project.version}</artemis-version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.qpid</groupId>
+            <artifactId>qpid-jms-client</artifactId>
+            <version>${qpid.jms.version}</version>
+        </dependency>
+    </dependencies>
+
+</project>
\ No newline at end of file
diff --git a/examples/protocols/amqp/sasl-scram/sasl-client/readme.md b/examples/protocols/amqp/sasl-scram/sasl-client/readme.md
new file mode 100644
index 0000000..50fe5d7
--- /dev/null
+++ b/examples/protocols/amqp/sasl-scram/sasl-client/readme.md
@@ -0,0 +1,3 @@
+# Artemis SASL-SCRAM Server and Client Example
+
+demonstrate the usage of SASL-SCRAM authentication with ActiveMQ Artemis
diff --git a/examples/protocols/amqp/sasl-scram/sasl-client/src/main/java/org/apache/activemq/artemis/jms/example/QPIDClient.java b/examples/protocols/amqp/sasl-scram/sasl-client/src/main/java/org/apache/activemq/artemis/jms/example/QPIDClient.java
new file mode 100644
index 0000000..5e4dd2e
--- /dev/null
+++ b/examples/protocols/amqp/sasl-scram/sasl-client/src/main/java/org/apache/activemq/artemis/jms/example/QPIDClient.java
@@ -0,0 +1,48 @@
+/*
+ * <p>
+ * All rights reserved. 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.activemq.artemis.jms.example;
+
+import javax.jms.Connection;
+import javax.jms.ConnectionFactory;
+import javax.jms.JMSException;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+
+import org.apache.qpid.jms.JmsConnectionFactory;
+
+public class QPIDClient {
+   public static void main(String[] args) throws JMSException {
+      sendReceive("SCRAM-SHA-1", "hello", "ogre1234");
+      sendReceive("SCRAM-SHA-256", "test", "test");
+   }
+
+   private static void sendReceive(String method, String username, String password) throws JMSException {
+      ConnectionFactory connectionFactory =
+               new JmsConnectionFactory("amqp://localhost:5672?amqp.saslMechanisms=" + method);
+      try (Connection connection = connectionFactory.createConnection(username, password)) {
+         Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+         Queue queue = session.createQueue("exampleQueue");
+         MessageProducer sender = session.createProducer(queue);
+         sender.send(session.createTextMessage("Hello " + method));
+         connection.start();
+         MessageConsumer consumer = session.createConsumer(queue);
+         TextMessage m = (TextMessage) consumer.receive(5000);
+         System.out.println("message = " + m.getText());
+      }
+   }
+}
diff --git a/examples/protocols/amqp/sasl-scram/sasl-server/pom.xml b/examples/protocols/amqp/sasl-scram/sasl-server/pom.xml
new file mode 100644
index 0000000..e1683ab
--- /dev/null
+++ b/examples/protocols/amqp/sasl-scram/sasl-server/pom.xml
@@ -0,0 +1,54 @@
+<?xml version='1.0'?>
+<!--
+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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.activemq.examples.amqp</groupId>
+        <artifactId>sasl-scram</artifactId>
+        <version>2.18.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>sasl-scram-server</artifactId>
+    <packaging>jar</packaging>
+    <name>ActiveMQ Artemis SASL-SCRAM-Server Example</name>
+
+    <properties>
+        <activemq.basedir>${project.basedir}/../../../../..</activemq.basedir>
+        <artemis-version>${project.version}</artemis-version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.activemq</groupId>
+            <artifactId>artemis-server</artifactId>
+            <version>${artemis-version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.activemq</groupId>
+            <artifactId>artemis-amqp-protocol</artifactId>
+            <version>${artemis-version}</version>
+        </dependency>
+    </dependencies>
+
+</project>
\ No newline at end of file
diff --git a/examples/protocols/amqp/sasl-scram/sasl-server/readme.md b/examples/protocols/amqp/sasl-scram/sasl-server/readme.md
new file mode 100644
index 0000000..1cc11ed
--- /dev/null
+++ b/examples/protocols/amqp/sasl-scram/sasl-server/readme.md
@@ -0,0 +1,3 @@
+# Artemis SASL-SCRAM Server and Client Example
+
+demonstrate the usage of SASL-SCRAM authentication with ActiveMQ Artemis
\ No newline at end of file
diff --git a/examples/protocols/amqp/sasl-scram/sasl-server/src/main/java/org/apache/activemq/artemis/jms/example/TestServer.java b/examples/protocols/amqp/sasl-scram/sasl-server/src/main/java/org/apache/activemq/artemis/jms/example/TestServer.java
new file mode 100644
index 0000000..4bb92d3
--- /dev/null
+++ b/examples/protocols/amqp/sasl-scram/sasl-server/src/main/java/org/apache/activemq/artemis/jms/example/TestServer.java
@@ -0,0 +1,35 @@
+/*
+ * <p>
+ * All rights reserved. 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.activemq.artemis.jms.example;
+
+import java.io.File;
+
+import org.apache.activemq.artemis.core.server.embedded.EmbeddedActiveMQ;
+import org.apache.activemq.artemis.spi.core.security.ActiveMQJAASSecurityManager;
+
+public class TestServer {
+   public static void main(String[] args) throws Exception {
+      File configFolder = new File(args.length > 0 ? args[0] : "src/main/resources/").getAbsoluteFile();
+      System.setProperty("java.security.auth.login.config", new File(configFolder, "login.conf").getAbsolutePath());
+      EmbeddedActiveMQ embedded = new EmbeddedActiveMQ();
+      embedded.setSecurityManager(new ActiveMQJAASSecurityManager("artemis"));
+      embedded.setConfigResourcePath(new File(configFolder, "broker.xml").getAbsoluteFile().toURI().toASCIIString());
+      embedded.start();
+      while (true) {
+         // intentional empty
+      }
+
+   }
+}
diff --git a/examples/protocols/amqp/sasl-scram/sasl-server/src/main/resources/artemis-roles.properties b/examples/protocols/amqp/sasl-scram/sasl-server/src/main/resources/artemis-roles.properties
new file mode 100644
index 0000000..083c1e8
--- /dev/null
+++ b/examples/protocols/amqp/sasl-scram/sasl-server/src/main/resources/artemis-roles.properties
@@ -0,0 +1,18 @@
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+user=hello,test
+admin=test
\ No newline at end of file
diff --git a/examples/protocols/amqp/sasl-scram/sasl-server/src/main/resources/artemis-users.properties b/examples/protocols/amqp/sasl-scram/sasl-server/src/main/resources/artemis-users.properties
new file mode 100644
index 0000000..4846235
--- /dev/null
+++ b/examples/protocols/amqp/sasl-scram/sasl-server/src/main/resources/artemis-users.properties
@@ -0,0 +1,24 @@
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+##
+
+# Example for an encoded username/password, encoded forms can be generated with java org.apache.activemq.artemis.spi.core.security.jaas.SCRAMPropertiesLoginModule <username> <password> [<iterations>]
+test|SHA512 = ENC(7TilOEFipzE4KNkDUTlfnuMkYE1yveyXmK6iBx8/fnE=:4096:yPl/n8eZQEyVmkhuYvrgZCchEpO+a9QiGLXwJfqBWOIfTxMX5TkoHp5eYGABc68cUvoynqCnoqRLDPac+H1urg==:eX5X39hbChbXz00TCkMpmsHqsJTiMGCwamty6yjUS0M+HoE/SLtd2MYY1Shyn+5mu30qFsbXz0WlRA+dZ3Lv3A==)
+test|SHA256 = ENC(yNekJSAvbunYIIHKni32oXgg7uCSUZSzvgNq3pLL3so=:4096:45p4iB+tgMB2b2FM6MmuzyTF63QOfQroQLwNXxhCZ48=:PXUabvM/90DWQsl/p9Cp7wYlavCTPJZnzdU9PFUuiXc=)
+test|SHA1 = ENC(ehArM+Qzko2eua0hMq0o+NQ9BaTTf4q8xY0tzfy2Zvw=:4096:LvpLr4ezL4ICxeiXAkXEVH9EhO0=:gLELi8NpLVorxXbPIIbVZF/oqh8=)
+# Example for a plain username/password, don't use this on public servers!
+hello = ogre1234
\ No newline at end of file
diff --git a/examples/protocols/amqp/sasl-scram/sasl-server/src/main/resources/broker.xml b/examples/protocols/amqp/sasl-scram/sasl-server/src/main/resources/broker.xml
new file mode 100644
index 0000000..a0252ac
--- /dev/null
+++ b/examples/protocols/amqp/sasl-scram/sasl-server/src/main/resources/broker.xml
@@ -0,0 +1,50 @@
+<?xml version='1.0'?>
+<!--
+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.
+-->
+<configuration
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xmlns="urn:activemq"
+    xsi:schemaLocation="urn:activemq/schema/artemis-server.xsd">
+    <core xmlns="urn:activemq:core">
+
+        <persistence-enabled>false</persistence-enabled>
+
+        <security-enabled>true</security-enabled>
+
+        <acceptors>
+            <acceptor name="amqp">tcp://localhost:5672?protocols=AMQP;saslMechanisms=SCRAM-SHA-256,SCRAM-SHA-1;saslLoginConfigScope=amqp-sasl-scram
+            </acceptor>
+        </acceptors>
+        <security-settings>
+            <security-setting match="#">
+                <permission type="createAddress" roles="user" />
+                <permission type="createDurableQueue"
+                    roles="user" />
+                <permission type="deleteDurableQueue"
+                    roles="user" />
+                <permission type="createNonDurableQueue"
+                    roles="user" />
+                <permission type="deleteNonDurableQueue"
+                    roles="user" />
+                <permission type="consume" roles="user" />
+                <permission type="send" roles="user" />
+            </security-setting>
+        </security-settings>
+    </core>
+</configuration>
\ No newline at end of file
diff --git a/examples/protocols/amqp/sasl-scram/sasl-server/src/main/resources/login.conf b/examples/protocols/amqp/sasl-scram/sasl-server/src/main/resources/login.conf
new file mode 100644
index 0000000..7fad76f
--- /dev/null
+++ b/examples/protocols/amqp/sasl-scram/sasl-server/src/main/resources/login.conf
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+ 
+amqp-sasl-scram {
+   org.apache.activemq.artemis.spi.core.security.jaas.SCRAMPropertiesLoginModule required
+       debug=false
+       org.apache.activemq.jaas.properties.user="artemis-users.properties"
+       org.apache.activemq.jaas.properties.role="artemis-roles.properties";
+};
+
+
+artemis {
+    org.apache.activemq.artemis.spi.core.security.jaas.SCRAMLoginModule required
+    ;
+};
\ No newline at end of file
diff --git a/tests/integration-tests/.gitignore b/tests/integration-tests/.gitignore
new file mode 100644
index 0000000..00d2ab7
--- /dev/null
+++ b/tests/integration-tests/.gitignore
@@ -0,0 +1,2 @@
+/.apt_generated/
+/.apt_generated_tests/
diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/connect/AMQPConnectSaslTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/connect/AMQPConnectSaslTest.java
index 43c938d..0a37e89 100644
--- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/connect/AMQPConnectSaslTest.java
+++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/connect/AMQPConnectSaslTest.java
@@ -17,13 +17,24 @@
 package org.apache.activemq.artemis.tests.integration.amqp.connect;
 
 import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
 import java.util.Objects;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
 
+import javax.crypto.Mac;
+import javax.security.auth.login.LoginException;
+
 import org.apache.activemq.artemis.core.config.amqpBrokerConnectivity.AMQPBrokerConnectConfiguration;
 import org.apache.activemq.artemis.core.server.ActiveMQServer;
+import org.apache.activemq.artemis.protocol.amqp.sasl.SASLResult;
+import org.apache.activemq.artemis.protocol.amqp.sasl.scram.SCRAMServerSASL;
+import org.apache.activemq.artemis.spi.core.security.scram.SCRAM;
+import org.apache.activemq.artemis.spi.core.security.scram.ScramUtils;
+import org.apache.activemq.artemis.spi.core.security.scram.UserData;
 import org.apache.activemq.artemis.tests.integration.amqp.AmqpClientTestSupport;
 import org.apache.qpid.proton.engine.Sasl;
 import org.apache.qpid.proton.engine.Sasl.SaslOutcome;
@@ -107,7 +118,7 @@ public class AMQPConnectSaslTest extends AmqpClientTestSupport {
 
       // No user or pass given, it will have to select ANONYMOUS even though PLAIN also offered
       AMQPBrokerConnectConfiguration amqpConnection =
-            new AMQPBrokerConnectConfiguration("testSimpleConnect", "tcp://localhost:" + mockServer.actualPort());
+               new AMQPBrokerConnectConfiguration("testSimpleConnect", "tcp://localhost:" + mockServer.actualPort());
       amqpConnection.setReconnectAttempts(0);// No reconnects
 
       server.getConfiguration().addAMQPConnection(amqpConnection);
@@ -135,7 +146,8 @@ public class AMQPConnectSaslTest extends AmqpClientTestSupport {
       });
 
       // User and pass are given, it will select PLAIN
-      AMQPBrokerConnectConfiguration amqpConnection = new AMQPBrokerConnectConfiguration("testSimpleConnect", "tcp://localhost:" + mockServer.actualPort());
+      AMQPBrokerConnectConfiguration amqpConnection =
+               new AMQPBrokerConnectConfiguration("testSimpleConnect", "tcp://localhost:" + mockServer.actualPort());
       amqpConnection.setReconnectAttempts(0);// No reconnects
       amqpConnection.setUser(USER);
       amqpConnection.setPassword(PASSWD);
@@ -151,6 +163,36 @@ public class AMQPConnectSaslTest extends AmqpClientTestSupport {
       assertArrayEquals(expectedPlainInitialResponse(USER, PASSWD), authenticator.getInitialResponse());
    }
 
+   @Test(timeout = 200000)
+   public void testConnectsWithSCRAM() throws Exception {
+      CountDownLatch serverConnectionOpen = new CountDownLatch(1);
+      SCRAMTestAuthenticator authenticator = new SCRAMTestAuthenticator(SCRAM.SHA512);
+
+      mockServer = new MockServer(vertx, () -> authenticator, serverConnection -> {
+         serverConnection.openHandler(serverSender -> {
+            serverConnectionOpen.countDown();
+            serverConnection.closeHandler(x -> serverConnection.close());
+            serverConnection.open();
+         });
+      });
+
+      AMQPBrokerConnectConfiguration amqpConnection =
+               new AMQPBrokerConnectConfiguration("testSScramConnect", "tcp://localhost:" + mockServer.actualPort());
+      amqpConnection.setReconnectAttempts(0);// No reconnects
+      amqpConnection.setUser(USER);
+      amqpConnection.setPassword(PASSWD);
+
+      server.getConfiguration().addAMQPConnection(amqpConnection);
+
+      server.start();
+
+      boolean awaitConnectionOpen = serverConnectionOpen.await(10, TimeUnit.SECONDS);
+      assertTrue("Broker did not open connection in alotted time", awaitConnectionOpen);
+      assertEquals(SCRAM.SHA512.getName(), authenticator.chosenMech);
+      assertTrue(authenticator.succeeded());
+
+   }
+
    @Test(timeout = 20000)
    public void testConnectsWithExternal() throws Exception {
       doConnectWithExternalTestImpl(true);
@@ -161,10 +203,13 @@ public class AMQPConnectSaslTest extends AmqpClientTestSupport {
       doConnectWithExternalTestImpl(false);
    }
 
-   private void doConnectWithExternalTestImpl(boolean requireClientCert) throws ExecutionException, InterruptedException, Exception {
+   private void doConnectWithExternalTestImpl(boolean requireClientCert) throws ExecutionException,
+                                                                         InterruptedException, Exception {
       CountDownLatch serverConnectionOpen = new CountDownLatch(1);
-      // The test server always offers EXTERNAL, i.e sometimes mistakenly, to verify that the broker only selects it when it actually
-      // has a client-cert. Real servers shouldnt actually offer the mechanism to a client that didnt have to provide a cert.
+      // The test server always offers EXTERNAL, i.e sometimes mistakenly, to verify that the broker
+      // only selects it when it actually
+      // has a client-cert. Real servers shouldnt actually offer the mechanism to a client that
+      // didnt have to provide a cert.
       TestAuthenticator authenticator = new TestAuthenticator(true, EXTERNAL, PLAIN);
 
       final String keyStorePath = this.getClass().getClassLoader().getResource(SERVER_KEYSTORE_NAME).getFile();
@@ -191,14 +236,17 @@ public class AMQPConnectSaslTest extends AmqpClientTestSupport {
       });
 
       String amqpServerConnectionURI = "tcp://localhost:" + mockServer.actualPort() +
-            "?sslEnabled=true;trustStorePath=" + TRUSTSTORE_NAME + ";trustStorePassword=" + TRUSTSTORE_PASSWORD;
+               "?sslEnabled=true;trustStorePath=" + TRUSTSTORE_NAME + ";trustStorePassword=" + TRUSTSTORE_PASSWORD;
       if (requireClientCert) {
-         amqpServerConnectionURI += ";keyStorePath=" + CLIENT_KEYSTORE_NAME + ";keyStorePassword=" + CLIENT_KEYSTORE_PASSWORD;
+         amqpServerConnectionURI +=
+                  ";keyStorePath=" + CLIENT_KEYSTORE_NAME + ";keyStorePassword=" + CLIENT_KEYSTORE_PASSWORD;
       }
 
-      AMQPBrokerConnectConfiguration amqpConnection = new AMQPBrokerConnectConfiguration("testSimpleConnect", amqpServerConnectionURI);
+      AMQPBrokerConnectConfiguration amqpConnection =
+               new AMQPBrokerConnectConfiguration("testSimpleConnect", amqpServerConnectionURI);
       amqpConnection.setReconnectAttempts(0);// No reconnects
-      amqpConnection.setUser(USER); // Wont matter if EXTERNAL is offered and a client-certificate is provided, but will otherwise.
+      amqpConnection.setUser(USER); // Wont matter if EXTERNAL is offered and a client-certificate
+                                    // is provided, but will otherwise.
       amqpConnection.setPassword(PASSWD);
 
       server.getConfiguration().addAMQPConnection(amqpConnection);
@@ -236,8 +284,8 @@ public class AMQPConnectSaslTest extends AmqpClientTestSupport {
 
    private static final class TestAuthenticator implements ProtonSaslAuthenticator {
       private Sasl sasl;
-      private boolean succeed;
-      private String[] offeredMechs;
+      private final boolean succeed;
+      private final String[] offeredMechs;
       String chosenMech = null;
       byte[] initialResponse = null;
       boolean done = false;
@@ -268,7 +316,6 @@ public class AMQPConnectSaslTest extends AmqpClientTestSupport {
 
                initialResponse = new byte[sasl.pending()];
                sasl.recv(initialResponse, 0, initialResponse.length);
-
                if (succeed) {
                   sasl.done(SaslOutcome.PN_SASL_OK);
                } else {
@@ -296,4 +343,103 @@ public class AMQPConnectSaslTest extends AmqpClientTestSupport {
       }
    }
 
+   private static final class SCRAMTestAuthenticator implements ProtonSaslAuthenticator {
+
+      private final SCRAM mech;
+      private Sasl sasl;
+      private TestSCRAMServerSASL serverSASL;
+      private String chosenMech;
+
+      SCRAMTestAuthenticator(SCRAM mech) {
+         this.mech = mech;
+      }
+
+      @Override
+      public void init(NetSocket socket, ProtonConnection protonConnection, Transport transport) {
+         this.sasl = transport.sasl();
+         sasl.server();
+         sasl.allowSkip(false);
+         sasl.setMechanisms(mech.getName(), PLAIN, ANONYMOUS);
+         try {
+            serverSASL = new TestSCRAMServerSASL(mech);
+         } catch (NoSuchAlgorithmException e) {
+            throw new AssertionError(e);
+         }
+
+      }
+
+      @Override
+      public void process(Handler<Boolean> completionHandler) {
+         String[] remoteMechanisms = sasl.getRemoteMechanisms();
+         int pending = sasl.pending();
+         if (remoteMechanisms.length == 0 || pending == 0) {
+            completionHandler.handle(false);
+            return;
+         }
+         chosenMech = remoteMechanisms[0];
+         byte[] msg = new byte[pending];
+         sasl.recv(msg, 0, msg.length);
+         byte[] result = serverSASL.processSASL(msg);
+         if (result != null) {
+            sasl.send(result, 0, result.length);
+         }
+         boolean ended = serverSASL.isEnded();
+         if (ended) {
+            if (succeeded()) {
+               sasl.done(SaslOutcome.PN_SASL_OK);
+            } else {
+               sasl.done(SaslOutcome.PN_SASL_AUTH);
+            }
+            completionHandler.handle(true);
+         } else {
+            completionHandler.handle(false);
+         }
+      }
+
+      @Override
+      public boolean succeeded() {
+         SASLResult result = serverSASL.result();
+         return result != null && result.isSuccess() && serverSASL.e == null;
+      }
+
+   }
+
+   private static final class TestSCRAMServerSASL extends SCRAMServerSASL {
+
+      private Exception e;
+
+      TestSCRAMServerSASL(SCRAM mechanism) throws NoSuchAlgorithmException {
+         super(mechanism);
+      }
+
+      @Override
+      public void done() {
+         // nothing to do
+      }
+
+      @Override
+      protected UserData aquireUserData(String userName) throws LoginException {
+         if (!USER.equals(userName)) {
+            throw new LoginException("invalid username");
+         }
+         byte[] salt = new byte[32];
+         new SecureRandom().nextBytes(salt);
+         try {
+            MessageDigest digest = MessageDigest.getInstance(mechanism.getDigest());
+            Mac hmac = Mac.getInstance(mechanism.getHmac());
+            ScramUtils.NewPasswordStringData data =
+                     ScramUtils.byteArrayToStringData(ScramUtils.newPassword(PASSWD, salt, 4096, digest, hmac));
+            return new UserData(data.salt, data.iterations, data.serverKey, data.storedKey);
+         } catch (Exception e) {
+            throw new LoginException(e.getMessage());
+         }
+      }
+
+      @Override
+      protected void failed(Exception e) {
+         this.e = e;
+      }
+
+   }
+
 }
diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/sasl/SaslScramTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/sasl/SaslScramTest.java
new file mode 100644
index 0000000..76b1d4f
--- /dev/null
+++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/sasl/SaslScramTest.java
@@ -0,0 +1,117 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.activemq.artemis.tests.integration.amqp.sasl;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.File;
+
+import javax.jms.Connection;
+import javax.jms.ConnectionFactory;
+import javax.jms.JMSException;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+
+import org.apache.activemq.artemis.core.server.embedded.EmbeddedActiveMQ;
+import org.apache.activemq.artemis.spi.core.security.ActiveMQJAASSecurityManager;
+import org.apache.qpid.jms.JmsConnectionFactory;
+import org.apache.qpid.jms.exceptions.JMSSecuritySaslException;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * This test SASL-SCRAM Support
+ */
+public class SaslScramTest {
+
+   private static EmbeddedActiveMQ BROKER;
+
+   @BeforeClass
+   public static void startBroker() throws Exception {
+      String loginConfPath = new File(SaslScramTest.class.getResource("/login.config").toURI()).getAbsolutePath();
+      System.out.println(loginConfPath);
+      System.setProperty("java.security.auth.login.config", loginConfPath);
+      BROKER = new EmbeddedActiveMQ();
+      BROKER.setConfigResourcePath(SaslScramTest.class.getResource("/broker-saslscram.xml").toExternalForm());
+      BROKER.setSecurityManager(new ActiveMQJAASSecurityManager("artemis-sasl-scram"));
+      BROKER.start();
+   }
+
+   @AfterClass
+   public static void shutdownBroker() throws Exception {
+      BROKER.stop();
+   }
+
+   /**
+    * Checks if a user with plain text password can login using all mechanisms
+    * @throws JMSException should not happen
+    */
+   @Test
+   public void testUnencryptedWorksWithAllMechanism() throws JMSException {
+      sendRcv("SCRAM-SHA-1", "hello", "ogre1234");
+      sendRcv("SCRAM-SHA-256", "hello", "ogre1234");
+   }
+
+   /**
+    * Checks that a user that has encrypted passwords for all mechanism can login with any of them
+    * @throws JMSException should not happen
+    */
+   @Test
+   public void testEncryptedWorksWithAllMechanism() throws JMSException {
+      sendRcv("SCRAM-SHA-1", "multi", "worksforall");
+      sendRcv("SCRAM-SHA-256", "multi", "worksforall");
+   }
+
+   /**
+    * Checks that a user that is only stored with one explicit mechanism can't use another mechanism
+    * @throws JMSException is expected
+    */
+   @Test(expected = JMSSecuritySaslException.class)
+   public void testEncryptedWorksOnlyWithMechanism() throws JMSException {
+      sendRcv("SCRAM-SHA-1", "test", "test");
+   }
+
+   /**
+    * Checks that a user that is only stored with one explicit mechanism can login with this
+    * mechanism
+    * @throws JMSException should not happen
+    */
+   @Test
+   public void testEncryptedWorksWithMechanism() throws JMSException {
+      sendRcv("SCRAM-SHA-256", "test", "test");
+   }
+
+   private void sendRcv(String method, String username, String password) throws JMSException {
+      ConnectionFactory connectionFactory =
+               new JmsConnectionFactory("amqp://localhost:5672?amqp.saslMechanisms=" + method);
+      try (Connection connection = connectionFactory.createConnection(username, password)) {
+         Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+         Queue queue = session.createQueue("exampleQueue");
+         MessageProducer sender = session.createProducer(queue);
+         String text = "Hello " + method;
+         sender.send(session.createTextMessage(text));
+         connection.start();
+         MessageConsumer consumer = session.createConsumer(queue);
+         TextMessage m = (TextMessage) consumer.receive(5000);
+         assertEquals(text, m.getText());
+      }
+   }
+}
diff --git a/tests/integration-tests/src/test/resources/artemis-scram-roles.properties b/tests/integration-tests/src/test/resources/artemis-scram-roles.properties
new file mode 100644
index 0000000..ccff1c4
--- /dev/null
+++ b/tests/integration-tests/src/test/resources/artemis-scram-roles.properties
@@ -0,0 +1,18 @@
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+user=hello,test,multi
+admin=multi
\ No newline at end of file
diff --git a/tests/integration-tests/src/test/resources/artemis-scram-users.properties b/tests/integration-tests/src/test/resources/artemis-scram-users.properties
new file mode 100644
index 0000000..23a2e36
--- /dev/null
+++ b/tests/integration-tests/src/test/resources/artemis-scram-users.properties
@@ -0,0 +1,26 @@
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+
+# Example for an encoded username/password, encoded forms can be generated with java org.apache.activemq.artemis.spi.core.security.jaas.SCRAMPropertiesLoginModule <username> <password> [<iterations>]
+multi|SHA256 = ENC(o3ljCITL4Cw6pu+fxvz4k68F8jQuZAITcRNpy2THufw=:4096:Niuc0/lWg/YQztHqJCJ5SodyxbWPtGj6zp/HHPqSDBY=:O1YL/w08fvuTvqctbHrr4TxpzKso+NCdqt4Amqp7r0k=)
+multi|SHA1 = ENC(cJyfpU4wgmoSz1GVc39+CooXY2jIv2ILe7486+l9vbg=:4096:sMCix9TeOvmo2eb4xbCjQt4navs=:fSKdLYAgdx36RMjjMSn1dZ7IpY8=)
+
+# Example for a plain username/password, don't use this on public servers!
+hello = ogre1234
+
+# just for unit-test purpose!
+test = ENC(yNekJSAvbunYIIHKni32oXgg7uCSUZSzvgNq3pLL3so=:4096:45p4iB+tgMB2b2FM6MmuzyTF63QOfQroQLwNXxhCZ48=:PXUabvM/90DWQsl/p9Cp7wYlavCTPJZnzdU9PFUuiXc=)
diff --git a/tests/integration-tests/src/test/resources/broker-saslscram.xml b/tests/integration-tests/src/test/resources/broker-saslscram.xml
new file mode 100644
index 0000000..a0252ac
--- /dev/null
+++ b/tests/integration-tests/src/test/resources/broker-saslscram.xml
@@ -0,0 +1,50 @@
+<?xml version='1.0'?>
+<!--
+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.
+-->
+<configuration
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xmlns="urn:activemq"
+    xsi:schemaLocation="urn:activemq/schema/artemis-server.xsd">
+    <core xmlns="urn:activemq:core">
+
+        <persistence-enabled>false</persistence-enabled>
+
+        <security-enabled>true</security-enabled>
+
+        <acceptors>
+            <acceptor name="amqp">tcp://localhost:5672?protocols=AMQP;saslMechanisms=SCRAM-SHA-256,SCRAM-SHA-1;saslLoginConfigScope=amqp-sasl-scram
+            </acceptor>
+        </acceptors>
+        <security-settings>
+            <security-setting match="#">
+                <permission type="createAddress" roles="user" />
+                <permission type="createDurableQueue"
+                    roles="user" />
+                <permission type="deleteDurableQueue"
+                    roles="user" />
+                <permission type="createNonDurableQueue"
+                    roles="user" />
+                <permission type="deleteNonDurableQueue"
+                    roles="user" />
+                <permission type="consume" roles="user" />
+                <permission type="send" roles="user" />
+            </security-setting>
+        </security-settings>
+    </core>
+</configuration>
\ No newline at end of file
diff --git a/tests/integration-tests/src/test/resources/login.config b/tests/integration-tests/src/test/resources/login.config
index a70836b..6ff980c 100644
--- a/tests/integration-tests/src/test/resources/login.config
+++ b/tests/integration-tests/src/test/resources/login.config
@@ -319,3 +319,15 @@ amqp-jms-client {
     com.sun.security.auth.module.Krb5LoginModule required
     useKeyTab=true;
 };
+
+amqp-sasl-scram {
+   org.apache.activemq.artemis.spi.core.security.jaas.SCRAMPropertiesLoginModule required
+       debug=false
+       org.apache.activemq.jaas.properties.user="artemis-scram-users.properties"
+       org.apache.activemq.jaas.properties.role="artemis-scram-roles.properties";
+};
+
+artemis-sasl-scram {
+    org.apache.activemq.artemis.spi.core.security.jaas.SCRAMLoginModule required
+    ;
+};