You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by lg...@apache.org on 2015/11/26 06:33:04 UTC

[03/10] mina-sshd git commit: [SSHD-599] Allow per-session override of KEX and authentication related configuration

[SSHD-599] Allow per-session override of KEX and authentication related configuration


Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/e9dd7f47
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/e9dd7f47
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/e9dd7f47

Branch: refs/heads/master
Commit: e9dd7f4757556bc5e4464647b259fe8a6cdd0b24
Parents: f839083
Author: Lyor Goldstein <lg...@vmware.com>
Authored: Thu Nov 26 07:19:03 2015 +0200
Committer: Lyor Goldstein <lg...@vmware.com>
Committed: Thu Nov 26 07:19:03 2015 +0200

----------------------------------------------------------------------
 .../client/ClientAuthenticationManager.java     |  76 ++++
 .../sshd/client/ClientFactoryManager.java       |  59 +--
 .../java/org/apache/sshd/client/SshClient.java  | 107 ++++--
 .../auth/UserAuthKeyboardInteractive.java       |  12 +-
 .../sshd/client/auth/UserAuthPassword.java      |   8 +-
 .../sshd/client/auth/UserAuthPublicKey.java     |  43 ++-
 .../sshd/client/auth/UserInteraction.java       |  73 ++--
 .../client/auth/pubkey/KeyPairIdentity.java     |   6 +-
 .../keys/BuiltinClientIdentitiesWatcher.java    |  14 +
 .../config/keys/ClientIdentitiesWatcher.java    |  32 +-
 .../config/keys/ClientIdentityFileWatcher.java  |  30 +-
 .../keys/DefaultClientIdentitiesWatcher.java    |  19 +
 .../org/apache/sshd/client/kex/DHGClient.java   |   4 +-
 .../org/apache/sshd/client/kex/DHGEXClient.java |   4 +-
 .../client/session/AbstractClientSession.java   | 378 +++++++++++++++++++
 .../sshd/client/session/ClientSession.java      |  19 +-
 .../sshd/client/session/ClientSessionImpl.java  | 337 +----------------
 .../client/session/ClientUserAuthService.java   |  57 ++-
 .../sshd/common/AbstractFactoryManager.java     |  73 +---
 .../org/apache/sshd/common/FactoryManager.java  |  43 +--
 .../auth/AbstractUserAuthMethodFactory.java     |   5 +
 .../common/kex/AbstractKexFactoryManager.java   | 137 +++++++
 .../sshd/common/kex/KexFactoryManager.java      |  75 ++++
 .../keyprovider/KeyPairProviderHolder.java      |   1 +
 .../sshd/common/session/AbstractSession.java    |  49 ++-
 .../common/session/AbstractSessionFactory.java  |   7 -
 .../org/apache/sshd/common/session/Session.java |   4 +-
 .../apache/sshd/common/util/GenericUtils.java   |  20 +
 .../org/apache/sshd/common/util/Supplier.java   |  29 ++
 .../server/ServerAuthenticationManager.java     | 166 ++++++++
 .../sshd/server/ServerFactoryManager.java       |  68 +---
 .../java/org/apache/sshd/server/SshServer.java  |  58 +--
 .../auth/UserAuthKeyboardInteractive.java       |   7 +-
 .../sshd/server/auth/UserAuthPassword.java      |   6 +-
 .../sshd/server/auth/UserAuthPublicKey.java     |  12 +-
 .../sshd/server/auth/gss/UserAuthGSS.java       |  32 +-
 ...DefaultKeyboardInteractiveAuthenticator.java |   8 +-
 .../KeyboardInteractiveAuthenticator.java       |  20 +
 .../org/apache/sshd/server/kex/DHGEXServer.java |   3 +-
 .../org/apache/sshd/server/kex/DHGServer.java   |   4 +-
 .../server/session/AbstractServerSession.java   | 107 ++++++
 .../sshd/server/session/ServerSession.java      |   3 +-
 .../sshd/server/session/ServerSessionImpl.java  |  33 +-
 .../server/session/ServerUserAuthService.java   |  43 ++-
 .../java/org/apache/sshd/WelcomeBannerTest.java |   5 +
 .../sshd/client/ClientSessionListenerTest.java  | 188 +++++++++
 .../java/org/apache/sshd/client/ClientTest.java |  21 +-
 .../hosts/HostConfigEntryResolverTest.java      |   7 +-
 .../client/session/ClientSessionImplTest.java   |  40 ++
 .../sshd/common/auth/AuthenticationTest.java    |  15 +
 .../deprecated/ClientUserAuthServiceOld.java    |   6 +-
 .../deprecated/UserAuthKeyboardInteractive.java |   4 +-
 .../sshd/deprecated/UserAuthPublicKey.java      |   9 +-
 .../sshd/server/ServerSessionListenerTest.java  | 259 +++++++++++++
 .../java/org/apache/sshd/server/ServerTest.java |  72 +---
 55 files changed, 1988 insertions(+), 929 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e9dd7f47/sshd-core/src/main/java/org/apache/sshd/client/ClientAuthenticationManager.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/ClientAuthenticationManager.java b/sshd-core/src/main/java/org/apache/sshd/client/ClientAuthenticationManager.java
new file mode 100644
index 0000000..de41b9a
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/client/ClientAuthenticationManager.java
@@ -0,0 +1,76 @@
+/*
+ * 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.sshd.client;
+
+import java.util.List;
+
+import org.apache.sshd.client.auth.UserAuth;
+import org.apache.sshd.client.auth.UserInteraction;
+import org.apache.sshd.client.keyverifier.ServerKeyVerifier;
+import org.apache.sshd.common.NamedFactory;
+
+/**
+ * Holds information required for the client to perform authentication with the server
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface ClientAuthenticationManager {
+
+    /**
+     * Ordered comma separated list of authentications methods.
+     * Authentications methods accepted by the server will be tried in the given order.
+     * If not configured or {@code null}/empty, then the session's {@link #getUserAuthFactories()}
+     * is used as-is
+     */
+    String PREFERRED_AUTHS = "preferred-auths";
+
+    /**
+     * Specifies the number of interactive prompts before giving up.
+     * The argument to this keyword must be an integer.
+     */
+    String PASSWORD_PROMPTS = "password-prompts";
+
+    /**
+     * Default value for {@link #PASSWORD_PROMPTS} if none configured
+     */
+    int DEFAULT_PASSWORD_PROMPTS = 3;
+
+    /**
+     * Retrieve the server key verifier to be used to check the key when connecting
+     * to an SSH server.
+     *
+     * @return the {@link ServerKeyVerifier} to use - never {@code null}
+     */
+    ServerKeyVerifier getServerKeyVerifier();
+    void setServerKeyVerifier(ServerKeyVerifier serverKeyVerifier);
+
+    /**
+     * @return A {@link UserInteraction} object to communicate with the user
+     * (may be {@code null} to indicate that no such communication is allowed)
+     */
+    UserInteraction getUserInteraction();
+    void setUserInteraction(UserInteraction userInteraction);
+
+    /**
+     * @return a {@link List} of {@link UserAuth} {@link NamedFactory}-ies - never
+     * {@code null}/empty
+     */
+    List<NamedFactory<UserAuth>> getUserAuthFactories();
+    void setUserAuthFactories(List<NamedFactory<UserAuth>> userAuthFactories);
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e9dd7f47/sshd-core/src/main/java/org/apache/sshd/client/ClientFactoryManager.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/ClientFactoryManager.java b/sshd-core/src/main/java/org/apache/sshd/client/ClientFactoryManager.java
index 11000d2..c9337e5 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/ClientFactoryManager.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/ClientFactoryManager.java
@@ -18,15 +18,9 @@
  */
 package org.apache.sshd.client;
 
-import java.util.List;
-
-import org.apache.sshd.client.auth.UserAuth;
-import org.apache.sshd.client.auth.UserInteraction;
 import org.apache.sshd.client.config.hosts.HostConfigEntryResolver;
 import org.apache.sshd.client.config.keys.ClientIdentityLoader;
-import org.apache.sshd.client.keyverifier.ServerKeyVerifier;
 import org.apache.sshd.common.FactoryManager;
-import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.config.keys.FilePasswordProvider;
 
 /**
@@ -35,12 +29,12 @@ import org.apache.sshd.common.config.keys.FilePasswordProvider;
  *
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
-public interface ClientFactoryManager extends FactoryManager {
-
+public interface ClientFactoryManager extends FactoryManager, ClientAuthenticationManager {
     /**
      * Key used to set the heartbeat interval in milliseconds (0 to disable = default)
      */
     String HEARTBEAT_INTERVAL = "hearbeat-interval";
+
     /**
      * Default value for {@link #HEARTBEAT_INTERVAL} if none configured
      */
