DIRKRB-751: Fix remote kadmin sasl negotiation (#54)

diff --git a/kerby-kerb/kerb-admin-server/src/main/java/org/apache/kerby/kerberos/kerb/admin/server/kadmin/AdminServerHandler.java b/kerby-kerb/kerb-admin-server/src/main/java/org/apache/kerby/kerberos/kerb/admin/server/kadmin/AdminServerHandler.java
index e5a4b08..6510cbf 100644
--- a/kerby-kerb/kerb-admin-server/src/main/java/org/apache/kerby/kerberos/kerb/admin/server/kadmin/AdminServerHandler.java
+++ b/kerby-kerb/kerb-admin-server/src/main/java/org/apache/kerby/kerberos/kerb/admin/server/kadmin/AdminServerHandler.java
@@ -41,6 +41,7 @@
 import org.apache.kerby.xdr.XdrDataType;
 import org.apache.kerby.xdr.XdrFieldInfo;
 import org.apache.kerby.xdr.type.XdrStructType;
+import org.xnio.sasl.SaslWrapper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -60,6 +61,7 @@
 public class AdminServerHandler {
     private static final Logger LOG = LoggerFactory.getLogger(AdminServerHandler.class);
     private final AdminServerContext adminServerContext;
+    private SaslWrapper saslServerWrapper;
 
     /**
      * Constructor with admin server context.
@@ -71,6 +73,14 @@
         LOG.info("Admin realm: " + this.adminServerContext.getAdminRealm());
     }
 
+    public void setSaslServerWrapper(SaslWrapper saslServerWrapper) {
+        this.saslServerWrapper = saslServerWrapper;
+    }
+
+    public SaslWrapper getSaslServerWrapper() {
+        return saslServerWrapper;
+    }
+
     /**
      * Process the client request message.
      *
@@ -206,7 +216,8 @@
     }
 
     private ByteBuffer handleGetprincsReq(LocalKadmin localKadmin, XdrFieldInfo[] fieldInfos) throws IOException {
-        String globString = ((String) fieldInfos[2].getValue());
+        int paramNum = ((int) fieldInfos[1].getValue());
+        String globString = paramNum != 0 ? ((String) fieldInfos[2].getValue()) : null;
         List<String> princsList = null;
 
         try {
@@ -326,7 +337,7 @@
         AdminMessageCode value = new AdminMessageCode(xdrFieldInfos);
         adminMessage.setMessageBuffer(ByteBuffer.wrap(value.encode()));
 
-        return KadminCode.encodeMessage(adminMessage);
+        return KadminCode.encodeWrapMessage(adminMessage, getSaslServerWrapper());
     }
     
     private ByteBuffer infoPackageTool(File keytabFile, String dealType) throws IOException {
@@ -343,7 +354,7 @@
         KeytabMessageCode value = new KeytabMessageCode(xdrFieldInfos);
         adminMessage.setMessageBuffer(ByteBuffer.wrap(value.encode()));
 
-        return KadminCode.encodeMessage(adminMessage);
+        return KadminCode.encodeWrapMessage(adminMessage, getSaslServerWrapper());
     }
 
     private ByteBuffer infoPackageTool(KrbIdentity identity, String dealType) throws IOException {
@@ -370,7 +381,7 @@
         IdentityInfoCode value = new IdentityInfoCode(xdrFieldInfos);
         adminMessage.setMessageBuffer(ByteBuffer.wrap(value.encode()));
 
-        return KadminCode.encodeMessage(adminMessage);
+        return KadminCode.encodeWrapMessage(adminMessage, getSaslServerWrapper());
     }
 
     private String listToString(List<String> list) {
diff --git a/kerby-kerb/kerb-admin-server/src/main/java/org/apache/kerby/kerberos/kerb/admin/server/kadmin/AdminServerUtil.java b/kerby-kerb/kerb-admin-server/src/main/java/org/apache/kerby/kerberos/kerb/admin/server/kadmin/AdminServerUtil.java
index f625c37..7978501 100644
--- a/kerby-kerb/kerb-admin-server/src/main/java/org/apache/kerby/kerberos/kerb/admin/server/kadmin/AdminServerUtil.java
+++ b/kerby-kerb/kerb-admin-server/src/main/java/org/apache/kerby/kerberos/kerb/admin/server/kadmin/AdminServerUtil.java
@@ -119,4 +119,17 @@
 
         return result;
     }
+
+    /**
+     * Fix principal name.
+     *
+     * @param principal The principal name
+     * @return The fixed principal
+     */
+    public static String fixPrincipal(String principal, AdminServerSetting setting) {
+        if (!principal.contains("@")) {
+            principal += "@" + setting.getKdcRealm();
+        }
+        return principal;
+    }
 }
diff --git a/kerby-kerb/kerb-admin-server/src/main/java/org/apache/kerby/kerberos/kerb/admin/server/kadmin/impl/DefaultAdminServerHandler.java b/kerby-kerb/kerb-admin-server/src/main/java/org/apache/kerby/kerberos/kerb/admin/server/kadmin/impl/DefaultAdminServerHandler.java
index b8eb0a7..50eff7e 100644
--- a/kerby-kerb/kerb-admin-server/src/main/java/org/apache/kerby/kerberos/kerb/admin/server/kadmin/impl/DefaultAdminServerHandler.java
+++ b/kerby-kerb/kerb-admin-server/src/main/java/org/apache/kerby/kerberos/kerb/admin/server/kadmin/impl/DefaultAdminServerHandler.java
@@ -20,9 +20,16 @@
 package org.apache.kerby.kerberos.kerb.admin.server.kadmin.impl;
 
 import org.apache.kerby.kerberos.kerb.admin.AuthUtil;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.remote.NegotiationStatus;
+import org.apache.kerby.kerberos.kerb.admin.message.KadminCode;
 import org.apache.kerby.kerberos.kerb.admin.server.kadmin.AdminServerContext;
 import org.apache.kerby.kerberos.kerb.admin.server.kadmin.AdminServerHandler;
+import org.apache.kerby.kerberos.kerb.admin.server.kadmin.AdminServerUtil;
+import org.apache.kerby.kerberos.kerb.common.KrbUtil;
+import org.apache.kerby.kerberos.kerb.transport.KrbTcpTransport;
 import org.apache.kerby.kerberos.kerb.transport.KrbTransport;
+import org.xnio.sasl.SaslUtils;
+import org.xnio.sasl.SaslWrapper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -32,20 +39,21 @@
 import javax.security.auth.callback.UnsupportedCallbackException;
 import javax.security.sasl.AuthorizeCallback;
 import javax.security.sasl.Sasl;
+import javax.security.sasl.SaslException;
 import javax.security.sasl.SaslServer;
 import java.io.File;
 import java.io.IOException;
 import java.net.InetAddress;
 import java.net.SocketTimeoutException;
 import java.nio.ByteBuffer;
-import java.security.PrivilegedAction;
+import java.security.PrivilegedExceptionAction;
 import java.util.HashMap;
 import java.util.Map;
 
 public class DefaultAdminServerHandler extends AdminServerHandler implements Runnable {
     private static Logger logger = LoggerFactory.getLogger(DefaultAdminServerHandler.class);
+    private static final String MECHANISM = "GSSAPI";
     private final KrbTransport transport;
-    private static boolean sasl = false;
     private AdminServerContext adminServerContext;
 
     public DefaultAdminServerHandler(AdminServerContext adminServerContext, KrbTransport transport) {
@@ -56,31 +64,30 @@
 
     @Override
     public void run() {
-        while (true) {
+        try {
+            doSaslHandshake();
+        } catch (Exception e) {
+            logger.error("With exception when SASL negotiation." + e);
+            return;
+        }
+        do {
             try {
-                if (!sasl) {
-                    logger.info("Doing the sasl negotiation !!!");
-                    try {
-                        saslNegotiation();
-                    } catch (Exception e) {
-                        logger.error("With exception when sasl negotiation." + e);
-                    }
-                } else {
-                    ByteBuffer message = transport.receiveMessage();
-                    if (message == null) {
-                        logger.debug("No valid request recved. Disconnect actively");
-                        transport.release();
-                        break;
-                    }
-                    handleMessage(message);
+                ByteBuffer message = transport.receiveMessage();
+                if (message == null) {
+                    logger.debug("No valid request recved. Disconnect actively");
+                    transport.release();
+                    break;
                 }
+                // unwrap SASL message
+                ByteBuffer unwrapMessage = ByteBuffer.wrap(getSaslServerWrapper().unwrap(message));
+                handleMessage(unwrapMessage);
             } catch (IOException e) {
                 transport.release();
                 logger.debug("Transport or decoding error occurred, "
                         + "disconnecting abnormally", e);
                 break;
             }
-        }
+        } while (!((KrbTcpTransport) transport).isClosed());
     }
 
     protected void handleMessage(ByteBuffer message) {
@@ -95,80 +102,95 @@
         }
     }
 
-    private void saslNegotiation() throws Exception {
-
+    private void doSaslHandshake() throws Exception {
         File keytabFile = new File(adminServerContext.getConfig().getKeyTabFile());
         String principal = adminServerContext.getConfig().getProtocol() + "/"
             + adminServerContext.getConfig().getAdminHost();
-
-        Subject subject = AuthUtil.loginUsingKeytab(principal, keytabFile);
-        Subject.doAs(subject, new PrivilegedAction<Object>() {
-            @Override
-            public Object run() {
+        String fixedPrincipal = AdminServerUtil.fixPrincipal(principal, adminServerContext.getAdminServerSetting());
+        String adminPrincipal = KrbUtil.makeKadminPrincipal(
+                adminServerContext.getAdminServerSetting().getKdcRealm()).getName();
+        
+        Subject subject = AuthUtil.loginUsingKeytab(fixedPrincipal, keytabFile);
+        Subject.doAs(subject, (PrivilegedExceptionAction<Object>) () -> {
+            boolean success = false;
+            try {
+                ByteBuffer message;
                 try {
-                    ByteBuffer message = null;
-                    try {
-                        message = transport.receiveMessage();
-                    } catch (SocketTimeoutException e) {
-                        // ignore time out
-                        return null;
-                    }
-
-                    Map<String, Object> props = new HashMap<>();
-                    props.put(Sasl.QOP, "auth-conf");
-                    props.put(Sasl.SERVER_AUTH, "true");
-
-                    String protocol = adminServerContext.getConfig().getProtocol();
-                    String serverName = adminServerContext.getConfig().getServerName();
-                    CallbackHandler callbackHandler = new SaslGssCallbackHandler();
-                    SaslServer ss = Sasl.createSaslServer("GSSAPI",
-                        protocol, serverName, props, callbackHandler);
-
-                    if (ss == null) {
-                        throw new Exception("Unable to find server implementation for: GSSAPI");
-                    }
-
-                    while (!ss.isComplete()) {
-                        int scComplete = message.getInt();
-                        if (scComplete == 0) {
-                            logger.info("sasl negotiation success!!!");
-                            sasl = true;
-                            break;
-                        }
-                        sendMessage(message, ss);
-                        if (!ss.isComplete()) {
-                            logger.info("Waiting receive message");
-                            message = transport.receiveMessage();
-                        }
-                    }
-                } catch (Exception e) {
-                    logger.error("With exception when sasl negotiation. " + e);
+                    message = transport.receiveMessage();
+                } catch (SocketTimeoutException ignore) {
+                    // Ignore time out, wake up to see if should continue to run.
+                    // When client create a new tpc transport, socket always timeout,
+                    // because the first connection will not send message to the server.
+                    // SASL handshake is not performed until the connection is established.
+                    return null;
                 }
-                return null;
-            }
-        });
 
+                Map<String, Object> props = new HashMap<>();
+                props.put(Sasl.QOP, "auth-conf");
+                props.put(Sasl.SERVER_AUTH, "true");
+                String protocol = adminServerContext.getConfig().getProtocol();
+                String serverName = adminServerContext.getConfig().getServerName();
+                CallbackHandler callbackHandler = new SaslGssCallbackHandler(adminPrincipal);
+                SaslServer saslServer = Sasl.createSaslServer(MECHANISM, protocol,
+                        serverName, props, callbackHandler);
+
+                if (saslServer == null) {
+                    throw new Exception("Unable to find server implementation for: GSSAPI");
+                }
+
+                setSaslServerWrapper(SaslWrapper.create(saslServer));
+
+                while (!saslServer.isComplete()) {
+                    int scComplete = message.getInt();
+                    if (scComplete == NegotiationStatus.SUCCESS.getValue()) {
+                        logger.info("Sasl Client completed");
+                    }
+
+                    byte[] challenge = null;
+                    try {
+                        challenge = SaslUtils.evaluateResponse(saslServer, message);
+                    } catch (SaslException e) {
+                        throw new Exception("Sasl server evaluate challenge failed. " + e);
+                    }
+
+                    if (!saslServer.isComplete()) {
+                        // Send message to client only when SASL server is not complete
+                        sendMessage(challenge, saslServer);
+
+                        logger.info("Waiting receive message");
+                        message = transport.receiveMessage();
+                    }
+                }
+                success = true;
+            } finally {
+                if (!success) {
+                    transport.release();
+                }
+            }
+            return null;
+        });
     }
 
-    private void sendMessage(ByteBuffer message, SaslServer ss) throws IOException {
-
-        byte[] arr = new byte[message.remaining()];
-        message.get(arr);
-        byte[] challenge = ss.evaluateResponse(arr);
-
-        // 4 is the head to go through network
-        ByteBuffer buffer = ByteBuffer.allocate(challenge.length + 8);
-        buffer.putInt(challenge.length + 4);
-        int ssComplete = ss.isComplete() ? 0 : 1;
-        buffer.putInt(ssComplete);
-        buffer.put(challenge);
-        buffer.flip();
-        transport.sendMessage(buffer);
-        logger.info("Send message to admin client.");
+    private void sendMessage(byte[] challenge, SaslServer saslServer) throws IOException {
+        NegotiationStatus status = saslServer.isComplete()
+                ? NegotiationStatus.SUCCESS : NegotiationStatus.CONTINUE;
+        ByteBuffer buffer = KadminCode.encodeSaslMessage(challenge, status);
+        try {
+            transport.sendMessage(buffer);
+            logger.info("Send message to admin client.");
+        } catch (SaslException e) {
+            logger.error("Failed to send message to client. " + e.toString());
+        }
     }
 
     private static class SaslGssCallbackHandler implements CallbackHandler {
 
+        private final String adminPrincipal;
+
+        SaslGssCallbackHandler(String principal) {
+            this.adminPrincipal = principal;
+        }
+        
         @Override
         public void handle(Callback[] callbacks) throws
             UnsupportedCallbackException {
@@ -184,9 +206,10 @@
             if (ac != null) {
                 String authid = ac.getAuthenticationID();
                 String authzid = ac.getAuthorizationID();
-                if (authid.equals(authzid)) {
+                if (authid.equals(authzid) && authid.equals(adminPrincipal)) {
                     ac.setAuthorized(true);
                 } else {
+                    logger.warn("Client try to login using principal " + authid);
                     ac.setAuthorized(false);
                 }
                 if (ac.isAuthorized()) {
diff --git a/kerby-kerb/kerb-admin-server/src/test/java/org/apache/kerby/kerberos/kerb/admin/server/RemoteKadminTest.java b/kerby-kerb/kerb-admin-server/src/test/java/org/apache/kerby/kerberos/kerb/admin/server/RemoteKadminTest.java
index 1d53c70..1f85708 100644
--- a/kerby-kerb/kerb-admin-server/src/test/java/org/apache/kerby/kerberos/kerb/admin/server/RemoteKadminTest.java
+++ b/kerby-kerb/kerb-admin-server/src/test/java/org/apache/kerby/kerberos/kerb/admin/server/RemoteKadminTest.java
@@ -24,7 +24,6 @@
 import org.apache.kerby.kerberos.kerb.admin.kadmin.local.LocalKadminImpl;
 import org.apache.kerby.kerberos.kerb.admin.kadmin.remote.AdminClient;
 import org.apache.kerby.kerberos.kerb.admin.kadmin.remote.AdminConfig;
-import org.apache.kerby.kerberos.kerb.admin.kadmin.remote.AdminUtil;
 import org.apache.kerby.kerberos.kerb.admin.server.kadmin.AdminServer;
 import org.apache.kerby.kerberos.kerb.admin.server.kadmin.AdminServerConfig;
 import org.apache.kerby.kerberos.kerb.identity.backend.BackendConfig;
@@ -33,9 +32,6 @@
 import org.apache.kerby.kerberos.kerb.server.KdcConfig;
 import org.apache.kerby.kerberos.kerb.server.KdcServer;
 import org.apache.kerby.kerberos.kerb.server.SimpleKdcServer;
-import org.apache.kerby.kerberos.kerb.transport.KrbNetwork;
-import org.apache.kerby.kerberos.kerb.transport.KrbTransport;
-import org.apache.kerby.kerberos.kerb.transport.TransportPair;
 import org.apache.kerby.kerberos.kerb.type.base.PrincipalName;
 import org.apache.kerby.util.NetworkUtil;
 import org.junit.AfterClass;
@@ -47,14 +43,8 @@
 
 import javax.security.auth.Subject;
 import javax.security.auth.login.LoginException;
-import javax.security.sasl.Sasl;
-import javax.security.sasl.SaslClient;
 import java.io.File;
-import java.nio.ByteBuffer;
-import java.security.PrivilegedAction;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 import java.util.stream.Collectors;
 
 import static org.junit.Assert.assertTrue;
@@ -219,64 +209,7 @@
     }
 
     private void doSaslHandShake(AdminClient adminClient, AdminConfig config) throws Exception {
-        TransportPair tpair = AdminUtil.getTransportPair(adminClient.getSetting());
-        KrbNetwork network = new KrbNetwork();
-        network.setSocketTimeout(adminClient.getSetting().getTimeout());
-        KrbTransport transport = network.connect(tpair);
         Subject subject = AuthUtil.loginUsingKeytab(ADMIN_PRINCIPAL, new File(config.getKeyTabFile()));
-        Subject.doAs(subject, (PrivilegedAction<Object>) () -> {
-            try {
-                Map<String, String> props = new HashMap<>();
-                props.put(Sasl.QOP, "auth-conf");
-                props.put(Sasl.SERVER_AUTH, "true");
-                SaslClient saslClient = null;
-
-                String protocol = config.getProtocol();
-                String serverName = config.getServerName();
-                saslClient = Sasl.createSaslClient(new String[]{"GSSAPI"}, null,
-                        protocol, serverName, props, null);
-                if (saslClient == null) {
-                    System.out.println("Unable to find client implementation for: GSSAPI");
-                    return null;
-                }
-                byte[] response;
-                response = saslClient.hasInitialResponse()
-                        ? saslClient.evaluateChallenge(new byte[0]) : new byte[0];
-
-                sendMessage(response, saslClient, transport);
-
-                ByteBuffer message = transport.receiveMessage();
-
-                while (!saslClient.isComplete()) {
-                    int ssComplete = message.getInt();
-                    if (ssComplete == 0) {
-                        System.out.println("Sasl Server completed");
-                    }
-                    byte[] arr = new byte[message.remaining()];
-                    message.get(arr);
-                    byte[] challenge = saslClient.evaluateChallenge(arr);
-
-                    sendMessage(challenge, saslClient, transport);
-
-                    if (!saslClient.isComplete()) {
-                        message = transport.receiveMessage();
-                    }
-                }
-            } catch (Exception e) {
-                LOG.warn(e.getMessage());
-            }
-            return null;
-        });
-    }
-
-    private void sendMessage(byte[] challenge, SaslClient saslClient, KrbTransport transport) throws Exception {
-        ByteBuffer buffer = ByteBuffer.allocate(challenge.length + 8);
-        buffer.putInt(challenge.length + 4);
-        int scComplete = saslClient.isComplete() ? 0 : 1;
-
-        buffer.putInt(scComplete);
-        buffer.put(challenge);
-        buffer.flip();
-        transport.sendMessage(buffer);
+        adminClient.setSubject(subject);
     }
 }
diff --git a/kerby-kerb/kerb-admin/pom.xml b/kerby-kerb/kerb-admin/pom.xml
index 84b110f..f6810d9 100644
--- a/kerby-kerb/kerb-admin/pom.xml
+++ b/kerby-kerb/kerb-admin/pom.xml
@@ -52,5 +52,10 @@
       <artifactId>jsch</artifactId>
       <version>${jsch.version}</version>
     </dependency>
+    <dependency>
+      <groupId>org.jboss.xnio</groupId>
+      <artifactId>xnio-api</artifactId>
+      <version>${xnio-api.version}</version>
+    </dependency>
   </dependencies>
 </project>
diff --git a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/AuthUtil.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/AuthUtil.java
index 763a53f..dad1e74 100644
--- a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/AuthUtil.java
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/AuthUtil.java
@@ -35,7 +35,7 @@
 
 public class AuthUtil {
 
-    public static final boolean ENABLE_DEBUG = true;
+    public static final boolean ENABLE_DEBUG = false;
 
     private static String getKrb5LoginModuleName() {
         return System.getProperty("java.vendor").contains("IBM")
diff --git a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/RemoteAdminClientTool.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/RemoteAdminClientTool.java
index 78cf4af..6dd2d2d 100644
--- a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/RemoteAdminClientTool.java
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/RemoteAdminClientTool.java
@@ -22,7 +22,6 @@
 import org.apache.kerby.kerberos.kerb.KrbException;
 import org.apache.kerby.kerberos.kerb.admin.kadmin.remote.AdminClient;
 import org.apache.kerby.kerberos.kerb.admin.kadmin.remote.AdminConfig;
-import org.apache.kerby.kerberos.kerb.admin.kadmin.remote.AdminUtil;
 import org.apache.kerby.kerberos.kerb.admin.kadmin.remote.command.RemoteCommand;
 import org.apache.kerby.kerberos.kerb.admin.kadmin.remote.command.RemoteAddPrincipalCommand;
 import org.apache.kerby.kerberos.kerb.admin.kadmin.remote.command.RemoteDeletePrincipalCommand;
@@ -34,9 +33,6 @@
 import org.apache.kerby.kerberos.kerb.common.KrbUtil;
 import org.apache.kerby.kerberos.kerb.server.KdcConfig;
 import org.apache.kerby.kerberos.kerb.server.KdcUtil;
-import org.apache.kerby.kerberos.kerb.transport.KrbNetwork;
-import org.apache.kerby.kerberos.kerb.transport.KrbTransport;
-import org.apache.kerby.kerberos.kerb.transport.TransportPair;
 import org.apache.kerby.util.OSUtil;
 import org.jline.reader.LineReader;
 import org.jline.reader.LineReaderBuilder;
@@ -51,23 +47,14 @@
 
 import javax.security.auth.Subject;
 import javax.security.auth.login.LoginException;
-import javax.security.sasl.Sasl;
-import javax.security.sasl.SaslClient;
-import javax.security.sasl.SaslException;
 import java.io.File;
 import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.security.PrivilegedAction;
-import java.util.HashMap;
-import java.util.Map;
 
 /**
  * Command use of remote admin
  */
 public class RemoteAdminClientTool {
     private static final Logger LOG = LoggerFactory.getLogger(RemoteAdminClientTool.class);
-    private static final byte[] EMPTY = new byte[0];
-    private static KrbTransport transport;
     private static final String PROMPT = RemoteAdminClientTool.class.getSimpleName() + ".remote";
     private static final String USAGE = (OSUtil.isWindows()
         ? "Usage: bin\\remote-admin-client.cmd" : "Usage: sh bin/remote-admin-client.sh")
@@ -140,21 +127,6 @@
         adminClient.init();
         System.out.println("admin init successful");
 
-        TransportPair tpair = null;
-        try {
-            tpair = AdminUtil.getTransportPair(adminClient.getSetting());
-        } catch (KrbException e) {
-            LOG.error("Fail to get transport pair. " + e);
-        }
-        KrbNetwork network = new KrbNetwork();
-        network.setSocketTimeout(adminClient.getSetting().getTimeout());
-
-        try {
-            transport = network.connect(tpair);
-        } catch (IOException e) {
-            throw new KrbException("Failed to create transport", e);
-        }
-
         String adminPrincipal = KrbUtil.makeKadminPrincipal(
             adminClient.getSetting().getKdcRealm()).getName();
         Subject subject = null;
@@ -163,60 +135,11 @@
                 new File(adminConfig.getKeyTabFile()));
         } catch (LoginException e) {
             LOG.error("Fail to login using keytab. " + e);
+            return;
         }
-        Subject.doAs(subject, new PrivilegedAction<Object>() {
-            @Override
-            public Object run() {
-                try {
 
-                    Map<String, String> props = new HashMap<>();
-                    props.put(Sasl.QOP, "auth-conf");
-                    props.put(Sasl.SERVER_AUTH, "true");
-                    SaslClient saslClient = null;
-                    try {
-                        String protocol = adminConfig.getProtocol();
-                        String serverName = adminConfig.getServerName();
-                        saslClient = Sasl.createSaslClient(new String[]{"GSSAPI"}, null,
-                            protocol, serverName, props, null);
-                    } catch (SaslException e) {
-                        LOG.error("Fail to create sasl client. " + e);
-                    }
-                    if (saslClient == null) {
-                        throw new KrbException("Unable to find client implementation for: GSSAPI");
-                    }
-                    byte[] response = new byte[0];
-                    try {
-                        response = saslClient.hasInitialResponse()
-                            ? saslClient.evaluateChallenge(EMPTY) : EMPTY;
-                    } catch (SaslException e) {
-                        LOG.error("Sasl client evaluate challenge failed." + e);
-                    }
-
-                    sendMessage(response, saslClient);
-
-                    ByteBuffer message = transport.receiveMessage();
-
-                    while (!saslClient.isComplete()) {
-                        int ssComplete = message.getInt();
-                        if (ssComplete == 0) {
-                            System.out.println("Sasl Server completed");
-                        }
-                        byte[] arr = new byte[message.remaining()];
-                        message.get(arr);
-                        byte[] challenge = saslClient.evaluateChallenge(arr);
-
-                        sendMessage(challenge, saslClient);
-
-                        if (!saslClient.isComplete()) {
-                            message = transport.receiveMessage();
-                        }
-                    }
-                } catch (Exception e) {
-                    LOG.error("Failed to run. " + e.toString());
-                }
-                return null;
-            }
-        });
+        // set login subject, used by SASL negotiation
+        adminClient.setSubject(subject);
 
         System.out.println("enter \"command\" to see legal commands.");
 
@@ -246,25 +169,6 @@
         }
     }
 
-    private static void sendMessage(byte[] challenge, SaslClient saslClient)
-        throws SaslException {
-
-        // 4 is the head to go through network
-        ByteBuffer buffer = ByteBuffer.allocate(challenge.length + 8);
-        buffer.putInt(challenge.length + 4);
-        int scComplete = saslClient.isComplete() ? 0 : 1;
-
-        buffer.putInt(scComplete);
-        buffer.put(challenge);
-        buffer.flip();
-
-        try {
-            transport.sendMessage(buffer);
-        } catch (IOException e) {
-            LOG.error("Failed to send Kerberos message. " + e.toString());
-        }
-    }
-
     private static void execute(AdminClient adminClient, String input) throws KrbException {
         input = input.trim();
         if (input.startsWith("command")) {
diff --git a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/AdminClient.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/AdminClient.java
index 1937c19..fa20b29 100644
--- a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/AdminClient.java
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/AdminClient.java
@@ -26,6 +26,7 @@
 import org.apache.kerby.kerberos.kerb.admin.kadmin.remote.impl.InternalAdminClient;
 import org.apache.kerby.kerberos.kerb.request.KrbIdentity;
 
+import javax.security.auth.Subject;
 import java.io.File;
 import java.util.List;
 
@@ -39,6 +40,7 @@
     private final AdminSetting adminSetting;
 
     private InternalAdminClient innerClient;
+    private Subject subject = null;
 
     /**
      * Default constructor.
@@ -171,53 +173,57 @@
         return adminConfig;
     }
 
+    public void setSubject(Subject subject) {
+        this.subject = subject;
+    }
+
     public void requestAddPrincipal(String principal) throws KrbException {
-        Kadmin remote = new RemoteKadminImpl(innerClient);
+        Kadmin remote = new RemoteKadminImpl(innerClient, subject);
         remote.addPrincipal(principal);
     }
 
     public void requestAddPrincipal(String principal, String password) throws KrbException {
-        Kadmin remote = new RemoteKadminImpl(innerClient);
+        Kadmin remote = new RemoteKadminImpl(innerClient, subject);
         remote.addPrincipal(principal, password);
     }
 
     public void requestDeletePrincipal(String principal) throws KrbException {
-        Kadmin remote = new RemoteKadminImpl(innerClient);
+        Kadmin remote = new RemoteKadminImpl(innerClient, subject);
         remote.deletePrincipal(principal);
     }
 
     public void requestRenamePrincipal(String oldPrincipal, String newPrincipal) throws KrbException {
-        Kadmin remote = new RemoteKadminImpl(innerClient);
+        Kadmin remote = new RemoteKadminImpl(innerClient, subject);
         remote.renamePrincipal(oldPrincipal, newPrincipal);
     }
 
     public List<String> requestGetprincs() throws KrbException {
-        Kadmin remote = new RemoteKadminImpl(innerClient);
+        Kadmin remote = new RemoteKadminImpl(innerClient, subject);
         return remote.getPrincipals();
     }
 
     public List<String> requestGetprincsWithExp(String exp) throws KrbException {
-        Kadmin remote = new RemoteKadminImpl(innerClient);
+        Kadmin remote = new RemoteKadminImpl(innerClient, subject);
         return remote.getPrincipals(exp);
     }
     
     public void requestExportKeytab(File keytabFile, String principal) throws KrbException {
-        Kadmin remote = new RemoteKadminImpl(innerClient);
+        Kadmin remote = new RemoteKadminImpl(innerClient, subject);
         remote.exportKeytab(keytabFile, principal);
     }
 
     public void requestExportKeytab(File keytabFile, List<String> principals) throws KrbException {
-        Kadmin remote = new RemoteKadminImpl(innerClient);
+        Kadmin remote = new RemoteKadminImpl(innerClient, subject);
         remote.exportKeytab(keytabFile, principals);
     }
 
     public void requestChangePassword(String principal, String newPassword) throws KrbException {
-        Kadmin remote = new RemoteKadminImpl(innerClient);
+        Kadmin remote = new RemoteKadminImpl(innerClient, subject);
         remote.changePassword(principal, newPassword);
     }
 
     public KrbIdentity requestGetPrincipal(String principal) throws KrbException {
-        RemoteKadminImpl remote = new RemoteKadminImpl(innerClient);
+        RemoteKadminImpl remote = new RemoteKadminImpl(innerClient, subject);
         return remote.getPrincipal(principal);
     }
 }
diff --git a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/AdminHandler.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/AdminHandler.java
index 37f7980..6640945 100644
--- a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/AdminHandler.java
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/AdminHandler.java
@@ -33,6 +33,7 @@
 import org.apache.kerby.kerberos.kerb.type.base.EncryptionType;
 import org.apache.kerby.xdr.XdrFieldInfo;
 import org.apache.kerby.xdr.type.XdrStructType;
+import org.xnio.sasl.SaslWrapper;
 
 import java.io.IOException;
 import java.nio.ByteBuffer;
@@ -54,15 +55,16 @@
      * Handle the kdc request.
      *
      * @param adminRequest The admin request
+     * @param sasl The SASL client wrapper
      * @throws KrbException e
      */
-    public void handleRequest(AdminRequest adminRequest) throws KrbException {
+    public void handleRequest(AdminRequest adminRequest, SaslWrapper sasl) throws KrbException {
         adminRequest.process();
         AdminReq adminReq = adminRequest.getAdminReq();
-        ByteBuffer requestMessage = KadminCode.encodeMessage(adminReq);
-        requestMessage.flip();
 
         try {
+            ByteBuffer requestMessage = KadminCode.encodeWrapMessage(adminReq, sasl);
+            requestMessage.flip();
             sendMessage(adminRequest, requestMessage);
         } catch (IOException e) {
             throw new KrbException("Admin sends request message failed", e);
@@ -240,9 +242,12 @@
     protected abstract void sendMessage(AdminRequest adminRequest,
                                         ByteBuffer requestMessage) throws IOException;
 
-    protected abstract List<String> handleRequestForList(AdminRequest adminRequest) throws KrbException;
-    
-    protected abstract byte[] handleRequestForBytes(AdminRequest adminRequest) throws KrbException;
+    protected abstract List<String> handleRequestForList(AdminRequest adminRequest,
+                                                         SaslWrapper sasl) throws KrbException;
 
-    protected abstract KrbIdentity handleRequestForIdentity(AdminRequest adminRequest) throws KrbException;
+    protected abstract byte[] handleRequestForBytes(AdminRequest adminRequest,
+                                                    SaslWrapper sasl) throws KrbException;
+
+    protected abstract KrbIdentity handleRequestForIdentity(AdminRequest adminRequest,
+                                                            SaslWrapper sasl) throws KrbException;
 }
diff --git a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/NegotiationStatus.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/NegotiationStatus.java
new file mode 100644
index 0000000..3eae226
--- /dev/null
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/NegotiationStatus.java
@@ -0,0 +1,40 @@
+/**
+ *  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.kerby.kerberos.kerb.admin.kadmin.remote;
+
+import org.apache.kerby.xdr.EnumType;
+public enum  NegotiationStatus implements EnumType {
+    SUCCESS(0),
+    CONTINUE(1),
+    ERROR(2);
+
+    private final int value;
+    NegotiationStatus(int value) {
+        this.value = value;
+    }
+    @Override
+    public int getValue() {
+        return value;
+    }
+    @Override
+    public String getName() {
+        return name();
+    }
+}
diff --git a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/RemoteKadminImpl.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/RemoteKadminImpl.java
index e12a913..66517df 100644
--- a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/RemoteKadminImpl.java
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/RemoteKadminImpl.java
@@ -33,6 +33,7 @@
 import org.apache.kerby.kerberos.kerb.admin.kadmin.remote.request.GetprincsRequest;
 import org.apache.kerby.kerberos.kerb.admin.kadmin.remote.request.ChangePasswordRequest;
 import org.apache.kerby.kerberos.kerb.admin.kadmin.remote.request.GetPrincipalRequest;
+import org.apache.kerby.kerberos.kerb.admin.message.KadminCode;
 import org.apache.kerby.kerberos.kerb.common.KrbUtil;
 import org.apache.kerby.kerberos.kerb.keytab.Keytab;
 import org.apache.kerby.kerberos.kerb.request.KrbIdentity;
@@ -40,14 +41,24 @@
 import org.apache.kerby.kerberos.kerb.transport.KrbTransport;
 import org.apache.kerby.kerberos.kerb.transport.TransportPair;
 import org.apache.kerby.kerberos.kerb.type.base.PrincipalName;
+import org.xnio.sasl.SaslUtils;
+import org.xnio.sasl.SaslWrapper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import javax.security.auth.Subject;
+import javax.security.sasl.Sasl;
+import javax.security.sasl.SaslClient;
+import javax.security.sasl.SaslException;
 import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.security.PrivilegedExceptionAction;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 /**
  * Server side admin facilities from remote, similar to MIT Kadmin remote mode.
@@ -60,11 +71,17 @@
  */
 public class RemoteKadminImpl implements Kadmin {
     private static final Logger LOG = LoggerFactory.getLogger(RemoteKadminImpl.class);
+    private static final String MECHANISM = "GSSAPI";
+    private static final byte[] EMPTY_BYTES = new byte[0];
     private InternalAdminClient innerClient;
     private KrbTransport transport;
+    private SaslClient saslClient = null;
+    private SaslWrapper saslClientWrapper = null;
+    private final Subject subject;
 
-    public RemoteKadminImpl(InternalAdminClient innerClient) throws KrbException {
+    public RemoteKadminImpl(InternalAdminClient innerClient, Subject subject) throws KrbException {
         this.innerClient = innerClient;
+        this.subject = subject;
         TransportPair tpair = null;
         try {
             tpair = AdminUtil.getTransportPair(innerClient.getSetting());
@@ -78,6 +95,11 @@
         } catch (IOException e) {
             throw new KrbException("Failed to create transport", e);
         }
+        try {
+            doSaslHandshake();
+        } catch (Exception e) {
+            throw new KrbException("Failed to do SASL handshake. " + e);
+        }
     }
 
     public InternalAdminClient getInnerClient() {
@@ -97,7 +119,7 @@
         adRequest.setTransport(transport);
         //handle it
         AdminHandler adminHandler = new DefaultAdminHandler();
-        adminHandler.handleRequest(adRequest);
+        adminHandler.handleRequest(adRequest, saslClientWrapper);
 
     }
 
@@ -108,7 +130,7 @@
         //wrap buffer problem
         adRequest.setTransport(transport);
         AdminHandler adminHandler = new DefaultAdminHandler();
-        adminHandler.handleRequest(adRequest);
+        adminHandler.handleRequest(adRequest, saslClientWrapper);
     }
 
     @Override
@@ -117,7 +139,7 @@
         AdminRequest addPrincipalRequest = new AddPrincipalRequest(principal, password);
         addPrincipalRequest.setTransport(transport);
         AdminHandler adminHandler = new DefaultAdminHandler();
-        adminHandler.handleRequest(addPrincipalRequest);
+        adminHandler.handleRequest(addPrincipalRequest, saslClientWrapper);
     }
 
     @Override
@@ -139,7 +161,7 @@
         AdminRequest exportKeytabRequest = new ExportKeytabRequest(principalsStr);
         exportKeytabRequest.setTransport(transport);
         AdminHandler adminHandler = new DefaultAdminHandler();
-        byte[] keytabFileBytes = adminHandler.handleRequestForBytes(exportKeytabRequest);
+        byte[] keytabFileBytes = adminHandler.handleRequestForBytes(exportKeytabRequest, saslClientWrapper);
         
         Keytab keytab = AdminHelper.loadKeytab(new ByteArrayInputStream(keytabFileBytes));
         Keytab outputKeytab = AdminHelper.createOrLoadKeytab(keytabFile);
@@ -179,7 +201,7 @@
         AdminRequest deletePrincipalRequest = new DeletePrincipalRequest(principal);
         deletePrincipalRequest.setTransport(transport);
         AdminHandler adminHandler = new DefaultAdminHandler();
-        adminHandler.handleRequest(deletePrincipalRequest);
+        adminHandler.handleRequest(deletePrincipalRequest, saslClientWrapper);
     }
 
     @Override
@@ -194,7 +216,7 @@
         AdminRequest renamePrincipalRequest =  new RenamePrincipalRequest(oldPrincipalName, newPrincipalName);
         renamePrincipalRequest.setTransport(transport);
         AdminHandler adminHandler = new DefaultAdminHandler();
-        adminHandler.handleRequest(renamePrincipalRequest);
+        adminHandler.handleRequest(renamePrincipalRequest, saslClientWrapper);
     }
 
     @Override
@@ -202,7 +224,7 @@
         AdminRequest getPrincsRequest = new GetprincsRequest();
         getPrincsRequest.setTransport(transport);
         AdminHandler adminHandler = new DefaultAdminHandler();
-        return adminHandler.handleRequestForList(getPrincsRequest);
+        return adminHandler.handleRequestForList(getPrincsRequest, saslClientWrapper);
     }
 
     @Override
@@ -210,7 +232,7 @@
         AdminRequest getPrincsRequest = new GetprincsRequest(globString);
         getPrincsRequest.setTransport(transport);
         AdminHandler adminHandler = new DefaultAdminHandler();
-        return adminHandler.handleRequestForList(getPrincsRequest);
+        return adminHandler.handleRequestForList(getPrincsRequest, saslClientWrapper);
     }
 
     @Override
@@ -219,7 +241,7 @@
         AdminRequest changePwdRequest = new ChangePasswordRequest(principal, newPassword);
         changePwdRequest.setTransport(transport);
         AdminHandler adminHandler = new DefaultAdminHandler();
-        adminHandler.handleRequest(changePwdRequest);
+        adminHandler.handleRequest(changePwdRequest, saslClientWrapper);
     }
 
     @Override
@@ -236,7 +258,7 @@
         AdminRequest getPrincipalRequest = new GetPrincipalRequest(principalName);
         getPrincipalRequest.setTransport(transport);
         AdminHandler adminHandler = new DefaultAdminHandler();
-        return adminHandler.handleRequestForIdentity(getPrincipalRequest);
+        return adminHandler.handleRequestForIdentity(getPrincipalRequest, saslClientWrapper);
     }
 
     private String listToString(List<String> list) {
@@ -250,4 +272,64 @@
         }
         return result.toString();
     }
+
+    private void doSaslHandshake() throws Exception {
+        Subject.doAs(subject, (PrivilegedExceptionAction<Object>) () -> {
+            boolean success = false;
+            try {
+                Map<String, String> saslProps = new HashMap<>();
+                saslProps.put(Sasl.QOP, "auth-conf");
+                saslProps.put(Sasl.SERVER_AUTH, "true");
+                try {
+                    String protocol = innerClient.getSetting().getAdminConfig().getProtocol();
+                    String serverName = innerClient.getSetting().getAdminConfig().getServerName();
+                    saslClient = Sasl.createSaslClient(new String[]{MECHANISM}, null,
+                            protocol, serverName, saslProps, null);
+                    this.saslClientWrapper = SaslWrapper.create(saslClient);
+                } catch (SaslException e) {
+                    throw new KrbException("Fail to create SASL client. " + e);
+                }
+                if (saslClient == null) {
+                    throw new KrbException("Unable to find client implementation for: GSSAPI");
+                }
+                byte[] response;
+                try {
+                    response = saslClient.hasInitialResponse()
+                            ? saslClient.evaluateChallenge(EMPTY_BYTES) : EMPTY_BYTES;
+                } catch (SaslException e) {
+                    throw new KrbException("Sasl client evaluate challenge failed." + e);
+                }
+                sendSaslMessage(response);
+                ByteBuffer message = transport.receiveMessage();
+
+                while (!saslClient.isComplete()) {
+                    int ssComplete = message.getInt();
+                    if (ssComplete == NegotiationStatus.SUCCESS.getValue()) {
+                        LOG.info("Sasl Server completed");
+                    }
+                    sendSaslMessage(SaslUtils.evaluateChallenge(saslClient, message));
+                    if (!saslClient.isComplete()) {
+                        message = transport.receiveMessage();
+                    }
+
+                }
+                success = true;
+            } finally {
+                if (!success) {
+                    transport.release();
+                }
+            }
+            return null;
+        });
+    }
+    private void sendSaslMessage(byte[] response) {
+        NegotiationStatus status = saslClient.isComplete()
+                ? NegotiationStatus.SUCCESS : NegotiationStatus.CONTINUE;
+        ByteBuffer buffer = KadminCode.encodeSaslMessage(response, status);
+        try {
+            transport.sendMessage(buffer);
+        } catch (IOException e) {
+            LOG.error("Failed to send message to server. ", e);
+        }
+    }
 }
diff --git a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/impl/DefaultAdminHandler.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/impl/DefaultAdminHandler.java
index 03e3e93..45321d8 100644
--- a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/impl/DefaultAdminHandler.java
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/impl/DefaultAdminHandler.java
@@ -24,6 +24,7 @@
 import org.apache.kerby.kerberos.kerb.admin.kadmin.remote.request.AdminRequest;
 import org.apache.kerby.kerberos.kerb.request.KrbIdentity;
 import org.apache.kerby.kerberos.kerb.transport.KrbTransport;
+import org.xnio.sasl.SaslWrapper;
 
 import java.io.IOException;
 import java.nio.ByteBuffer;
@@ -36,18 +37,18 @@
      * and use this to receive message.
      */
     @Override
-    public void handleRequest(AdminRequest adminRequest) throws KrbException {
-        /**super is used to send message*/
-        super.handleRequest(adminRequest);
+    public void handleRequest(AdminRequest adminRequest, SaslWrapper sasl) throws KrbException {
+        // super is used to send message
+        super.handleRequest(adminRequest, sasl);
 
         KrbTransport transport = adminRequest.getTransport();
-        ByteBuffer receiveMessage = null;
         try {
-            receiveMessage = transport.receiveMessage();
+            ByteBuffer receiveMessage = transport.receiveMessage();
+            ByteBuffer unwrapMessage = ByteBuffer.wrap(sasl.unwrap(receiveMessage));
+            super.onResponseMessage(adminRequest, unwrapMessage);
         } catch (IOException e) {
             throw new KrbException("Admin receives response message failed", e);
         }
-        super.onResponseMessage(adminRequest, receiveMessage);
     }
 
     /**
@@ -61,16 +62,17 @@
     }
 
     @Override
-    public List<String> handleRequestForList(AdminRequest adminRequest) throws KrbException {
-        /**send message*/
-        super.handleRequest(adminRequest);
+    public List<String> handleRequestForList(AdminRequest adminRequest,
+                                             SaslWrapper sasl) throws KrbException {
+        // send message
+        super.handleRequest(adminRequest, sasl);
 
         KrbTransport transport = adminRequest.getTransport();
-        ByteBuffer receiveMessage = null;
-        List<String> prinicalList = null;
+        List<String> prinicalList;
         try {
-            receiveMessage = transport.receiveMessage();
-            prinicalList = super.onResponseMessageForList(adminRequest, receiveMessage);
+            ByteBuffer receiveMessage = transport.receiveMessage();
+            ByteBuffer unwrapMessage = ByteBuffer.wrap(sasl.unwrap(receiveMessage));
+            prinicalList = super.onResponseMessageForList(adminRequest, unwrapMessage);
         } catch (IOException e) {
             throw new KrbException("Admin receives response message failed", e);
         }
@@ -79,15 +81,16 @@
     }
 
     @Override
-    protected byte[] handleRequestForBytes(AdminRequest adminRequest) throws KrbException {
-        super.handleRequest(adminRequest);
+    protected byte[] handleRequestForBytes(AdminRequest adminRequest,
+                                           SaslWrapper sasl) throws KrbException {
+        super.handleRequest(adminRequest, sasl);
         
         KrbTransport transport = adminRequest.getTransport();
-        ByteBuffer receiveMessage = null;
         byte[] keytabFileBytes;
         try {
-            receiveMessage = transport.receiveMessage();
-            keytabFileBytes = super.onResponseMessageForBytesArray(adminRequest, receiveMessage);
+            ByteBuffer receiveMessage = transport.receiveMessage();
+            ByteBuffer unwrapMessage = ByteBuffer.wrap(sasl.unwrap(receiveMessage));
+            keytabFileBytes = super.onResponseMessageForBytesArray(adminRequest, unwrapMessage);
         } catch (IOException e) {
             throw new KrbException("Admin receives response message failed", e);
         }
@@ -95,15 +98,16 @@
     }
 
     @Override
-    protected KrbIdentity handleRequestForIdentity(AdminRequest adminRequest) throws KrbException {
-        super.handleRequest(adminRequest);
+    protected KrbIdentity handleRequestForIdentity(AdminRequest adminRequest,
+                                                   SaslWrapper sasl) throws KrbException {
+        super.handleRequest(adminRequest, sasl);
 
         KrbTransport transport = adminRequest.getTransport();
-        ByteBuffer receiveMessage = null;
         KrbIdentity identity;
         try {
-            receiveMessage = transport.receiveMessage();
-            identity = super.onResponseMessageForIdentity(adminRequest, receiveMessage);
+            ByteBuffer receiveMessage = transport.receiveMessage();
+            ByteBuffer unwrapMessage = ByteBuffer.wrap(sasl.unwrap(receiveMessage));
+            identity = super.onResponseMessageForIdentity(adminRequest, unwrapMessage);
         } catch (IOException e) {
             throw new KrbException("Admin receives response message failed", e);
         }
diff --git a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/request/GetprincsRequest.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/request/GetprincsRequest.java
index 2794010..9c082c8 100644
--- a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/request/GetprincsRequest.java
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/request/GetprincsRequest.java
@@ -52,7 +52,7 @@
 
         XdrFieldInfo[] xdrFieldInfos = new XdrFieldInfo[3];
         xdrFieldInfos[0] = new XdrFieldInfo(0, XdrDataType.ENUM, AdminMessageType.GET_PRINCS_REQ);
-        xdrFieldInfos[1] = new XdrFieldInfo(1, XdrDataType.INTEGER, 2);
+        xdrFieldInfos[1] = new XdrFieldInfo(1, XdrDataType.INTEGER, 1);
         xdrFieldInfos[2] = new XdrFieldInfo(2, XdrDataType.STRING, globString);
 
         AdminMessageCode value = new AdminMessageCode(xdrFieldInfos);
diff --git a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/message/KadminCode.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/message/KadminCode.java
index c5d6359..84db194 100644
--- a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/message/KadminCode.java
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/message/KadminCode.java
@@ -19,6 +19,11 @@
  */
 package org.apache.kerby.kerberos.kerb.admin.message;
 
+
+import org.apache.kerby.kerberos.kerb.admin.kadmin.remote.NegotiationStatus;
+import org.xnio.sasl.SaslWrapper;
+
+import javax.security.sasl.SaslException;
 import java.io.IOException;
 import java.nio.ByteBuffer;
 
@@ -37,6 +42,31 @@
         buffer.flip();
         return buffer;
     }
+    
+    public static ByteBuffer encodeSaslMessage(byte[] bytes, NegotiationStatus status) {
+        // NegotiationStatus occupies 4 bytes
+        int length = bytes.length + 4;
+        // 4 is the head to go through network
+        ByteBuffer buffer = ByteBuffer.allocate(length + 4);
+        buffer.putInt(length);
+        buffer.putInt(status.getValue());
+        buffer.put(bytes);
+        buffer.flip();
+        return buffer;
+    }
+
+    public static ByteBuffer encodeWrapMessage(AdminMessage adminMessage,
+                                               SaslWrapper sasl) throws SaslException {
+        // encode the data to be sent to the peer, in order to use SASL negotiated layer
+        byte[] wrapBytes = sasl.wrap(adminMessage.getMessageBuffer());
+        int length = wrapBytes.length;
+        // 4 is the head to go through network
+        ByteBuffer buffer = ByteBuffer.allocate(length + 4);
+        buffer.putInt(length); // head in network
+        buffer.put(wrapBytes);
+        buffer.flip();
+        return buffer;
+    }
 
     public static AdminMessage decodeMessage(ByteBuffer buffer) throws IOException {
         //go through network, the total length has been removed.
diff --git a/kerby-kerb/kerb-common/pom.xml b/kerby-kerb/kerb-common/pom.xml
index e4c4a19..d9de7c9 100644
--- a/kerby-kerb/kerb-common/pom.xml
+++ b/kerby-kerb/kerb-common/pom.xml
@@ -50,5 +50,10 @@
       <groupId>org.assertj</groupId>
       <artifactId>assertj-core</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.jboss.xnio</groupId>
+      <artifactId>xnio-api</artifactId>
+      <version>${xnio-api.version}</version>
+    </dependency>
   </dependencies>
 </project>
diff --git a/kerby-kerb/kerb-common/src/main/java/org/apache/kerby/kerberos/kerb/transport/KdcNetwork.java b/kerby-kerb/kerb-common/src/main/java/org/apache/kerby/kerberos/kerb/transport/KdcNetwork.java
index b40fa04..2965e81 100644
--- a/kerby-kerb/kerb-common/src/main/java/org/apache/kerby/kerberos/kerb/transport/KdcNetwork.java
+++ b/kerby-kerb/kerb-common/src/main/java/org/apache/kerby/kerberos/kerb/transport/KdcNetwork.java
@@ -162,5 +162,11 @@
         }
     }
 
+    public boolean isStopped() {
+        synchronized (this) {
+            return isStopped;
+        }
+    }
+
     protected abstract void onNewTransport(KrbTransport transport);
 }
diff --git a/kerby-kerb/kerb-common/src/main/java/org/apache/kerby/kerberos/kerb/transport/KrbTcpTransport.java b/kerby-kerb/kerb-common/src/main/java/org/apache/kerby/kerberos/kerb/transport/KrbTcpTransport.java
index 50f8306..02cb877 100644
--- a/kerby-kerb/kerb-common/src/main/java/org/apache/kerby/kerberos/kerb/transport/KrbTcpTransport.java
+++ b/kerby-kerb/kerb-common/src/main/java/org/apache/kerby/kerberos/kerb/transport/KrbTcpTransport.java
@@ -81,4 +81,8 @@
             // System.err.println(e); // NOOP
         }
     }
+
+    public boolean isClosed() {
+        return socket.isClosed();
+    }
 }
diff --git a/pom.xml b/pom.xml
index 1a264d9..37e1dce 100644
--- a/pom.xml
+++ b/pom.xml
@@ -79,6 +79,7 @@
     <ini4j.version>0.5.4</ini4j.version>
     <targetJdk.version>1.8</targetJdk.version>
     <zookeeper.version>3.4.14</zookeeper.version>
+    <xnio-api.version>3.6.5.Final</xnio-api.version>
   </properties>
 
   <prerequisites>