@@ -50,30 +44,13 @@ public interface ClientFactoryManager extends FactoryManager {
      * Key used to check the heartbeat request that should be sent to the server
      */
     String HEARTBEAT_REQUEST = "heartbeat-request";
+
     /**
      * Default value for {@link ClientFactoryManager#HEARTBEAT_REQUEST} is none configured
      */
     String DEFAULT_KEEP_ALIVE_HEARTBEAT_STRING = "keepalive@sshd.apache.org";
 
     /**
-     * Ordered comma separated list of authentications methods.
-     * Authentications methods accepted by the server will be tried in the given order.
-     * If not configured or {@code null}/empty, then the session's {@link #getUserAuthFactories()}
-     * is used as-is
-     */
-    String PREFERRED_AUTHS = "preferred-auths";
-
-    /**
-     * Specifies the number of interactive prompts before giving up.
-     * The argument to this keyword must be an integer.
-     */
-    String PASSWORD_PROMPTS = "password-prompts";
-    /**
-     * Default value for {@link #PASSWORD_PROMPTS} if none configured
-     */
-    int DEFAULT_PASSWORD_PROMPTS = 3;
-
-    /**
      * Whether to ignore invalid identities files when pre-initializing
      * the client session
      * @see ClientIdentityLoader#isValidLocation(String)
@@ -86,40 +63,24 @@ public interface ClientFactoryManager extends FactoryManager {
     boolean DEFAULT_IGNORE_INVALID_IDENTITIES = true;
 
     /**
-     * Retrieve the server key verifier to be used to check the key when connecting
-     * to an ssh server.
-     *
-     * @return the server key verifier to use
-     */
-    ServerKeyVerifier getServerKeyVerifier();
-
-    /**
-     * @return A {@link UserInteraction} object to communicate with the user
-     * (may be {@code null} to indicate that no such communication is allowed)
-     */
-    UserInteraction getUserInteraction();
-
-    /**
-     * @return a {@link List} of {@link UserAuth} {@link NamedFactory}-ies - never
-     * {@code null}/empty
-     */
-    List<NamedFactory<UserAuth>> getUserAuthFactories();
-
-    /**
      * @return The {@link HostConfigEntryResolver} to use in order to resolve the
-     * effective session parameters
+     * effective session parameters - never {@code null}
      */
     HostConfigEntryResolver getHostConfigEntryResolver();
+    void setHostConfigEntryResolver(HostConfigEntryResolver resolver);
 
     /**
      * @return The {@link ClientIdentityLoader} to use in order to load client
-     * key pair identities
+     * key pair identities - never {@code null}
      */
     ClientIdentityLoader getClientIdentityLoader();
+    void setClientIdentityLoader(ClientIdentityLoader loader);
 
     /**
      * @return The {@link FilePasswordProvider} to use if need to load encrypted
-     * identities keys
+     * identities keys - never {@code null}
+     * @see FilePasswordProvider#EMPTY
      */
     FilePasswordProvider getFilePasswordProvider();
+    void setFilePasswordProvider(FilePasswordProvider provider);
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e9dd7f47/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java b/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java
index fdb4dbb..02f7db2 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java
@@ -66,7 +66,6 @@ import org.apache.sshd.client.keyverifier.ServerKeyVerifier;
 import org.apache.sshd.client.session.ClientConnectionServiceFactory;
 import org.apache.sshd.client.session.ClientSession;
 import org.apache.sshd.client.session.ClientSessionCreator;
-import org.apache.sshd.client.session.ClientSessionImpl;
 import org.apache.sshd.client.session.ClientUserAuthServiceFactory;
 import org.apache.sshd.client.session.SessionFactory;
 import org.apache.sshd.client.simple.AbstractSimpleClientSessionCreator;
@@ -85,12 +84,14 @@ import org.apache.sshd.common.config.keys.KeyUtils;
 import org.apache.sshd.common.future.SshFutureListener;
 import org.apache.sshd.common.io.IoConnectFuture;
 import org.apache.sshd.common.io.IoConnector;
+import org.apache.sshd.common.io.IoSession;
 import org.apache.sshd.common.keyprovider.AbstractFileKeyPairProvider;
 import org.apache.sshd.common.keyprovider.KeyPairProvider;
 import org.apache.sshd.common.session.AbstractSession;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.OsUtils;
 import org.apache.sshd.common.util.SecurityUtils;
+import org.apache.sshd.common.util.Supplier;
 import org.apache.sshd.common.util.ValidateUtils;
 import org.apache.sshd.common.util.io.IoUtils;
 import org.apache.sshd.common.util.io.NoCloseInputStream;
@@ -194,8 +195,9 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
         return serverKeyVerifier;
     }
 
+    @Override
     public void setServerKeyVerifier(ServerKeyVerifier serverKeyVerifier) {
-        this.serverKeyVerifier = serverKeyVerifier;
+        this.serverKeyVerifier = ValidateUtils.checkNotNull(serverKeyVerifier, "No server key verifier");
     }
 
     @Override
@@ -203,8 +205,9 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
         return hostConfigEntryResolver;
     }
 
+    @Override
     public void setHostConfigEntryResolver(HostConfigEntryResolver resolver) {
-        this.hostConfigEntryResolver = resolver;
+        this.hostConfigEntryResolver = ValidateUtils.checkNotNull(resolver, "No host configuration entry resolver");
     }
 
     @Override
@@ -212,8 +215,9 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
         return filePasswordProvider;
     }
 
+    @Override
     public void setFilePasswordProvider(FilePasswordProvider provider) {
-        this.filePasswordProvider = provider;
+        this.filePasswordProvider = ValidateUtils.checkNotNull(provider, "No file password provider");
     }
 
     @Override
@@ -221,8 +225,9 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
         return clientIdentityLoader;
     }
 
+    @Override
     public void setClientIdentityLoader(ClientIdentityLoader loader) {
-        this.clientIdentityLoader = loader;
+        this.clientIdentityLoader = ValidateUtils.checkNotNull(loader, "No client identity loader");
     }
 
     @Override
@@ -230,6 +235,7 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
         return userInteraction;
     }
 
+    @Override
     public void setUserInteraction(UserInteraction userInteraction) {
         this.userInteraction = userInteraction;
     }
@@ -239,8 +245,9 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
         return userAuthFactories;
     }
 
+    @Override
     public void setUserAuthFactories(List<NamedFactory<UserAuth>> userAuthFactories) {
-        this.userAuthFactories = userAuthFactories;
+        this.userAuthFactories = ValidateUtils.checkNotNullAndNotEmpty(userAuthFactories, "No user auth factories");
     }
 
     @Override
@@ -256,7 +263,19 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
         // if no client identities override use the default
         KeyPairProvider defaultIdentities = getKeyPairProvider();
         if (defaultIdentities == null) {
-            setKeyPairProvider(new DefaultClientIdentitiesWatcher(getClientIdentityLoader(), getFilePasswordProvider()));
+            setKeyPairProvider(new DefaultClientIdentitiesWatcher(
+                    new Supplier<ClientIdentityLoader>() {
+                        @Override
+                        public ClientIdentityLoader get() {
+                            return getClientIdentityLoader();
+                        }
+                    },
+                    new Supplier<FilePasswordProvider>() {
+                        @Override
+                        public FilePasswordProvider get() {
+                            return getFilePasswordProvider();
+                        }
+                    }));
         }
 
         // Register the additional agent forwarding channel if needed
@@ -453,38 +472,63 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
             final ConnectFuture connectFuture, final String username, final SocketAddress address,
             final Collection<? extends KeyPair> identities, final boolean useDefaultIdentities) {
         return new SshFutureListener<IoConnectFuture>() {
-            @SuppressWarnings("synthetic-access")
             @Override
+            @SuppressWarnings("synthetic-access")
             public void operationComplete(IoConnectFuture future) {
                 if (future.isCanceled()) {
                     connectFuture.cancel();
-                } else if (future.getException() != null) {
-                    connectFuture.setException(future.getException());
-                } else {
-                    ClientSessionImpl session = (ClientSessionImpl) AbstractSession.getSession(future.getSession());
-                    session.setUsername(username);
+                    return;
+                }
 
-                    if (useDefaultIdentities) {
-                        session.setKeyPairProvider(getKeyPairProvider());
+                Throwable t = future.getException();
+                if (t != null) {
+                    if (log.isDebugEnabled()) {
+                        log.debug("operationComplete({}@{}) failed ({}): {}",
+                                  username, address, t.getClass().getSimpleName(), t.getMessage());
                     }
+                    connectFuture.setException(t);
+                } else {
+                    onConnectOperationComplete(future.getSession(), connectFuture, username, address, identities, useDefaultIdentities);
+                }
+            }
+        };
+    }
 
-                    int numIds = GenericUtils.size(identities);
-                    if (numIds > 0) {
-                        if (log.isDebugEnabled()) {
-                            log.debug("doConnect({}@{}) adding {} identities", username, address, numIds);
-                        }
-                        for (KeyPair kp : identities) {
-                            if (log.isTraceEnabled()) {
-                                log.trace("doConnect({}@{}) add identity type={}, fingerprint={}",
-                                          username, address, KeyUtils.getKeyType(kp), KeyUtils.getFingerPrint(kp.getPublic()));
-                            }
-                            session.addPublicKeyIdentity(kp);
-                        }
+    protected void onConnectOperationComplete(IoSession ioSession, ConnectFuture connectFuture,
+            String username, SocketAddress address, Collection<? extends KeyPair> identities, boolean useDefaultIdentities) {
+        ClientSession session = (ClientSession) AbstractSession.getSession(ioSession);
+        session.setUsername(username);
+
+        if (useDefaultIdentities) {
+            // check if session listener intervened
+            KeyPairProvider kpSession = session.getKeyPairProvider();
+            KeyPairProvider kpClient = ValidateUtils.checkNotNull(getKeyPairProvider(), "No default key-pair provider");
+            if (kpSession == null) {
+                session.setKeyPairProvider(kpClient);
+            } else {
+                if (kpSession != kpClient) {
+                    if (log.isDebugEnabled()) {
+                        log.debug("onConnectOperationComplete({}) key-pair provider override", session);
                     }
-                    connectFuture.setSession(session);
                 }
             }
-        };
+        }
+
+        int numIds = GenericUtils.size(identities);
+        if (numIds > 0) {
+            if (log.isDebugEnabled()) {
+                log.debug("onConnectOperationComplete({}@{}) adding {} identities", username, address, numIds);
+            }
+            for (KeyPair kp : identities) {
+                if (log.isTraceEnabled()) {
+                    log.trace("onConnectOperationComplete({}@{}) add identity type={}, fingerprint={}",
+                              username, address, KeyUtils.getKeyType(kp), KeyUtils.getFingerPrint(kp.getPublic()));
+                }
+                session.addPublicKeyIdentity(kp);
+            }
+        }
+
+        connectFuture.setSession(session);
     }
 
     protected IoConnector createConnector() {
@@ -699,6 +743,11 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
             client.start();
             client.setUserInteraction(new UserInteraction() {
                 @Override
+                public boolean isInteractionAllowed(ClientSession session) {
+                    return true;
+                }
+
+                @Override
                 public void welcome(ClientSession clientSession, String banner, String lang) {
                     stdout.println(banner);
                 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e9dd7f47/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthKeyboardInteractive.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthKeyboardInteractive.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthKeyboardInteractive.java
index 34ba100..553a544 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthKeyboardInteractive.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthKeyboardInteractive.java
@@ -24,7 +24,7 @@ import java.util.Collection;
 import java.util.Iterator;
 import java.util.List;
 
-import org.apache.sshd.client.ClientFactoryManager;
+import org.apache.sshd.client.ClientAuthenticationManager;
 import org.apache.sshd.client.session.ClientSession;
 import org.apache.sshd.common.PropertyResolverUtils;
 import org.apache.sshd.common.SshConstants;
@@ -94,7 +94,7 @@ public class UserAuthKeyboardInteractive extends AbstractUserAuth {
             }
         }
         passwords = pwds.iterator();
-        maxTrials = PropertyResolverUtils.getIntProperty(session, ClientFactoryManager.PASSWORD_PROMPTS, ClientFactoryManager.DEFAULT_PASSWORD_PROMPTS);
+        maxTrials = PropertyResolverUtils.getIntProperty(session, ClientAuthenticationManager.PASSWORD_PROMPTS, ClientAuthenticationManager.DEFAULT_PASSWORD_PROMPTS);
         ValidateUtils.checkTrue(maxTrials > 0, "Non-positive max. trials: %d", maxTrials);
     }
 
@@ -184,7 +184,9 @@ public class UserAuthKeyboardInteractive extends AbstractUserAuth {
             session.writePacket(buffer);
             return true;
         }
-        throw new IllegalStateException("process(" + session + ")[" + service + ") received unknown packet: cmd=" + cmd);
+
+        throw new IllegalStateException("process(" + session + ")[" + service + ")"
+                + " received unknown packet: cmd=" + SshConstants.getCommandMessageName(cmd));
     }
 
     protected String getExchangeLanguageTag(ClientSession session) {
@@ -229,8 +231,8 @@ public class UserAuthKeyboardInteractive extends AbstractUserAuth {
             return new String[]{candidate};
         } else {
             ClientSession session = getClientSession();
-            UserInteraction ui = UserInteraction.Utils.resolveUserInteraction(session);
-            if (ui != null) {
+            UserInteraction ui = session.getUserInteraction();
+            if ((ui != null) && ui.isInteractionAllowed(session)) {
                 return ui.interactive(session, name, instruction, lang, prompt, echo);
             }
         }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e9dd7f47/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPassword.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPassword.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPassword.java
index 4924f0c..450c915 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPassword.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPassword.java
@@ -81,9 +81,8 @@ public class UserAuthPassword extends AbstractUserAuth {
         if (cmd == SshConstants.SSH_MSG_USERAUTH_PASSWD_CHANGEREQ) {
             String prompt = buffer.getString();
             String lang = buffer.getString();
-            UserInteraction ui = UserInteraction.Utils.resolveUserInteraction(session);
-
-            if (ui != null) {
+            UserInteraction ui = session.getUserInteraction();
+            if ((ui != null) && ui.isInteractionAllowed(session)) {
                 String password = ui.getUpdatedPassword(session, prompt, lang);
                 if (GenericUtils.isEmpty(password)) {
                     if (log.isDebugEnabled()) {
@@ -104,7 +103,8 @@ public class UserAuthPassword extends AbstractUserAuth {
             return false;
         }
 
-        throw new IllegalStateException("process(" + username + "@" + session + ")[" + service + "] received unknown packet: cmd=" + cmd);
+        throw new IllegalStateException("process(" + username + "@" + session + ")[" + service + "]"
+                + " received unknown packet: cmd=" + SshConstants.getCommandMessageName(cmd));
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e9dd7f47/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPublicKey.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPublicKey.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPublicKey.java
index 525eb43..d4b0db4 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPublicKey.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPublicKey.java
@@ -37,7 +37,9 @@ import org.apache.sshd.common.SshConstants;
 import org.apache.sshd.common.config.keys.KeyUtils;
 import org.apache.sshd.common.kex.KeyExchange;
 import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.Pair;
+import org.apache.sshd.common.util.ValidateUtils;
 import org.apache.sshd.common.util.buffer.Buffer;
 import org.apache.sshd.common.util.buffer.BufferUtils;
 import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
@@ -65,16 +67,24 @@ public class UserAuthPublicKey extends AbstractUserAuth {
         List<PublicKeyIdentity> ids = new ArrayList<>();
         for (Object o : identities) {
             if (o instanceof KeyPair) {
-                ids.add(new KeyPairIdentity(session.getFactoryManager(), (KeyPair) o));
+                ids.add(new KeyPairIdentity(session, (KeyPair) o));
             }
         }
 
-        FactoryManager manager = session.getFactoryManager();
+        FactoryManager manager = ValidateUtils.checkNotNull(session.getFactoryManager(), "No session factory manager");
         SshAgentFactory factory = manager.getAgentFactory();
         if (factory != null) {
-            this.agent = factory.createClient(manager);
-            for (Pair<PublicKey, String> pair : agent.getIdentities()) {
-                ids.add(new KeyAgentIdentity(agent, pair.getFirst()));
+            this.agent = ValidateUtils.checkNotNull(factory.createClient(manager), "No agent created");
+            Collection<Pair<PublicKey, String>> agentKeys = agent.getIdentities();
+            if (GenericUtils.size(agentKeys) > 0) {
+                for (Pair<PublicKey, String> pair : agentKeys) {
+                    PublicKey key = pair.getFirst();
+                    if (log.isDebugEnabled()) {
+                        log.debug("init({}) add agent public key type={}: comment={}, fingerprint={}",
+                                  session, pair.getSecond(), KeyUtils.getKeyType(key), KeyUtils.getFingerPrint(key));
+                    }
+                    ids.add(new KeyAgentIdentity(agent, key));
+                }
             }
         } else {
             this.agent = null;
@@ -82,8 +92,12 @@ public class UserAuthPublicKey extends AbstractUserAuth {
 
         KeyPairProvider provider = session.getKeyPairProvider();
         if (provider != null) {
-            for (KeyPair pair : provider.loadKeys()) {
-                ids.add(new KeyPairIdentity(manager, pair));
+            for (KeyPair kp : provider.loadKeys()) {
+                if (log.isDebugEnabled()) {
+                    log.debug("init({}) add provider public key type={}: {}",
+                              session, KeyUtils.getKeyType(kp), KeyUtils.getFingerPrint(kp.getPublic()));
+                }
+                ids.add(new KeyPairIdentity(session, kp));
             }
         }
         this.keys = ids.iterator();
@@ -103,8 +117,8 @@ public class UserAuthPublicKey extends AbstractUserAuth {
                 String algo = KeyUtils.getKeyType(key);
                 String name = getName();
                 if (log.isDebugEnabled()) {
-                    log.debug("process({}@{})[{}] Send SSH_MSG_USERAUTH_REQUEST request {} algo={}",
-                              username, session, service, name, algo);
+                    log.debug("process({}@{})[{}] Send SSH_MSG_USERAUTH_REQUEST request {} type={} - fingerprint={}",
+                              username, session, service, name, algo, KeyUtils.getFingerPrint(key));
                 }
 
                 buffer = session.createBuffer(SshConstants.SSH_MSG_USERAUTH_REQUEST);
@@ -130,8 +144,8 @@ public class UserAuthPublicKey extends AbstractUserAuth {
             String algo = KeyUtils.getKeyType(key);
             String name = getName();
             if (log.isDebugEnabled()) {
-                log.debug("process({}@{})[{}] Send SSH_MSG_USERAUTH_REQUEST reply {} algo={}",
-                          username, session, service, name, algo);
+                log.debug("process({}@{})[{}] Send SSH_MSG_USERAUTH_REQUEST reply {} type={} - fingerprint={}",
+                          username, session, service, name, algo, KeyUtils.getFingerPrint(key));
             }
 
             buffer = session.prepareBuffer(SshConstants.SSH_MSG_USERAUTH_REQUEST, BufferUtils.clear(buffer));
@@ -154,7 +168,7 @@ public class UserAuthPublicKey extends AbstractUserAuth {
             bs.putPublicKey(key);
 
             byte[] sig = current.sign(bs.getCompactData());
-            bs = new ByteArrayBuffer();
+            bs = new ByteArrayBuffer(algo.length() + sig.length + Long.SIZE);
             bs.putString(algo);
             bs.putBytes(sig);
             buffer.putBytes(bs.array(), bs.rpos(), bs.available());
@@ -163,7 +177,8 @@ public class UserAuthPublicKey extends AbstractUserAuth {
             return true;
         }
 
-        throw new IllegalStateException("process(" + username + "@" + session + ")[" + service + "] received unknown packet: cmd=" + cmd);
+        throw new IllegalStateException("process(" + username + "@" + session + ")[" + service + "]"
+                + " received unknown packet: cmd=" + SshConstants.getCommandMessageName(cmd));
     }
 
     @Override
@@ -173,6 +188,8 @@ public class UserAuthPublicKey extends AbstractUserAuth {
                 agent.close();
             } catch (IOException e) {
                 throw new RuntimeException("Failed (" + e.getClass().getSimpleName() + ") to close agent: " + e.getMessage(), e);
+            } finally {
+                agent = null;
             }
         }
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e9dd7f47/sshd-core/src/main/java/org/apache/sshd/client/auth/UserInteraction.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/UserInteraction.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/UserInteraction.java
index a05dfbc..700055a 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/UserInteraction.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/auth/UserInteraction.java
@@ -18,7 +18,6 @@
  */
 package org.apache.sshd.client.auth;
 
-import org.apache.sshd.client.ClientFactoryManager;
 import org.apache.sshd.client.session.ClientSession;
 
 /**
@@ -28,6 +27,44 @@ import org.apache.sshd.client.session.ClientSession;
  * @see <a href="https://www.ietf.org/rfc/rfc4256.txt">RFC 4256</A>
  */
 public interface UserInteraction {
+    /**
+     * A useful &quot;placeholder&quot; that indicates that no interaction is expected.
+     * <B>Note:</B> throws {@link IllegalStateException} is any of the interaction
+     * methods is called
+     */
+    UserInteraction NONE = new UserInteraction() {
+        @Override
+        public boolean isInteractionAllowed(ClientSession session) {
+            return false;
+        }
+
+        @Override
+        public void welcome(ClientSession session, String banner, String lang) {
+            // ignored
+        }
+
+        @Override
+        public String[] interactive(ClientSession session, String name, String instruction, String lang, String[] prompt, boolean[] echo) {
+            throw new IllegalStateException("interactive(" + session + ")[" + name + "] unexpected call");
+        }
+
+        @Override
+        public String getUpdatedPassword(ClientSession session, String prompt, String lang) {
+            throw new IllegalStateException("getUpdatedPassword(" + session + ")[" + prompt + "] unexpected call");
+        }
+
+        @Override
+        public String toString() {
+            return "NONE";
+        }
+    };
+
+    /**
+     *
+     * @param session The {@link ClientSession}
+     * @return {@code true} if user interaction allowed for this session
+     */
+    boolean isInteractionAllowed(ClientSession session);
 
     /**
      * Displays the welcome banner to the user.
@@ -70,38 +107,4 @@ public interface UserInteraction {
      * be it other passwords, public keys, etc...)
      */
     String getUpdatedPassword(ClientSession session, String prompt, String lang);
-
-    // CHECKSTYLE:OFF
-    final class Utils {
-    // CHECKSTYLE:ON
-
-        private Utils() {
-            throw new UnsupportedOperationException("No instance allowed");
-        }
-
-        /**
-         * Checks which {@link UserInteraction} instance to use - if the session
-         * has a specific one, then it is used. Otherwise, the {@link ClientFactoryManager}
-         * (if any is available) is consulted.
-         *
-         * @param session The {@link ClientSession}
-         * @return The resolved user interaction instance
-         * @see ClientSession#getUserInteraction()
-         * @see ClientSession#getFactoryManager()
-         * @see ClientFactoryManager#getUserInteraction()
-         */
-        public static UserInteraction resolveUserInteraction(ClientSession session) {
-            if (session == null) {
-                return null;
-            }
-
-            UserInteraction ui = session.getUserInteraction();
-            if (ui == null) {
-                ClientFactoryManager manager = session.getFactoryManager();
-                ui = (manager == null) ? null : manager.getUserInteraction();
-            }
-
-            return ui;
-        }
-    }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e9dd7f47/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/KeyPairIdentity.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/KeyPairIdentity.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/KeyPairIdentity.java
index 40b3837..e84195f 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/KeyPairIdentity.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/KeyPairIdentity.java
@@ -21,9 +21,9 @@ package org.apache.sshd.client.auth.pubkey;
 import java.security.KeyPair;
 import java.security.PublicKey;
 
-import org.apache.sshd.common.FactoryManager;
 import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.kex.KexFactoryManager;
 import org.apache.sshd.common.signature.Signature;
 import org.apache.sshd.common.util.ValidateUtils;
 
@@ -34,9 +34,9 @@ import org.apache.sshd.common.util.ValidateUtils;
  */
 public class KeyPairIdentity implements PublicKeyIdentity {
     private final KeyPair pair;
-    private final FactoryManager manager;
+    private final KexFactoryManager manager;
 
-    public KeyPairIdentity(FactoryManager manager, KeyPair pair) {
+    public KeyPairIdentity(KexFactoryManager manager, KeyPair pair) {
         this.manager = ValidateUtils.checkNotNull(manager, "No manager");
         this.pair = ValidateUtils.checkNotNull(pair, "No key pair");
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e9dd7f47/sshd-core/src/main/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcher.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcher.java b/sshd-core/src/main/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcher.java
index 694b1dd..fb104b4 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcher.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcher.java
@@ -32,6 +32,7 @@ import org.apache.sshd.common.config.keys.BuiltinIdentities;
 import org.apache.sshd.common.config.keys.FilePasswordProvider;
 import org.apache.sshd.common.config.keys.KeyUtils;
 import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.Supplier;
 import org.apache.sshd.common.util.ValidateUtils;
 
 /**
@@ -47,6 +48,19 @@ public class BuiltinClientIdentitiesWatcher extends ClientIdentitiesWatcher {
 
     public BuiltinClientIdentitiesWatcher(Path keysFolder, Collection<String> ids, boolean supportedOnly,
             ClientIdentityLoader loader, FilePasswordProvider provider, boolean strict) {
+        this(keysFolder, ids, supportedOnly,
+             GenericUtils.supplierOf(ValidateUtils.checkNotNull(loader, "No client identity loader")),
+             GenericUtils.supplierOf(ValidateUtils.checkNotNull(provider, "No password provider")),
+             strict);
+    }
+
+    public BuiltinClientIdentitiesWatcher(Path keysFolder, boolean supportedOnly,
+            Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider, boolean strict) {
+        this(keysFolder, NamedResource.Utils.getNameList(BuiltinIdentities.VALUES), supportedOnly, loader, provider, strict);
+    }
+
+    public BuiltinClientIdentitiesWatcher(Path keysFolder, Collection<String> ids, boolean supportedOnly,
+            Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider, boolean strict) {
         super(getBuiltinIdentitiesPaths(keysFolder, ids), loader, provider, strict);
         this.supportedOnly = supportedOnly;
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e9dd7f47/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentitiesWatcher.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentitiesWatcher.java b/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentitiesWatcher.java
index aa26b8b..e0d930a 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentitiesWatcher.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentitiesWatcher.java
@@ -30,6 +30,8 @@ import org.apache.sshd.common.config.keys.FilePasswordProvider;
 import org.apache.sshd.common.keyprovider.AbstractKeyPairProvider;
 import org.apache.sshd.common.keyprovider.KeyPairProvider;
 import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.Supplier;
+import org.apache.sshd.common.util.ValidateUtils;
 
 /**
  * Watches over a group of files that contains client identities
@@ -39,11 +41,26 @@ import org.apache.sshd.common.util.GenericUtils;
 public class ClientIdentitiesWatcher extends AbstractKeyPairProvider implements KeyPairProvider {
     private final Collection<ClientIdentityProvider> providers;
 
-    public ClientIdentitiesWatcher(Collection<? extends Path> paths, ClientIdentityLoader loader, FilePasswordProvider provider) {
+    public ClientIdentitiesWatcher(Collection<? extends Path> paths,
+            ClientIdentityLoader loader, FilePasswordProvider provider) {
         this(paths, loader, provider, true);
     }
 
-    public ClientIdentitiesWatcher(Collection<? extends Path> paths, ClientIdentityLoader loader, FilePasswordProvider provider, boolean strict) {
+    public ClientIdentitiesWatcher(Collection<? extends Path> paths,
+            ClientIdentityLoader loader, FilePasswordProvider provider, boolean strict) {
+        this(paths,
+             GenericUtils.supplierOf(ValidateUtils.checkNotNull(loader, "No client identity loader")),
+             GenericUtils.supplierOf(ValidateUtils.checkNotNull(provider, "No password provider")),
+             strict);
+    }
+
+    public ClientIdentitiesWatcher(Collection<? extends Path> paths,
+            Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider) {
+        this(paths, loader, provider, true);
+    }
+
+    public ClientIdentitiesWatcher(Collection<? extends Path> paths,
+            Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider, boolean strict) {
         this(buildProviders(paths, loader, provider, strict));
     }
 
@@ -77,7 +94,16 @@ public class ClientIdentitiesWatcher extends AbstractKeyPairProvider implements
         return keys;
     }
 
-    public static List<ClientIdentityProvider> buildProviders(Collection<? extends Path> paths, ClientIdentityLoader loader, FilePasswordProvider provider, boolean strict) {
+    public static List<ClientIdentityProvider> buildProviders(
+            Collection<? extends Path> paths, ClientIdentityLoader loader, FilePasswordProvider provider, boolean strict) {
+        return buildProviders(paths,
+                GenericUtils.supplierOf(ValidateUtils.checkNotNull(loader, "No client identity loader")),
+                GenericUtils.supplierOf(ValidateUtils.checkNotNull(provider, "No password provider")),
+                strict);
+    }
+
+    public static List<ClientIdentityProvider> buildProviders(
+            Collection<? extends Path> paths, Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider, boolean strict) {
         if (GenericUtils.isEmpty(paths)) {
             return Collections.emptyList();
         }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e9dd7f47/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcher.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcher.java b/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcher.java
index bdd2bf5..2923ba2 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcher.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcher.java
@@ -27,7 +27,9 @@ import java.util.concurrent.atomic.AtomicReference;
 
 import org.apache.sshd.common.config.keys.FilePasswordProvider;
 import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.Pair;
+import org.apache.sshd.common.util.Supplier;
 import org.apache.sshd.common.util.ValidateUtils;
 import org.apache.sshd.common.util.io.IoUtils;
 import org.apache.sshd.common.util.io.ModifiableFileWatcher;
@@ -40,8 +42,8 @@ import org.apache.sshd.common.util.io.ModifiableFileWatcher;
  */
 public class ClientIdentityFileWatcher extends ModifiableFileWatcher implements ClientIdentityProvider {
     private final AtomicReference<KeyPair> identityHolder = new AtomicReference<>(null);
-    private final ClientIdentityLoader loader;
-    private final FilePasswordProvider provider;
+    private final Supplier<ClientIdentityLoader> loaderHolder;
+    private final Supplier<FilePasswordProvider> providerHolder;
     private final boolean strict;
 
     public ClientIdentityFileWatcher(Path path, ClientIdentityLoader loader, FilePasswordProvider provider) {
@@ -49,9 +51,20 @@ public class ClientIdentityFileWatcher extends ModifiableFileWatcher implements
     }
 
     public ClientIdentityFileWatcher(Path path, ClientIdentityLoader loader, FilePasswordProvider provider, boolean strict) {
+        this(path,
+             GenericUtils.supplierOf(ValidateUtils.checkNotNull(loader, "No client identity loader")),
+             GenericUtils.supplierOf(ValidateUtils.checkNotNull(provider, "No password provider")),
+             strict);
+    }
+
+    public ClientIdentityFileWatcher(Path path, Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider) {
+        this(path, loader, provider, true);
+    }
+
+    public ClientIdentityFileWatcher(Path path, Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider, boolean strict) {
         super(path);
-        this.loader = ValidateUtils.checkNotNull(loader, "No client identity loader");
-        this.provider = ValidateUtils.checkNotNull(provider, "No password provided");
+        this.loaderHolder = ValidateUtils.checkNotNull(loader, "No client identity loader");
+        this.providerHolder = ValidateUtils.checkNotNull(provider, "No password provider");
         this.strict = strict;
     }
 
@@ -60,11 +73,11 @@ public class ClientIdentityFileWatcher extends ModifiableFileWatcher implements
     }
 
     public final ClientIdentityLoader getClientIdentityLoader() {
-        return loader;
+        return loaderHolder.get();
     }
 
     public final FilePasswordProvider getFilePasswordProvider() {
-        return provider;
+        return providerHolder.get();
     }
 
     @Override
@@ -101,14 +114,15 @@ public class ClientIdentityFileWatcher extends ModifiableFileWatcher implements
         }
 
         String location = path.toString();
-        ClientIdentityLoader idLoader = getClientIdentityLoader();
+        ClientIdentityLoader idLoader = ValidateUtils.checkNotNull(getClientIdentityLoader(), "No client identity loader");
         if (idLoader.isValidLocation(location)) {
-            return idLoader.loadClientIdentity(location, getFilePasswordProvider());
+            return idLoader.loadClientIdentity(location, ValidateUtils.checkNotNull(getFilePasswordProvider(), "No file password provider"));
         }
 
         if (log.isDebugEnabled()) {
             log.debug("reloadClientIdentity({}) invalid location", location);
         }
+
         return null;
     }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e9dd7f47/sshd-core/src/main/java/org/apache/sshd/client/config/keys/DefaultClientIdentitiesWatcher.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/keys/DefaultClientIdentitiesWatcher.java b/sshd-core/src/main/java/org/apache/sshd/client/config/keys/DefaultClientIdentitiesWatcher.java
index a689599..ba1719f 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/keys/DefaultClientIdentitiesWatcher.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/config/keys/DefaultClientIdentitiesWatcher.java
@@ -24,6 +24,9 @@ import java.util.List;
 
 import org.apache.sshd.common.config.keys.FilePasswordProvider;
 import org.apache.sshd.common.config.keys.PublicKeyEntry;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.Supplier;
+import org.apache.sshd.common.util.ValidateUtils;
 
 /**
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
@@ -38,6 +41,22 @@ public class DefaultClientIdentitiesWatcher extends BuiltinClientIdentitiesWatch
     }
 
     public DefaultClientIdentitiesWatcher(boolean supportedOnly, ClientIdentityLoader loader, FilePasswordProvider provider, boolean strict) {
+        this(supportedOnly,
+             GenericUtils.supplierOf(ValidateUtils.checkNotNull(loader, "No client identity loader")),
+             GenericUtils.supplierOf(ValidateUtils.checkNotNull(provider, "No password provider")),
+             strict);
+    }
+
+    public DefaultClientIdentitiesWatcher(Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider) {
+        this(loader, provider, true);
+    }
+
+    public DefaultClientIdentitiesWatcher(Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider, boolean strict) {
+        this(true, loader, provider, strict);
+    }
+
+    public DefaultClientIdentitiesWatcher(boolean supportedOnly,
+            Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider, boolean strict) {
         super(PublicKeyEntry.getDefaultKeysFolderPath(), supportedOnly, loader, provider, strict);
     }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e9dd7f47/sshd-core/src/main/java/org/apache/sshd/client/kex/DHGClient.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/kex/DHGClient.java b/sshd-core/src/main/java/org/apache/sshd/client/kex/DHGClient.java
index 95af92c..9ffdb9c 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/kex/DHGClient.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/kex/DHGClient.java
@@ -18,7 +18,6 @@
  */
 package org.apache.sshd.client.kex;
 
-import org.apache.sshd.common.FactoryManager;
 import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.SshConstants;
 import org.apache.sshd.common.SshException;
@@ -130,8 +129,7 @@ public class DHGClient extends AbstractDHClientKeyExchange {
         hash.update(buffer.array(), 0, buffer.available());
         h = hash.digest();
 
-        FactoryManager manager = session.getFactoryManager();
-        Signature verif = ValidateUtils.checkNotNull(NamedFactory.Utils.create(manager.getSignatureFactories(), keyAlg),
+        Signature verif = ValidateUtils.checkNotNull(NamedFactory.Utils.create(session.getSignatureFactories(), keyAlg),
                 "No verifier located for algorithm=%s",
                 keyAlg);
         verif.initVerifier(serverKey);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e9dd7f47/sshd-core/src/main/java/org/apache/sshd/client/kex/DHGEXClient.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/kex/DHGEXClient.java b/sshd-core/src/main/java/org/apache/sshd/client/kex/DHGEXClient.java
index ac00fe3..cff00d8 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/kex/DHGEXClient.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/kex/DHGEXClient.java
@@ -21,7 +21,6 @@ package org.apache.sshd.client.kex;
 
 import java.math.BigInteger;
 
-import org.apache.sshd.common.FactoryManager;
 import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.SshConstants;
 import org.apache.sshd.common.SshException;
@@ -159,9 +158,8 @@ public class DHGEXClient extends AbstractDHClientKeyExchange {
             hash.update(buffer.array(), 0, buffer.available());
             h = hash.digest();
 
-            FactoryManager manager = session.getFactoryManager();
             Signature verif = ValidateUtils.checkNotNull(
-                    NamedFactory.Utils.create(manager.getSignatureFactories(), keyAlg),
+                    NamedFactory.Utils.create(session.getSignatureFactories(), keyAlg),
                     "No verifier located for algorithm=%s",
                     keyAlg);
             verif.initVerifier(serverKey);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e9dd7f47/sshd-core/src/main/java/org/apache/sshd/client/session/AbstractClientSession.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/session/AbstractClientSession.java b/sshd-core/src/main/java/org/apache/sshd/client/session/AbstractClientSession.java
new file mode 100644
index 0000000..76d042b
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/client/session/AbstractClientSession.java
@@ -0,0 +1,378 @@
+/*
+ * 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.sshd.client.session;
+
+import java.io.IOException;
+import java.nio.file.FileSystem;
+import java.security.KeyPair;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+
+import org.apache.sshd.client.ClientFactoryManager;
+import org.apache.sshd.client.auth.UserAuth;
+import org.apache.sshd.client.auth.UserInteraction;
+import org.apache.sshd.client.channel.ChannelDirectTcpip;
+import org.apache.sshd.client.channel.ChannelExec;
+import org.apache.sshd.client.channel.ChannelSubsystem;
+import org.apache.sshd.client.channel.ClientChannel;
+import org.apache.sshd.client.keyverifier.ServerKeyVerifier;
+import org.apache.sshd.client.scp.DefaultScpClient;
+import org.apache.sshd.client.scp.ScpClient;
+import org.apache.sshd.client.subsystem.sftp.DefaultSftpClient;
+import org.apache.sshd.client.subsystem.sftp.SftpClient;
+import org.apache.sshd.client.subsystem.sftp.SftpFileSystem;
+import org.apache.sshd.client.subsystem.sftp.SftpFileSystemProvider;
+import org.apache.sshd.client.subsystem.sftp.SftpVersionSelector;
+import org.apache.sshd.common.FactoryManager;
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.SshdSocketAddress;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.forward.TcpipForwarder;
+import org.apache.sshd.common.io.IoSession;
+import org.apache.sshd.common.scp.ScpTransferEventListener;
+import org.apache.sshd.common.session.AbstractSession;
+import org.apache.sshd.common.session.ConnectionService;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractClientSession extends AbstractSession implements ClientSession {
+    /**
+     * Compares 2 password identities - returns zero ONLY if <U>both</U> compared
+     * objects are {@link String}s and equal to each other
+     */
+    public static final Comparator<Object> PASSWORD_IDENTITY_COMPARATOR = new Comparator<Object>() {
+        @Override
+        public int compare(Object o1, Object o2) {
+            if (!(o1 instanceof String) || !(o2 instanceof String)) {
+                return -1;
+            } else {
+                return ((String) o1).compareTo((String) o2);
+            }
+        }
+    };
+
+    /**
+     * Compares 2 {@link KeyPair} identities - returns zero ONLY if <U>both</U> compared
+     * objects are {@link KeyPair}s and equal to each other
+     */
+    public static final Comparator<Object> KEYPAIR_IDENTITY_COMPARATOR = new Comparator<Object>() {
+        @Override
+        public int compare(Object o1, Object o2) {
+            if ((!(o1 instanceof KeyPair)) || (!(o2 instanceof KeyPair))) {
+                return -1;
+            } else if (KeyUtils.compareKeyPairs((KeyPair) o1, (KeyPair) o2)) {
+                return 0;
+            } else {
+                return 1;
+            }
+        }
+    };
+
+    private final List<Object> identities = new ArrayList<>();
+    private ServerKeyVerifier serverKeyVerifier;
+    private UserInteraction userInteraction;
+    private List<NamedFactory<UserAuth>> userAuthFactories;
+    private ScpTransferEventListener scpListener;
+
+    protected AbstractClientSession(ClientFactoryManager factoryManager, IoSession ioSession) {
+        super(false, factoryManager, ioSession);
+    }
+
+    @Override
+    public ClientFactoryManager getFactoryManager() {
+        return (ClientFactoryManager) super.getFactoryManager();
+    }
+
+    @Override
+    public ServerKeyVerifier getServerKeyVerifier() {
+        return resolveEffectiveProvider(ServerKeyVerifier.class, serverKeyVerifier, getFactoryManager().getServerKeyVerifier());
+    }
+
+    @Override
+    public void setServerKeyVerifier(ServerKeyVerifier serverKeyVerifier) {
+        this.serverKeyVerifier = serverKeyVerifier; // OK if null - inherit from parent
+    }
+
+    @Override
+    public UserInteraction getUserInteraction() {
+        return resolveEffectiveProvider(UserInteraction.class, userInteraction, getFactoryManager().getUserInteraction());
+    }
+
+    @Override
+    public void setUserInteraction(UserInteraction userInteraction) {
+        this.userInteraction = userInteraction; // OK if null - inherit from parent
+    }
+
+    @Override
+    public List<NamedFactory<UserAuth>> getUserAuthFactories() {
+        return resolveEffectiveFactories(UserAuth.class, userAuthFactories, getFactoryManager().getUserAuthFactories());
+    }
+
+    @Override
+    public void setUserAuthFactories(List<NamedFactory<UserAuth>> userAuthFactories) {
+        this.userAuthFactories = userAuthFactories; // OK if null/empty - inherit from parent
+    }
+
+    protected List<Object> getRegisteredIdentities() {
+        return identities;
+    }
+
+    @Override
+    public void addPasswordIdentity(String password) {
+        identities.add(ValidateUtils.checkNotNullAndNotEmpty(password, "No password provided"));
+        if (log.isDebugEnabled()) { // don't show the password in the log
+            log.debug("addPasswordIdentity({}) {}", this, KeyUtils.getFingerPrint(password));
+        }
+    }
+
+    @Override
+    public String removePasswordIdentity(String password) {
+        if (GenericUtils.isEmpty(password)) {
+            return null;
+        }
+
+        int index = findIdentityIndex(PASSWORD_IDENTITY_COMPARATOR, password);
+        if (index >= 0) {
+            return (String) identities.remove(index);
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public void addPublicKeyIdentity(KeyPair kp) {
+        ValidateUtils.checkNotNull(kp, "No key-pair to add");
+        ValidateUtils.checkNotNull(kp.getPublic(), "No public key");
+        ValidateUtils.checkNotNull(kp.getPrivate(), "No private key");
+
+        identities.add(kp);
+
+        if (log.isDebugEnabled()) {
+            log.debug("addPublicKeyIdentity({}) {}", this, KeyUtils.getFingerPrint(kp.getPublic()));
+        }
+    }
+
+    @Override
+    public KeyPair removePublicKeyIdentity(KeyPair kp) {
+        if (kp == null) {
+            return null;
+        }
+
+        int index = findIdentityIndex(KEYPAIR_IDENTITY_COMPARATOR, kp);
+        if (index >= 0) {
+            return (KeyPair) identities.remove(index);
+        } else {
+            return null;
+        }
+    }
+
+    protected int findIdentityIndex(Comparator<? super Object> comp, Object target) {
+        for (int index = 0; index < identities.size(); index++) {
+            Object value = identities.get(index);
+            if (comp.compare(value, target) == 0) {
+                return index;
+            }
+        }
+
+        return -1;
+    }
+
+    @Override
+    public ClientChannel createChannel(String type) throws IOException {
+        return createChannel(type, null);
+    }
+
+    @Override
+    public ClientChannel createChannel(String type, String subType) throws IOException {
+        if (ClientChannel.CHANNEL_SHELL.equals(type)) {
+            return createShellChannel();
+        } else if (ClientChannel.CHANNEL_EXEC.equals(type)) {
+            return createExecChannel(subType);
+        } else if (ClientChannel.CHANNEL_SUBSYSTEM.equals(type)) {
+            return createSubsystemChannel(subType);
+        } else {
+            throw new IllegalArgumentException("Unsupported channel type " + type);
+        }
+    }
+
+    @Override
+    public ChannelExec createExecChannel(String command) throws IOException {
+        ChannelExec channel = new ChannelExec(command);
+        ConnectionService service = getConnectionService();
+        int id = service.registerChannel(channel);
+        if (log.isDebugEnabled()) {
+            log.debug("createExecChannel({})[{}] created id={}", this, command, id);
+        }
+        return channel;
+    }
+
+    @Override
+    public ChannelSubsystem createSubsystemChannel(String subsystem) throws IOException {
+        ChannelSubsystem channel = new ChannelSubsystem(subsystem);
+        ConnectionService service = getConnectionService();
+        int id = service.registerChannel(channel);
+        if (log.isDebugEnabled()) {
+            log.debug("createSubsystemChannel({})[{}] created id={}", this, subsystem, id);
+        }
+        return channel;
+    }
+
+    @Override
+    public ChannelDirectTcpip createDirectTcpipChannel(SshdSocketAddress local, SshdSocketAddress remote) throws IOException {
+        ChannelDirectTcpip channel = new ChannelDirectTcpip(local, remote);
+        ConnectionService service = getConnectionService();
+        int id = service.registerChannel(channel);
+        if (log.isDebugEnabled()) {
+            log.debug("createDirectTcpipChannel({})[{} => {}] created id={}", this, local, remote, id);
+        }
+        return channel;
+    }
+
+    protected ClientUserAuthService getUserAuthService() {
+        return getService(ClientUserAuthService.class);
+    }
+
+    protected ConnectionService getConnectionService() {
+        return getService(ConnectionService.class);
+    }
+
+    @Override
+    public ScpTransferEventListener getScpTransferEventListener() {
+        return scpListener;
+    }
+
+    @Override
+    public void setScpTransferEventListener(ScpTransferEventListener listener) {
+        scpListener = listener;
+    }
+
+    @Override
+    public ScpClient createScpClient() {
+        return createScpClient(getScpTransferEventListener());
+    }
+
+    @Override
+    public ScpClient createScpClient(ScpTransferEventListener listener) {
+        return new DefaultScpClient(this, listener);
+    }
+
+    @Override   // TODO make this a default method in JDK-8
+    public SftpClient createSftpClient() throws IOException {
+        return createSftpClient(SftpVersionSelector.CURRENT);
+    }
+
+    @Override   // TODO make this a default method in JDK-8
+    public SftpClient createSftpClient(final int version) throws IOException {
+        return createSftpClient(SftpVersionSelector.Utils.fixedVersionSelector(version));
+    }
+
+    @Override
+    public SftpClient createSftpClient(SftpVersionSelector selector) throws IOException {
+        DefaultSftpClient client = new DefaultSftpClient(this);
+        client.negotiateVersion(selector);
+        return client;
+    }
+
+    @Override
+    public FileSystem createSftpFileSystem() throws IOException {
+        return createSftpFileSystem(SftpVersionSelector.CURRENT);
+    }
+
+    @Override
+    public FileSystem createSftpFileSystem(int version) throws IOException {
+        return createSftpFileSystem(SftpVersionSelector.Utils.fixedVersionSelector(version));
+    }
+
+    @Override
+    public FileSystem createSftpFileSystem(SftpVersionSelector selector) throws IOException {
+        return createSftpFileSystem(selector, SftpClient.DEFAULT_READ_BUFFER_SIZE, SftpClient.DEFAULT_WRITE_BUFFER_SIZE);
+    }
+
+    @Override
+    public FileSystem createSftpFileSystem(int version, int readBufferSize, int writeBufferSize) throws IOException {
+        return createSftpFileSystem(SftpVersionSelector.Utils.fixedVersionSelector(version), readBufferSize, writeBufferSize);
+    }
+
+    @Override
+    public FileSystem createSftpFileSystem(int readBufferSize, int writeBufferSize) throws IOException {
+        return createSftpFileSystem(SftpVersionSelector.CURRENT, readBufferSize, writeBufferSize);
+    }
+
+    @Override
+    public FileSystem createSftpFileSystem(SftpVersionSelector selector, int readBufferSize, int writeBufferSize) throws IOException {
+        SftpFileSystemProvider provider = new SftpFileSystemProvider((org.apache.sshd.client.SshClient) getFactoryManager(), selector);
+        SftpFileSystem fs = provider.newFileSystem(this);
+        fs.setReadBufferSize(readBufferSize);
+        fs.setWriteBufferSize(writeBufferSize);
+        return fs;
+    }
+
+    @Override
+    public SshdSocketAddress startLocalPortForwarding(SshdSocketAddress local, SshdSocketAddress remote) throws IOException {
+        return getTcpipForwarder().startLocalPortForwarding(local, remote);
+    }
+
+    @Override
+    public void stopLocalPortForwarding(SshdSocketAddress local) throws IOException {
+        getTcpipForwarder().stopLocalPortForwarding(local);
+    }
+
+    @Override
+    public SshdSocketAddress startRemotePortForwarding(SshdSocketAddress remote, SshdSocketAddress local) throws IOException {
+        return getTcpipForwarder().startRemotePortForwarding(remote, local);
+    }
+
+    @Override
+    public void stopRemotePortForwarding(SshdSocketAddress remote) throws IOException {
+        getTcpipForwarder().stopRemotePortForwarding(remote);
+    }
+
+    @Override
+    public SshdSocketAddress startDynamicPortForwarding(SshdSocketAddress local) throws IOException {
+        return getTcpipForwarder().startDynamicPortForwarding(local);
+    }
+
+    @Override
+    public void stopDynamicPortForwarding(SshdSocketAddress local) throws IOException {
+        getTcpipForwarder().stopDynamicPortForwarding(local);
+    }
+
+    protected TcpipForwarder getTcpipForwarder() {
+        ConnectionService service = ValidateUtils.checkNotNull(getConnectionService(), "No connection service");
+        return ValidateUtils.checkNotNull(service.getTcpipForwarder(), "No forwarder");
+    }
+
+    @Override
+    protected String resolveAvailableSignaturesProposal(FactoryManager manager) {
+        // the client does not have to provide keys for the available signatures
+        ValidateUtils.checkTrue(manager == getFactoryManager(), "Mismatched factory manager instances");
+        return NamedResource.Utils.getNames(getSignatureFactories());
+    }
+
+    @Override
+    public void startService(String name) throws Exception {
+        throw new IllegalStateException("Starting services is not supported on the client side: " + name);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e9dd7f47/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSession.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSession.java b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSession.java
index 87e50f5..6ffb863 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSession.java
@@ -25,8 +25,8 @@ import java.util.Collection;
 import java.util.Map;
 import java.util.Set;
 
+import org.apache.sshd.client.ClientAuthenticationManager;
 import org.apache.sshd.client.ClientFactoryManager;
-import org.apache.sshd.client.auth.UserInteraction;
 import org.apache.sshd.client.channel.ChannelDirectTcpip;
 import org.apache.sshd.client.channel.ChannelExec;
 import org.apache.sshd.client.channel.ChannelShell;
@@ -70,7 +70,7 @@ import org.apache.sshd.common.session.Session;
  *
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
-public interface ClientSession extends Session, KeyPairProviderHolder {
+public interface ClientSession extends Session, KeyPairProviderHolder, ClientAuthenticationManager {
     enum ClientSessionEvent {
         TIMEOUT,
         CLOSED,
@@ -105,21 +105,6 @@ public interface ClientSession extends Session, KeyPairProviderHolder {
     KeyPair removePublicKeyIdentity(KeyPair kp);
 
     /**
-     * @return The <U>specific</U> {@link UserInteraction} for this
-     * session. May be {@code null} indicating that the
-     * {@link ClientFactoryManager#getUserInteraction()} (if any) should
-     * be used
-     */
-    UserInteraction getUserInteraction();
-
-    /**
-     * @param userInteraction The <U>specific</U> {@link UserInteraction} for this
-     * session. May be {@code null} indicating that the
-     * {@link ClientFactoryManager#getUserInteraction()} (if any) should be used
-     */
-    void setUserInteraction(UserInteraction userInteraction);
-
-    /**
      * Starts the authentication process.
      * User identities will be tried until the server successfully authenticate the user.
      * User identities must be provided before calling this method using

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e9dd7f47/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java
index 0c9de7c..58eee3d 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java
@@ -20,12 +20,8 @@ package org.apache.sshd.client.session;
 
 import java.io.IOException;
 import java.net.SocketAddress;
-import java.nio.file.FileSystem;
-import java.security.KeyPair;
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.Comparator;
 import java.util.EnumMap;
 import java.util.EnumSet;
 import java.util.HashMap;
@@ -34,42 +30,23 @@ import java.util.Map;
 import java.util.Set;
 
 import org.apache.sshd.client.ClientFactoryManager;
-import org.apache.sshd.client.auth.UserInteraction;
-import org.apache.sshd.client.channel.ChannelDirectTcpip;
-import org.apache.sshd.client.channel.ChannelExec;
 import org.apache.sshd.client.channel.ChannelShell;
-import org.apache.sshd.client.channel.ChannelSubsystem;
-import org.apache.sshd.client.channel.ClientChannel;
 import org.apache.sshd.client.future.AuthFuture;
 import org.apache.sshd.client.future.DefaultAuthFuture;
 import org.apache.sshd.client.keyverifier.ServerKeyVerifier;
-import org.apache.sshd.client.scp.DefaultScpClient;
-import org.apache.sshd.client.scp.ScpClient;
-import org.apache.sshd.client.subsystem.sftp.DefaultSftpClient;
-import org.apache.sshd.client.subsystem.sftp.SftpClient;
-import org.apache.sshd.client.subsystem.sftp.SftpFileSystem;
-import org.apache.sshd.client.subsystem.sftp.SftpFileSystemProvider;
-import org.apache.sshd.client.subsystem.sftp.SftpVersionSelector;
 import org.apache.sshd.common.FactoryManager;
-import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.Service;
 import org.apache.sshd.common.ServiceFactory;
 import org.apache.sshd.common.SshConstants;
 import org.apache.sshd.common.SshException;
-import org.apache.sshd.common.SshdSocketAddress;
 import org.apache.sshd.common.cipher.BuiltinCiphers;
 import org.apache.sshd.common.cipher.CipherNone;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.forward.TcpipForwarder;
 import org.apache.sshd.common.future.DefaultKeyExchangeFuture;
 import org.apache.sshd.common.future.KeyExchangeFuture;
 import org.apache.sshd.common.io.IoSession;
 import org.apache.sshd.common.kex.KexProposalOption;
 import org.apache.sshd.common.kex.KexState;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.scp.ScpTransferEventListener;
 import org.apache.sshd.common.session.AbstractConnectionService;
-import org.apache.sshd.common.session.AbstractSession;
 import org.apache.sshd.common.session.ConnectionService;
 import org.apache.sshd.common.session.SessionListener;
 import org.apache.sshd.common.util.GenericUtils;
@@ -81,39 +58,7 @@ import org.apache.sshd.common.util.buffer.Buffer;
  *
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
-public class ClientSessionImpl extends AbstractSession implements ClientSession {
-    /**
-     * Compares 2 password identities - returns zero ONLY if <U>both</U> compared
-     * objects are {@link String}s and equal to each other
-     */
-    public static final Comparator<Object> PASSWORD_IDENTITY_COMPARATOR = new Comparator<Object>() {
-        @Override
-        public int compare(Object o1, Object o2) {
-            if (!(o1 instanceof String) || !(o2 instanceof String)) {
-                return -1;
-            } else {
-                return ((String) o1).compareTo((String) o2);
-            }
-        }
-    };
-
-    /**
-     * Compares 2 {@link KeyPair} identities - returns zero ONLY if <U>both</U> compared
-     * objects are {@link KeyPair}s and equal to each other
-     */
-    public static final Comparator<Object> KEYPAIR_IDENTITY_COMPARATOR = new Comparator<Object>() {
-        @Override
-        public int compare(Object o1, Object o2) {
-            if ((!(o1 instanceof KeyPair)) || (!(o2 instanceof KeyPair))) {
-                return -1;
-            } else if (KeyUtils.compareKeyPairs((KeyPair) o1, (KeyPair) o2)) {
-                return 0;
-            } else {
-                return 1;
-            }
-        }
-    };
-
+public class ClientSessionImpl extends AbstractClientSession {
     protected AuthFuture authFuture;
 
     /**
@@ -126,14 +71,12 @@ public class ClientSessionImpl extends AbstractSession implements ClientSession
     private ServiceFactory currentServiceFactory;
     private Service nextService;
     private ServiceFactory nextServiceFactory;
-    private final List<Object> identities = new ArrayList<>();
-    private UserInteraction userInteraction;
-    private ScpTransferEventListener scpListener;
-    private KeyPairProvider keyPairProvider;
 
     public ClientSessionImpl(ClientFactoryManager client, IoSession session) throws Exception {
-        super(false, client, session);
-        log.debug("Client session created: {}", session);
+        super(client, session);
+        if (log.isDebugEnabled()) {
+            log.debug("Client session created: {}", session);
+        }
         // Need to set the initial service early as calling code likes to start trying to
         // manipulate it before the connection has even been established.  For instance, to
         // set the authPassword.
@@ -152,6 +95,11 @@ public class ClientSessionImpl extends AbstractSession implements ClientSession
 
         authFuture = new DefaultAuthFuture(lock);
         authFuture.setAuthed(false);
+
+        // Inform the listener of the newly created session
+        SessionListener listener = getSessionListenerProxy();
+        listener.sessionCreated(this);
+
         sendClientIdentification();
         kexState.set(KexState.INIT);
         sendKexInit();
@@ -171,90 +119,6 @@ public class ClientSessionImpl extends AbstractSession implements ClientSession
     }
 
     @Override
-    public ClientFactoryManager getFactoryManager() {
-        return (ClientFactoryManager) super.getFactoryManager();
-    }
-
-    @Override
-    public KeyPairProvider getKeyPairProvider() {
-        return keyPairProvider;
-    }
-
-    public void setKeyPairProvider(KeyPairProvider keyPairProvider) {
-        this.keyPairProvider = keyPairProvider;
-    }
-
-    @Override
-    public void addPasswordIdentity(String password) {
-        identities.add(ValidateUtils.checkNotNullAndNotEmpty(password, "No password provided"));
-        if (log.isDebugEnabled()) { // don't show the password in the log
-            log.debug("addPasswordIdentity({}) {}", this, KeyUtils.getFingerPrint(password));
-        }
-    }
-
-    @Override
-    public String removePasswordIdentity(String password) {
-        if (GenericUtils.isEmpty(password)) {
-            return null;
-        }
-
-        int index = findIdentityIndex(PASSWORD_IDENTITY_COMPARATOR, password);
-        if (index >= 0) {
-            return (String) identities.remove(index);
-        } else {
-            return null;
-        }
-    }
-
-    @Override
-    public void addPublicKeyIdentity(KeyPair kp) {
-        ValidateUtils.checkNotNull(kp, "No key-pair to add");
-        ValidateUtils.checkNotNull(kp.getPublic(), "No public key");
-        ValidateUtils.checkNotNull(kp.getPrivate(), "No private key");
-
-        identities.add(kp);
-
-        if (log.isDebugEnabled()) {
-            log.debug("addPublicKeyIdentity({}) {}", this, KeyUtils.getFingerPrint(kp.getPublic()));
-        }
-    }
-
-    @Override
-    public KeyPair removePublicKeyIdentity(KeyPair kp) {
-        if (kp == null) {
-            return null;
-        }
-
-        int index = findIdentityIndex(KEYPAIR_IDENTITY_COMPARATOR, kp);
-        if (index >= 0) {
-            return (KeyPair) identities.remove(index);
-        } else {
-            return null;
-        }
-    }
-
-    protected int findIdentityIndex(Comparator<? super Object> comp, Object target) {
-        for (int index = 0; index < identities.size(); index++) {
-            Object value = identities.get(index);
-            if (comp.compare(value, target) == 0) {
-                return index;
-            }
-        }
-
-        return -1;
-    }
-
-    @Override
-    public UserInteraction getUserInteraction() {
-        return userInteraction;
-    }
-
-    @Override
-    public void setUserInteraction(UserInteraction userInteraction) {
-        this.userInteraction = userInteraction;
-    }
-
-    @Override
     public AuthFuture auth() throws IOException {
         if (username == null) {
             throw new IllegalStateException("No username specified when the session was created");
@@ -262,7 +126,7 @@ public class ClientSessionImpl extends AbstractSession implements ClientSession
 
         ClientUserAuthService authService = getUserAuthService();
         synchronized (lock) {
-            authFuture = authService.auth(identities, nextServiceName());
+            authFuture = authService.auth(getRegisteredIdentities(), nextServiceName());
             return authFuture;
         }
     }
@@ -350,24 +214,6 @@ public class ClientSessionImpl extends AbstractSession implements ClientSession
     }
 
     @Override
-    public ClientChannel createChannel(String type) throws IOException {
-        return createChannel(type, null);
-    }
-
-    @Override
-    public ClientChannel createChannel(String type, String subType) throws IOException {
-        if (ClientChannel.CHANNEL_SHELL.equals(type)) {
-            return createShellChannel();
-        } else if (ClientChannel.CHANNEL_EXEC.equals(type)) {
-            return createExecChannel(subType);
-        } else if (ClientChannel.CHANNEL_SUBSYSTEM.equals(type)) {
-            return createSubsystemChannel(subType);
-        } else {
-            throw new IllegalArgumentException("Unsupported channel type " + type);
-        }
-    }
-
-    @Override
     public ChannelShell createShellChannel() throws IOException {
         if ((inCipher instanceof CipherNone) || (outCipher instanceof CipherNone)) {
             throw new IllegalStateException("Interactive channels are not supported with none cipher");
@@ -382,153 +228,6 @@ public class ClientSessionImpl extends AbstractSession implements ClientSession
     }
 
     @Override
-    public ChannelExec createExecChannel(String command) throws IOException {
-        ChannelExec channel = new ChannelExec(command);
-        ConnectionService service = getConnectionService();
-        int id = service.registerChannel(channel);
-        if (log.isDebugEnabled()) {
-            log.debug("createExecChannel({})[{}] created id={}", this, command, id);
-        }
-        return channel;
-    }
-
-    @Override
-    public ChannelSubsystem createSubsystemChannel(String subsystem) throws IOException {
-        ChannelSubsystem channel = new ChannelSubsystem(subsystem);
-        ConnectionService service = getConnectionService();
-        int id = service.registerChannel(channel);
-        if (log.isDebugEnabled()) {
-            log.debug("createSubsystemChannel({})[{}] created id={}", this, subsystem, id);
-        }
-        return channel;
-    }
-
-    @Override
-    public ChannelDirectTcpip createDirectTcpipChannel(SshdSocketAddress local, SshdSocketAddress remote) throws IOException {
-        ChannelDirectTcpip channel = new ChannelDirectTcpip(local, remote);
-        ConnectionService service = getConnectionService();
-        int id = service.registerChannel(channel);
-        if (log.isDebugEnabled()) {
-            log.debug("createDirectTcpipChannel({})[{} => {}] created id={}", this, local, remote, id);
-        }
-        return channel;
-    }
-
-    private ClientUserAuthService getUserAuthService() {
-        return getService(ClientUserAuthService.class);
-    }
-
-    private ConnectionService getConnectionService() {
-        return getService(ConnectionService.class);
-    }
-
-    @Override
-    public ScpTransferEventListener getScpTransferEventListener() {
-        return scpListener;
-    }
-
-    @Override
-    public void setScpTransferEventListener(ScpTransferEventListener listener) {
-        scpListener = listener;
-    }
-
-    @Override
-    public ScpClient createScpClient() {
-        return createScpClient(getScpTransferEventListener());
-    }
-
-    @Override
-    public ScpClient createScpClient(ScpTransferEventListener listener) {
-        return new DefaultScpClient(this, listener);
-    }
-
-    @Override   // TODO make this a default method in JDK-8
-    public SftpClient createSftpClient() throws IOException {
-        return createSftpClient(SftpVersionSelector.CURRENT);
-    }
-
-    @Override   // TODO make this a default method in JDK-8
-    public SftpClient createSftpClient(final int version) throws IOException {
-        return createSftpClient(SftpVersionSelector.Utils.fixedVersionSelector(version));
-    }
-
-    @Override
-    public SftpClient createSftpClient(SftpVersionSelector selector) throws IOException {
-        DefaultSftpClient client = new DefaultSftpClient(this);
-        client.negotiateVersion(selector);
-        return client;
-    }
-
-    @Override
-    public FileSystem createSftpFileSystem() throws IOException {
-        return createSftpFileSystem(SftpVersionSelector.CURRENT);
-    }
-
-    @Override
-    public FileSystem createSftpFileSystem(int version) throws IOException {
-        return createSftpFileSystem(SftpVersionSelector.Utils.fixedVersionSelector(version));
-    }
-
-    @Override
-    public FileSystem createSftpFileSystem(SftpVersionSelector selector) throws IOException {
-        return createSftpFileSystem(selector, SftpClient.DEFAULT_READ_BUFFER_SIZE, SftpClient.DEFAULT_WRITE_BUFFER_SIZE);
-    }
-
-    @Override
-    public FileSystem createSftpFileSystem(int version, int readBufferSize, int writeBufferSize) throws IOException {
-        return createSftpFileSystem(SftpVersionSelector.Utils.fixedVersionSelector(version), readBufferSize, writeBufferSize);
-    }
-
-    @Override
-    public FileSystem createSftpFileSystem(int readBufferSize, int writeBufferSize) throws IOException {
-        return createSftpFileSystem(SftpVersionSelector.CURRENT, readBufferSize, writeBufferSize);
-    }
-
-    @Override
-    public FileSystem createSftpFileSystem(SftpVersionSelector selector, int readBufferSize, int writeBufferSize) throws IOException {
-        SftpFileSystemProvider provider = new SftpFileSystemProvider((org.apache.sshd.client.SshClient) getFactoryManager(), selector);
-        SftpFileSystem fs = provider.newFileSystem(this);
-        fs.setReadBufferSize(readBufferSize);
-        fs.setWriteBufferSize(writeBufferSize);
-        return fs;
-    }
-
-    @Override
-    public SshdSocketAddress startLocalPortForwarding(SshdSocketAddress local, SshdSocketAddress remote) throws IOException {
-        return getTcpipForwarder().startLocalPortForwarding(local, remote);
-    }
-
-    @Override
-    public void stopLocalPortForwarding(SshdSocketAddress local) throws IOException {
-        getTcpipForwarder().stopLocalPortForwarding(local);
-    }
-
-    @Override
-    public SshdSocketAddress startRemotePortForwarding(SshdSocketAddress remote, SshdSocketAddress local) throws IOException {
-        return getTcpipForwarder().startRemotePortForwarding(remote, local);
-    }
-
-    @Override
-    public void stopRemotePortForwarding(SshdSocketAddress remote) throws IOException {
-        getTcpipForwarder().stopRemotePortForwarding(remote);
-    }
-
-    @Override
-    public SshdSocketAddress startDynamicPortForwarding(SshdSocketAddress local) throws IOException {
-        return getTcpipForwarder().startDynamicPortForwarding(local);
-    }
-
-    @Override
-    public void stopDynamicPortForwarding(SshdSocketAddress local) throws IOException {
-        getTcpipForwarder().stopDynamicPortForwarding(local);
-    }
-
-    protected TcpipForwarder getTcpipForwarder() {
-        ConnectionService service = ValidateUtils.checkNotNull(getConnectionService(), "No connection service");
-        return ValidateUtils.checkNotNull(service.getTcpipForwarder(), "No forwarder");
-    }
-
-    @Override
     protected void handleMessage(Buffer buffer) throws Exception {
         synchronized (lock) {
             super.handleMessage(buffer);
@@ -639,12 +338,6 @@ public class ClientSessionImpl extends AbstractSession implements ClientSession
     }
 
     @Override
-    protected String resolveAvailableSignaturesProposal(FactoryManager manager) {
-        // the client does not have to provide keys for the available signatures
-        return NamedResource.Utils.getNames(manager.getSignatureFactories());
-    }
-
-    @Override
     protected void receiveKexInit(Map<KexProposalOption, String> proposal, byte[] seed) throws IOException {
         mergeProposals(serverProposal, proposal);
         i_s = seed;
@@ -652,8 +345,7 @@ public class ClientSessionImpl extends AbstractSession implements ClientSession
 
     @Override
     protected void checkKeys() throws SshException {
-        ClientFactoryManager manager = getFactoryManager();
-        ServerKeyVerifier serverKeyVerifier = manager.getServerKeyVerifier();
+        ServerKeyVerifier serverKeyVerifier = ValidateUtils.checkNotNull(getServerKeyVerifier(), "No server key verifier");
         SocketAddress remoteAddress = ioSession.getRemoteAddress();
 
         if (!serverKeyVerifier.verifyServerKey(this, remoteAddress, kex.getServerKey())) {
@@ -692,11 +384,6 @@ public class ClientSessionImpl extends AbstractSession implements ClientSession
     }
 
     @Override
-    public void startService(String name) throws Exception {
-        throw new IllegalStateException("Starting services is not supported on the client side: " + name);
-    }
-
-    @Override
     public Map<Object, Object> getMetadataMap() {
         return metadataMap;
     }