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 2016/01/05 07:05:48 UTC

[3/3] mina-sshd git commit: [SSHD-620] Add support for 'hostbased' authentication

[SSHD-620] Add support for 'hostbased' authentication


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

Branch: refs/heads/master
Commit: 18335089d97a76c1aec74b4c082f9a3a2bfb3517
Parents: cbff09d
Author: Lyor Goldstein <lg...@vmware.com>
Authored: Tue Jan 5 08:05:18 2016 +0200
Committer: Lyor Goldstein <lg...@vmware.com>
Committed: Tue Jan 5 08:05:18 2016 +0200

----------------------------------------------------------------------
 .../client/ClientAuthenticationManager.java     |   4 +-
 .../java/org/apache/sshd/client/SshClient.java  |  13 +-
 .../auth/AuthenticationIdentitiesProvider.java  |   1 +
 .../client/auth/PasswordIdentityProvider.java   | 146 ---------
 .../auth/UserAuthKeyboardInteractive.java       | 304 ------------------
 .../UserAuthKeyboardInteractiveFactory.java     |  36 ---
 .../sshd/client/auth/UserAuthPassword.java      | 135 --------
 .../client/auth/UserAuthPasswordFactory.java    |  36 ---
 .../sshd/client/auth/UserAuthPublicKey.java     | 179 -----------
 .../client/auth/UserAuthPublicKeyFactory.java   |  72 -----
 .../sshd/client/auth/UserInteraction.java       | 110 -------
 .../auth/hostbased/HostKeyIdentityProvider.java |  93 ++++++
 .../auth/hostbased/UserAuthHostBased.java       | 202 ++++++++++++
 .../hostbased/UserAuthHostBasedFactory.java     |  86 ++++++
 .../keyboard/UserAuthKeyboardInteractive.java   | 306 +++++++++++++++++++
 .../UserAuthKeyboardInteractiveFactory.java     |  38 +++
 .../client/auth/keyboard/UserInteraction.java   | 110 +++++++
 .../auth/password/PasswordIdentityProvider.java | 146 +++++++++
 .../client/auth/password/UserAuthPassword.java  | 137 +++++++++
 .../auth/password/UserAuthPasswordFactory.java  |  38 +++
 .../client/auth/pubkey/UserAuthPublicKey.java   | 178 +++++++++++
 .../auth/pubkey/UserAuthPublicKeyFactory.java   |  73 +++++
 .../client/session/AbstractClientSession.java   |   4 +-
 .../client/session/ClientUserAuthService.java   |   2 +-
 .../apache/sshd/common/SshdSocketAddress.java   | 299 ++++++++++++++++++
 .../sshd/common/auth/UserAuthMethodFactory.java |   5 +
 .../apache/sshd/common/util/NumberUtils.java    |  32 +-
 .../apache/sshd/common/util/SecurityUtils.java  |  12 +
 .../server/ServerAuthenticationManager.java     |  23 +-
 .../java/org/apache/sshd/server/SshServer.java  |  12 +
 .../auth/UserAuthKeyboardInteractive.java       | 116 -------
 .../UserAuthKeyboardInteractiveFactory.java     |  36 ---
 .../sshd/server/auth/UserAuthNoneFactory.java   |   2 +-
 .../sshd/server/auth/UserAuthPassword.java      | 142 ---------
 .../server/auth/UserAuthPasswordFactory.java    |  36 ---
 .../sshd/server/auth/UserAuthPublicKey.java     | 161 ----------
 .../server/auth/UserAuthPublicKeyFactory.java   |  72 -----
 .../AcceptAllHostBasedAuthenticator.java        |  31 ++
 .../auth/hostbased/HostBasedAuthenticator.java  |  45 +++
 .../RejectAllHostBasedAuthenticator.java        |  31 ++
 .../hostbased/StaticHostBasedAuthenticator.java |  72 +++++
 .../auth/hostbased/UserAuthHostBased.java       | 173 +++++++++++
 .../hostbased/UserAuthHostBasedFactory.java     |  74 +++++
 .../keyboard/UserAuthKeyboardInteractive.java   | 115 +++++++
 .../UserAuthKeyboardInteractiveFactory.java     |  38 +++
 .../server/auth/password/UserAuthPassword.java  | 141 +++++++++
 .../auth/password/UserAuthPasswordFactory.java  |  38 +++
 .../server/auth/pubkey/UserAuthPublicKey.java   | 166 ++++++++++
 .../auth/pubkey/UserAuthPublicKeyFactory.java   |  73 +++++
 .../server/session/AbstractServerSession.java   |  12 +
 .../apache/sshd/SinglePublicKeyAuthTest.java    |   2 +-
 .../java/org/apache/sshd/WelcomeBannerTest.java |   2 +-
 .../client/ClientAuthenticationManagerTest.java |   5 +-
 .../sshd/client/ClientSessionListenerTest.java  |   2 +-
 .../java/org/apache/sshd/client/ClientTest.java |  35 +--
 .../auth/PasswordIdentityProviderTest.java      |   1 +
 .../sshd/common/auth/AuthenticationTest.java    |  73 ++++-
 .../deprecated/ClientUserAuthServiceOld.java    |   2 +-
 .../apache/sshd/deprecated/UserAuthAgent.java   |   2 +-
 .../deprecated/UserAuthKeyboardInteractive.java |   2 +-
 .../sshd/deprecated/UserAuthPublicKey.java      |   2 +-
 .../java/org/apache/sshd/server/ServerTest.java |   2 +-
 62 files changed, 2887 insertions(+), 1649 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/18335089/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
index ab1f154..c48701b 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/ClientAuthenticationManager.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/ClientAuthenticationManager.java
@@ -23,9 +23,9 @@ import java.security.KeyPair;
 import java.util.List;
 
 import org.apache.sshd.client.auth.AuthenticationIdentitiesProvider;
-import org.apache.sshd.client.auth.PasswordIdentityProvider;
 import org.apache.sshd.client.auth.UserAuth;
-import org.apache.sshd.client.auth.UserInteraction;
+import org.apache.sshd.client.auth.keyboard.UserInteraction;
+import org.apache.sshd.client.auth.password.PasswordIdentityProvider;
 import org.apache.sshd.client.keyverifier.ServerKeyVerifier;
 import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.keyprovider.KeyPairProviderHolder;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/18335089/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 e8da138..8375c89 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
@@ -56,12 +56,12 @@ import java.util.logging.Logger;
 
 import org.apache.sshd.agent.SshAgentFactory;
 import org.apache.sshd.client.auth.AuthenticationIdentitiesProvider;
-import org.apache.sshd.client.auth.PasswordIdentityProvider;
 import org.apache.sshd.client.auth.UserAuth;
-import org.apache.sshd.client.auth.UserAuthKeyboardInteractiveFactory;
-import org.apache.sshd.client.auth.UserAuthPasswordFactory;
-import org.apache.sshd.client.auth.UserAuthPublicKeyFactory;
-import org.apache.sshd.client.auth.UserInteraction;
+import org.apache.sshd.client.auth.keyboard.UserAuthKeyboardInteractiveFactory;
+import org.apache.sshd.client.auth.keyboard.UserInteraction;
+import org.apache.sshd.client.auth.password.PasswordIdentityProvider;
+import org.apache.sshd.client.auth.password.UserAuthPasswordFactory;
+import org.apache.sshd.client.auth.pubkey.UserAuthPublicKeyFactory;
 import org.apache.sshd.client.channel.ChannelShell;
 import org.apache.sshd.client.channel.ClientChannel;
 import org.apache.sshd.client.config.hosts.HostConfigEntry;
@@ -1029,6 +1029,7 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
                     break;
                 }
 
+                i++;
                 continue;
             }
 
@@ -1109,7 +1110,7 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
 
                 try {
                     if (socksPort >= 0) {
-                        session.startDynamicPortForwarding(new SshdSocketAddress("localhost", socksPort));
+                        session.startDynamicPortForwarding(new SshdSocketAddress(SshdSocketAddress.LOCALHOST_NAME, socksPort));
                         Thread.sleep(Long.MAX_VALUE);
                     } else {
                         ClientChannel channel;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/18335089/sshd-core/src/main/java/org/apache/sshd/client/auth/AuthenticationIdentitiesProvider.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/AuthenticationIdentitiesProvider.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/AuthenticationIdentitiesProvider.java
index 2d0637b..56ef102 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/AuthenticationIdentitiesProvider.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/auth/AuthenticationIdentitiesProvider.java
@@ -27,6 +27,7 @@ import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
 
+import org.apache.sshd.client.auth.password.PasswordIdentityProvider;
 import org.apache.sshd.common.config.keys.KeyUtils;
 import org.apache.sshd.common.keyprovider.KeyIdentityProvider;
 import org.apache.sshd.common.util.GenericUtils;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/18335089/sshd-core/src/main/java/org/apache/sshd/client/auth/PasswordIdentityProvider.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/PasswordIdentityProvider.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/PasswordIdentityProvider.java
deleted file mode 100644
index 26811db..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/PasswordIdentityProvider.java
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.client.auth;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Iterator;
-
-import org.apache.sshd.client.session.ClientSession;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.Supplier;
-import org.apache.sshd.common.util.Transformer;
-import org.apache.sshd.common.util.ValidateUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface PasswordIdentityProvider {
-    PasswordIdentityProvider EMPTY_PASSWORDS_PROVIDER = new PasswordIdentityProvider() {
-        @Override
-        public Iterable<String> loadPasswords() {
-            return Collections.emptyList();
-        }
-
-        @Override
-        public String toString() {
-            return "EMPTY";
-        }
-    };
-
-    /**
-     * @return The currently available passwords - never {@code null}
-     */
-    Iterable<String> loadPasswords();
-
-    /**
-     * A helper class for password identity provider related operations
-     * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
-     */
-    // CHECKSTYLE:OFF
-    final class Utils {
-    // CHECKSTYLE:ON
-        public static final Transformer<PasswordIdentityProvider, Iterable<String>> LOADER =
-            new Transformer<PasswordIdentityProvider, Iterable<String>>() {
-                @Override
-                public Iterable<String> transform(PasswordIdentityProvider p) {
-                    return (p == null) ? null : p.loadPasswords();
-                }
-            };
-
-        private Utils() {
-            throw new UnsupportedOperationException("No instance allowed");
-        }
-
-        public static Iterator<String> iteratorOf(ClientSession session) {
-            ValidateUtils.checkNotNull(session, "No session");
-            return iteratorOf(session.getRegisteredIdentities(), session.getPasswordIdentityProvider());
-        }
-
-        public static Iterator<String> iteratorOf(PasswordIdentityProvider identities, PasswordIdentityProvider passwords) {
-            return iteratorOf(resolvePasswordIdentityProvider(identities, passwords));
-        }
-
-        /**
-         * Resolves a non-{@code null} iterator of the available passwords
-         *
-         * @param provider The {@link PasswordIdentityProvider} - ignored if {@code null}
-         * @return A non-{@code null} iterator - which may be empty if no provider or no passwords
-         */
-        public static Iterator<String> iteratorOf(PasswordIdentityProvider provider) {
-            return GenericUtils.iteratorOf((provider == null) ? null : provider.loadPasswords());
-        }
-
-        public static PasswordIdentityProvider resolvePasswordIdentityProvider(PasswordIdentityProvider identities, PasswordIdentityProvider passwords) {
-            if ((passwords == null) || (identities == passwords)) {
-                return identities;
-            } else if (identities == null) {
-                return passwords;
-            } else {
-                return multiProvider(identities, passwords);
-            }
-        }
-
-        public static PasswordIdentityProvider multiProvider(PasswordIdentityProvider ... providers) {
-            return multiProvider(GenericUtils.isEmpty(providers) ? Collections.<PasswordIdentityProvider>emptyList() : Arrays.asList(providers));
-        }
-
-        public static PasswordIdentityProvider multiProvider(Collection<? extends PasswordIdentityProvider> providers) {
-            return wrap(iterableOf(providers));
-        }
-
-        public static Iterable<String> iterableOf(Collection<? extends PasswordIdentityProvider> providers) {
-            if (GenericUtils.isEmpty(providers)) {
-                return Collections.emptyList();
-            }
-
-            Collection<Supplier<Iterable<String>>> suppliers = new ArrayList<Supplier<Iterable<String>>>(providers.size());
-            for (final PasswordIdentityProvider p : providers) {
-                if (p == null) {
-                    continue;
-                }
-
-                suppliers.add(new Supplier<Iterable<String>>() {
-                    @Override
-                    public Iterable<String> get() {
-                        return p.loadPasswords();
-                    }
-                });
-            }
-
-            if (GenericUtils.isEmpty(suppliers)) {
-                return Collections.emptyList();
-            }
-
-            return GenericUtils.multiIterableSuppliers(suppliers);
-        }
-
-        public static PasswordIdentityProvider wrap(final Iterable<String> passwords) {
-            return new PasswordIdentityProvider() {
-                @Override
-                public Iterable<String> loadPasswords() {
-                    return passwords;
-                }
-            };
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/18335089/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
deleted file mode 100644
index 9327236..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthKeyboardInteractive.java
+++ /dev/null
@@ -1,304 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sshd.client.auth;
-
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
-
-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;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.buffer.Buffer;
-import org.apache.sshd.common.util.buffer.BufferUtils;
-
-/**
- * Manages a &quot;keyboard-interactive&quot; exchange according to
- * <A HREF="https://www.ietf.org/rfc/rfc4256.txt">RFC4256</A>
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class UserAuthKeyboardInteractive extends AbstractUserAuth {
-    public static final String NAME = UserAuthKeyboardInteractiveFactory.NAME;
-
-    public static final String INTERACTIVE_LANGUAGE_TAG = "kb-client-interactive-language-tag";
-
-    /*
-     * As per RFC-4256:
-     *
-     *      The language tag is deprecated and SHOULD be the empty string.  It
-     *      may be removed in a future revision of this specification.  Instead,
-     *      the server SHOULD select the language to be used based on the tags
-     *      communicated during key exchange
-     */
-    public static final String DEFAULT_INTERACTIVE_LANGUAGE_TAG = "";
-
-    public static final String INTERACTIVE_SUBMETHODS = "kb-client-interactive-sub-methods";
-
-    /*
-     * As per RFC-4256:
-     *
-     *      The submethods field is included so the user can give a hint of which
-     *      actual methods he wants to use.  It is a comma-separated list of
-     *      authentication submethods (software or hardware) that the user
-     *      prefers.  If the client has knowledge of the submethods preferred by
-     *      the user, presumably through a configuration setting, it MAY use the
-     *      submethods field to pass this information to the server.  Otherwise,
-     *      it MUST send the empty string.
-     *
-     *      The actual names of the submethods is something the user and the
-     *      server need to agree upon.
-     *
-     *      Server interpretation of the submethods field is implementation-
-     *      dependent.
-     */
-    public static final String DEFAULT_INTERACTIVE_SUBMETHODS = "";
-
-    private final AtomicBoolean requestPending = new AtomicBoolean(false);
-    private final AtomicInteger trialsCount = new AtomicInteger(0);
-    private Iterator<String> passwords;
-    private int maxTrials;
-
-    public UserAuthKeyboardInteractive() {
-        super(NAME);
-    }
-
-    @Override
-    public void init(ClientSession session, String service) throws Exception {
-        super.init(session, service);
-        passwords = PasswordIdentityProvider.Utils.iteratorOf(session);
-        maxTrials = PropertyResolverUtils.getIntProperty(session, ClientAuthenticationManager.PASSWORD_PROMPTS, ClientAuthenticationManager.DEFAULT_PASSWORD_PROMPTS);
-        ValidateUtils.checkTrue(maxTrials > 0, "Non-positive max. trials: %d", maxTrials);
-    }
-
-    @Override
-    protected boolean sendAuthDataRequest(ClientSession session, String service) throws Exception {
-        String name = getName();
-        if (requestPending.get()) {
-            if (log.isDebugEnabled()) {
-                log.debug("sendAuthDataRequest({})[{}] no reply for previous request for {}",
-                          session, service, name);
-            }
-            return false;
-        }
-
-        if (!verifyTrialsCount(session, service, SshConstants.SSH_MSG_USERAUTH_REQUEST, trialsCount.get(), maxTrials)) {
-            return false;
-        }
-
-        String username = session.getUsername();
-        String lang = getExchangeLanguageTag(session);
-        String subMethods = getExchangeSubMethods(session);
-        if (log.isDebugEnabled()) {
-            log.debug("sendAuthDataRequest({})[{}] send SSH_MSG_USERAUTH_REQUEST for {}: lang={}, methods={}",
-                      session, service, name, lang, subMethods);
-        }
-
-        Buffer buffer = session.createBuffer(SshConstants.SSH_MSG_USERAUTH_REQUEST,
-                            username.length() + service.length() + name.length()
-                          + GenericUtils.length(lang) + GenericUtils.length(subMethods)
-                          + Long.SIZE /* a bit extra for the lengths */);
-        buffer.putString(username);
-        buffer.putString(service);
-        buffer.putString(name);
-        buffer.putString(lang);
-        buffer.putString(subMethods);
-        requestPending.set(true);
-        session.writePacket(buffer);
-        return true;
-    }
-
-    @Override
-    protected boolean processAuthDataRequest(ClientSession session, String service, Buffer buffer) throws Exception {
-        int cmd = buffer.getUByte();
-        if (cmd != SshConstants.SSH_MSG_USERAUTH_INFO_REQUEST) {
-            throw new IllegalStateException("processAuthDataRequest(" + session + ")[" + service + "]"
-                            + " received unknown packet: cmd=" + SshConstants.getCommandMessageName(cmd));
-        }
-
-        requestPending.set(false);
-
-        if (!verifyTrialsCount(session, service, cmd, trialsCount.incrementAndGet(), maxTrials)) {
-            return false;
-        }
-
-        String name = buffer.getString();
-        String instruction = buffer.getString();
-        String lang = buffer.getString();
-        int num = buffer.getInt();
-        if (log.isDebugEnabled()) {
-            log.debug("processAuthDataRequest({})[{}] SSH_MSG_USERAUTH_INFO_REQUEST name={}, instruction={}, language={}, num-prompts={}",
-                      session, service, name, instruction, lang, num);
-        }
-
-        String[] prompt = new String[num];
-        boolean[] echo = new boolean[num];
-        for (int i = 0; i < num; i++) {
-            // according to RFC4256: "The prompt field(s) MUST NOT be empty strings."
-            prompt[i] = buffer.getString();
-            echo[i] = buffer.getBoolean();
-        }
-
-        if (log.isTraceEnabled()) {
-            log.trace("processAuthDataRequest({})[{}] Prompt: {}", session, service, Arrays.toString(prompt));
-            log.trace("processAuthDataRequest({})[{}] Echo: {}", session, service, Arrays.toString(echo));
-        }
-
-        String[] rep = getUserResponses(name, instruction, lang, prompt, echo);
-        if (rep == null) {
-            if (log.isDebugEnabled()) {
-                log.debug("processAuthDataRequest({})[{}] no responses for {}", session, service, name);
-            }
-            return false;
-        }
-
-        /*
-         * According to RFC4256:
-         *
-         *      If the num-responses field does not match the num-prompts
-         *      field in the request message, the server MUST send a failure
-         *      message.
-         *
-         * However it is the server's (!) responsibility to fail, so we only warn...
-         */
-        if (num != rep.length) {
-            log.warn("processAuthDataRequest({})[{}] Mismatched prompts ({}) vs. responses count ({})",
-                     session, service, num, rep.length);
-        }
-
-        buffer = session.prepareBuffer(SshConstants.SSH_MSG_USERAUTH_INFO_RESPONSE, BufferUtils.clear(buffer));
-        buffer.putInt(rep.length);
-        for (int index = 0; index < rep.length; index++) {
-            String r = rep[index];
-            if (log.isTraceEnabled()) {
-                log.trace("processAuthDataRequest({})[{}] response #{}: {}", session, service, index + 1, r);
-            }
-            buffer.putString(r);
-        }
-
-        session.writePacket(buffer);
-        return true;
-    }
-
-    protected String getExchangeLanguageTag(ClientSession session) {
-        return PropertyResolverUtils.getStringProperty(session, INTERACTIVE_LANGUAGE_TAG, DEFAULT_INTERACTIVE_LANGUAGE_TAG);
-    }
-
-    protected String getExchangeSubMethods(ClientSession session) {
-        return PropertyResolverUtils.getStringProperty(session, INTERACTIVE_SUBMETHODS, DEFAULT_INTERACTIVE_SUBMETHODS);
-    }
-
-    protected String getCurrentPasswordCandidate() {
-        if ((passwords != null) && passwords.hasNext()) {
-            return passwords.next();
-        } else {
-            return null;
-        }
-    }
-
-    protected boolean verifyTrialsCount(ClientSession session, String service, int cmd, int nbTrials, int maxAllowed) {
-        if (log.isDebugEnabled()) {
-            log.debug("verifyTrialsCount({})[{}] cmd={} - {} out of {}",
-                      session, service, getAuthCommandName(cmd), nbTrials, maxAllowed);
-        }
-
-        return nbTrials <= maxAllowed;
-    }
-
-    /**
-     * @param name        The interaction name - may be empty
-     * @param instruction The instruction - may be empty
-     * @param lang        The language tag - may be empty
-     * @param prompt      The prompts - may be empty
-     * @param echo        Whether to echo the response for the prompt or not - same
-     *                    length as the prompts
-     * @return The response for each prompt - if {@code null} then the assumption
-     * is that some internal error occurred and no response is sent. <B>Note:</B>
-     * according to <A HREF="https://www.ietf.org/rfc/rfc4256.txt">RFC4256</A>
-     * the number of responses should be <U>exactly</U> the same as the number
-     * of prompts. However, since it is the <U>server's</U> responsibility to
-     * enforce this we do not validate the response (other than logging it as
-     * a warning...)
-     */
-    protected String[] getUserResponses(String name, String instruction, String lang, String[] prompt, boolean[] echo) {
-        ClientSession session = getClientSession();
-        int num = GenericUtils.length(prompt);
-        if (num == 0) {
-            if (log.isDebugEnabled()) {
-                log.debug("getUserResponses({}) no prompts for interaction={}", session, name);
-            }
-            return GenericUtils.EMPTY_STRING_ARRAY;
-        }
-
-        String candidate = getCurrentPasswordCandidate();
-        if (useCurrentPassword(candidate, name, instruction, lang, prompt, echo)) {
-            if (log.isDebugEnabled()) {
-                log.debug("getUserResponses({}) use password candidate for interaction={}", session, name);
-            }
-            return new String[]{candidate};
-        }
-
-        UserInteraction ui = session.getUserInteraction();
-        if ((ui != null) && ui.isInteractionAllowed(session)) {
-            return ui.interactive(session, name, instruction, lang, prompt, echo);
-        }
-
-        if (log.isDebugEnabled()) {
-            log.debug("getUserResponses({}) no user interaction for name={}", session, name);
-        }
-
-        return null;
-    }
-
-    protected boolean useCurrentPassword(String password, String name, String instruction, String lang, String[] prompt, boolean[] echo) {
-        int num = GenericUtils.length(prompt);
-        if ((num != 1) || (password == null) || echo[0]) {
-            return false;
-        }
-
-        // check that prompt is something like "XXX password YYY:"
-        String value = GenericUtils.trimToEmpty(prompt[0]).toLowerCase();
-        int passPos = value.lastIndexOf("password");
-        if (passPos < 0) {  // no password keyword in prompt
-            return false;
-        }
-
-        int sepPos = value.lastIndexOf(':');
-        if (sepPos <= passPos) {    // no prompt separator or separator before the password keyword
-            return false;
-        }
-
-        return true;
-    }
-
-    public static String getAuthCommandName(int cmd) {
-        switch(cmd) {
-            case SshConstants.SSH_MSG_USERAUTH_REQUEST:
-                return "SSH_MSG_USERAUTH_REQUEST";
-            case SshConstants.SSH_MSG_USERAUTH_INFO_REQUEST:
-                return "SSH_MSG_USERAUTH_INFO_REQUEST";
-            default:
-                return SshConstants.getCommandMessageName(cmd);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/18335089/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthKeyboardInteractiveFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthKeyboardInteractiveFactory.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthKeyboardInteractiveFactory.java
deleted file mode 100644
index 5cf1068..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthKeyboardInteractiveFactory.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sshd.client.auth;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class UserAuthKeyboardInteractiveFactory extends AbstractUserAuthFactory {
-    public static final String NAME = KB_INTERACTIVE;
-    public static final UserAuthKeyboardInteractiveFactory INSTANCE = new UserAuthKeyboardInteractiveFactory();
-
-    public UserAuthKeyboardInteractiveFactory() {
-        super(NAME);
-    }
-
-    @Override
-    public UserAuth create() {
-        return new UserAuthKeyboardInteractive();
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/18335089/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
deleted file mode 100644
index d4ed6f7..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPassword.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sshd.client.auth;
-
-import java.io.IOException;
-import java.util.Iterator;
-import java.util.Objects;
-
-import org.apache.sshd.client.session.ClientSession;
-import org.apache.sshd.common.SshConstants;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.buffer.Buffer;
-import org.apache.sshd.common.util.buffer.BufferUtils;
-
-/**
- * Implements the &quot;password&quot; authentication mechanism
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class UserAuthPassword extends AbstractUserAuth {
-    public static final String NAME = UserAuthPasswordFactory.NAME;
-
-    private Iterator<String> passwords;
-    private String current;
-
-    public UserAuthPassword() {
-        super(NAME);
-    }
-
-    @Override
-    public void init(ClientSession session, String service) throws Exception {
-        super.init(session, service);
-        passwords = PasswordIdentityProvider.Utils.iteratorOf(session);
-    }
-
-    @Override
-    protected boolean sendAuthDataRequest(ClientSession session, String service) throws Exception {
-        if ((passwords == null) || (!passwords.hasNext())) {
-            if (log.isDebugEnabled()) {
-                log.debug("sendAuthDataRequest({})[{}] no more passwords to send", session, service);
-            }
-
-            return false;
-        }
-
-        current = passwords.next();
-        String username = session.getUsername();
-        Buffer buffer = session.createBuffer(SshConstants.SSH_MSG_USERAUTH_REQUEST,
-                            username.length() + service.length() + getName().length() + current.length() + Integer.SIZE);
-        sendPassword(buffer, session, current, current);
-        return true;
-    }
-
-    @Override
-    protected boolean processAuthDataRequest(ClientSession session, String service, Buffer buffer) throws Exception {
-        int cmd = buffer.getUByte();
-        if (cmd != SshConstants.SSH_MSG_USERAUTH_PASSWD_CHANGEREQ) {
-            throw new IllegalStateException("processAuthDataRequest(" + session + ")[" + service + "]"
-                            + " received unknown packet: cmd=" + SshConstants.getCommandMessageName(cmd));
-        }
-
-        String prompt = buffer.getString();
-        String lang = buffer.getString();
-        UserInteraction ui = session.getUserInteraction();
-        if ((ui != null) && ui.isInteractionAllowed(session)) {
-            String password = ui.getUpdatedPassword(session, prompt, lang);
-            if (GenericUtils.isEmpty(password)) {
-                if (log.isDebugEnabled()) {
-                    log.debug("processAuthDataRequest({})[{}] No updated password for prompt={}, lang={}",
-                              session, service, prompt, lang);
-                }
-            } else {
-                sendPassword(buffer, session, password, password);
-                return true;
-            }
-        } else {
-            if (log.isDebugEnabled()) {
-                log.debug("processAuthDataRequest({})[{}] no UI for password change request for prompt={}, lang={}",
-                          session, service, prompt, lang);
-            }
-        }
-
-        return false;
-    }
-
-    /**
-     * Sends the password via a {@code SSH_MSG_USERAUTH_REQUEST} message.
-     * If old and new password are not the same then it requests a password
-     * modification from the server (which may be denied if the server does
-     * not support this feature).
-     *
-     * @param buffer The {@link Buffer} to re-use for sending the message
-     * @param session The target {@link ClientSession}
-     * @param oldPassword The previous password
-     * @param newPassword The new password
-     * @throws IOException If failed to send the message.
-     */
-    protected void sendPassword(Buffer buffer, ClientSession session, String oldPassword, String newPassword) throws IOException {
-        String username = session.getUsername();
-        String service = getService();
-        String name = getName();
-        boolean modified = !Objects.equals(oldPassword, newPassword);
-        if (log.isDebugEnabled()) {
-            log.debug("sendPassword({})[{}] send SSH_MSG_USERAUTH_REQUEST for {} - modified={}",
-                      session, service, name, modified);
-        }
-
-        buffer = session.prepareBuffer(SshConstants.SSH_MSG_USERAUTH_REQUEST, BufferUtils.clear(buffer));
-        buffer.putString(username);
-        buffer.putString(service);
-        buffer.putString(name);
-        buffer.putBoolean(modified);
-        // see RFC-4252 section 8
-        buffer.putString(oldPassword);
-        if (modified) {
-            buffer.putString(newPassword);
-        }
-        session.writePacket(buffer);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/18335089/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPasswordFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPasswordFactory.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPasswordFactory.java
deleted file mode 100644
index 14ecfb0..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPasswordFactory.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sshd.client.auth;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class UserAuthPasswordFactory extends AbstractUserAuthFactory {
-    public static final String NAME = PASSWORD;
-    public static final UserAuthPasswordFactory INSTANCE = new UserAuthPasswordFactory();
-
-    public UserAuthPasswordFactory() {
-        super(NAME);
-    }
-
-    @Override
-    public UserAuth create() {
-        return new UserAuthPassword();
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/18335089/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
deleted file mode 100644
index f800cad..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPublicKey.java
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sshd.client.auth;
-
-import java.io.Closeable;
-import java.io.IOException;
-import java.security.PublicKey;
-import java.util.Iterator;
-import java.util.List;
-
-import org.apache.sshd.client.auth.pubkey.PublicKeyIdentity;
-import org.apache.sshd.client.auth.pubkey.UserAuthPublicKeyIterator;
-import org.apache.sshd.client.session.ClientSession;
-import org.apache.sshd.common.NamedFactory;
-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.signature.Signature;
-import org.apache.sshd.common.signature.SignatureFactoriesManager;
-import org.apache.sshd.common.util.buffer.Buffer;
-import org.apache.sshd.common.util.buffer.BufferUtils;
-import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
-
-/**
- * Implements the &quot;publickey&quot; authentication mechanism
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class UserAuthPublicKey extends AbstractUserAuth implements SignatureFactoriesManager {
-    public static final String NAME = UserAuthPublicKeyFactory.NAME;
-
-    private Iterator<PublicKeyIdentity> keys;
-    private PublicKeyIdentity current;
-    private List<NamedFactory<Signature>> factories;
-
-    public UserAuthPublicKey() {
-        this(null);
-    }
-
-    public UserAuthPublicKey(List<NamedFactory<Signature>> factories) {
-        super(NAME);
-        this.factories = factories; // OK if null/empty
-    }
-
-    @Override
-    public List<NamedFactory<Signature>> getSignatureFactories() {
-        return factories;
-    }
-
-    @Override
-    public void setSignatureFactories(List<NamedFactory<Signature>> factories) {
-        this.factories = factories;
-    }
-
-    @Override
-    public void init(ClientSession session, String service) throws Exception {
-        super.init(session, service);
-        releaseKeys();  // just making sure in case multiple calls to the method
-        keys = new UserAuthPublicKeyIterator(session, this);
-    }
-
-    @Override
-    protected boolean sendAuthDataRequest(ClientSession session, String service) throws Exception {
-        if ((keys == null) || (!keys.hasNext())) {
-            if (log.isDebugEnabled()) {
-                log.debug("sendAuthDataRequest({})[{}] no more keys to send", session, service);
-            }
-
-            return false;
-        }
-
-        current = keys.next();
-        if (log.isTraceEnabled()) {
-            log.trace("sendAuthDataRequest({})[{}] current key details: {}", session, service, current);
-        }
-
-        PublicKey key = current.getPublicKey();
-        String algo = KeyUtils.getKeyType(key);
-        String name = getName();
-        if (log.isDebugEnabled()) {
-            log.debug("sendAuthDataRequest({})[{}] send SSH_MSG_USERAUTH_REQUEST request {} type={} - fingerprint={}",
-                      session, service, name, algo, KeyUtils.getFingerPrint(key));
-        }
-        Buffer buffer = session.createBuffer(SshConstants.SSH_MSG_USERAUTH_REQUEST);
-        buffer.putString(session.getUsername());
-        buffer.putString(service);
-        buffer.putString(name);
-        buffer.putBoolean(false);
-        buffer.putString(algo);
-        buffer.putPublicKey(key);
-        session.writePacket(buffer);
-        return true;
-    }
-
-    @Override
-    protected boolean processAuthDataRequest(ClientSession session, String service, Buffer buffer) throws Exception {
-        int cmd = buffer.getUByte();
-        if (cmd != SshConstants.SSH_MSG_USERAUTH_PK_OK) {
-            throw new IllegalStateException("processAuthDataRequest(" + session + ")[" + service + "]"
-                    + " received unknown packet: cmd=" + SshConstants.getCommandMessageName(cmd));
-        }
-
-        PublicKey key = current.getPublicKey();
-        String algo = KeyUtils.getKeyType(key);
-        String name = getName();
-        if (log.isDebugEnabled()) {
-            log.debug("processAuthDataRequest({})[{}] send SSH_MSG_USERAUTH_PK_OK reply {} type={} - fingerprint={}",
-                      session, service, name, algo, KeyUtils.getFingerPrint(key));
-        }
-
-        String username = session.getUsername();
-        buffer = session.prepareBuffer(SshConstants.SSH_MSG_USERAUTH_REQUEST, BufferUtils.clear(buffer));
-        buffer.putString(username);
-        buffer.putString(service);
-        buffer.putString(name);
-        buffer.putBoolean(true);
-        buffer.putString(algo);
-        buffer.putPublicKey(key);
-
-        Buffer bs = new ByteArrayBuffer();
-        KeyExchange kex = session.getKex();
-        bs.putBytes(kex.getH());
-        bs.putByte(SshConstants.SSH_MSG_USERAUTH_REQUEST);
-        bs.putString(username);
-        bs.putString(service);
-        bs.putString(name);
-        bs.putBoolean(true);
-        bs.putString(algo);
-        bs.putPublicKey(key);
-
-        byte[] sig = current.sign(bs.getCompactData());
-        bs = new ByteArrayBuffer(algo.length() + sig.length + Long.SIZE, false);
-        bs.putString(algo);
-        bs.putBytes(sig);
-        buffer.putBytes(bs.array(), bs.rpos(), bs.available());
-
-        session.writePacket(buffer);
-        return true;
-    }
-
-    @Override
-    public void destroy() {
-        try {
-            releaseKeys();
-        } catch (IOException e) {
-            throw new RuntimeException("Failed (" + e.getClass().getSimpleName() + ") to close agent: " + e.getMessage(), e);
-        }
-
-        super.destroy(); // for logging
-    }
-
-    protected void releaseKeys() throws IOException {
-        try {
-            if (keys instanceof Closeable) {
-                if (log.isTraceEnabled()) {
-                    log.trace("releaseKeys({}) closing {}", getClientSession(), keys);
-                }
-                ((Closeable) keys).close();
-            }
-        } finally {
-            keys = null;
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/18335089/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPublicKeyFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPublicKeyFactory.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPublicKeyFactory.java
deleted file mode 100644
index f7ccd54..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPublicKeyFactory.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sshd.client.auth;
-
-import java.util.List;
-
-import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.signature.Signature;
-import org.apache.sshd.common.signature.SignatureFactoriesManager;
-import org.apache.sshd.common.util.GenericUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class UserAuthPublicKeyFactory extends AbstractUserAuthFactory implements SignatureFactoriesManager {
-    public static final String NAME = PUBLIC_KEY;
-    public static final UserAuthPublicKeyFactory INSTANCE = new UserAuthPublicKeyFactory() {
-        @Override
-        public List<NamedFactory<Signature>> getSignatureFactories() {
-            return null;
-        }
-
-        @Override
-        public void setSignatureFactories(List<NamedFactory<Signature>> factories) {
-            if (!GenericUtils.isEmpty(factories)) {
-                throw new UnsupportedOperationException("Not allowed to change default instance signature factories");
-            }
-        }
-    };
-
-    private List<NamedFactory<Signature>> factories;
-
-    public UserAuthPublicKeyFactory() {
-        this(null);
-    }
-
-    public UserAuthPublicKeyFactory(List<NamedFactory<Signature>> factories) {
-        super(NAME);
-        this.factories = factories; // OK if null/empty
-    }
-
-    @Override
-    public List<NamedFactory<Signature>> getSignatureFactories() {
-        return factories;
-    }
-
-    @Override
-    public void setSignatureFactories(List<NamedFactory<Signature>> factories) {
-        this.factories = factories;
-    }
-
-    @Override
-    public UserAuth create() {
-        return new UserAuthPublicKey(getSignatureFactories());
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/18335089/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
deleted file mode 100644
index 700055a..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/UserInteraction.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sshd.client.auth;
-
-import org.apache.sshd.client.session.ClientSession;
-
-/**
- * Interface used by the ssh client to communicate with the end user.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- * @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.
-     *
-     * @param session The {@link ClientSession} through which the banner was received
-     * @param banner  The welcome banner
-     * @param lang    The banner language code - may be empty
-     */
-    void welcome(ClientSession session, String banner, String lang);
-
-    /**
-     * Invoked when &quot;keyboard-interactive&quot; authentication mechanism
-     * is used in order to provide responses for the server's challenges
-     * (a.k.a. prompts)
-     *
-     * @param session     The {@link ClientSession} through which the request was received
-     * @param name        The interaction name (may be empty)
-     * @param instruction The instruction (may be empty)
-     * @param lang        The language for the data (may be empty)
-     * @param prompt      The prompts to be displayed (may be empty)
-     * @param echo        For each prompt whether to echo the user's response
-     * @return The replies - <B>Note:</B> the protocol states that the number
-     * of replies should be <U>exactly</U> the same as the number of prompts,
-     * however we do not enforce it since it is defined as the <U>server's</U>
-     * job to check and manage this violation.
-     */
-    String[] interactive(ClientSession session, String name, String instruction, String lang, String[] prompt, boolean[] echo);
-
-    /**
-     * Invoked when the server returns an {@code SSH_MSG_USERAUTH_PASSWD_CHANGEREQ}
-     * response indicating that the password should be changed - e.g., expired or
-     * not strong enough (as per the server's policy).
-     *
-     * @param session The {@link ClientSession} through which the request was received
-     * @param prompt The server's prompt (may be empty)
-     * @param lang The prompt's language (may be empty)
-     * @return The password to use - if {@code null}/empty then no updated
-     * password was provided - thus failing the authentication via passwords
-     * (<B>Note:</B> authentication might still succeed via some other means -
-     * be it other passwords, public keys, etc...)
-     */
-    String getUpdatedPassword(ClientSession session, String prompt, String lang);
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/18335089/sshd-core/src/main/java/org/apache/sshd/client/auth/hostbased/HostKeyIdentityProvider.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/hostbased/HostKeyIdentityProvider.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/hostbased/HostKeyIdentityProvider.java
new file mode 100644
index 0000000..7819422
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/client/auth/hostbased/HostKeyIdentityProvider.java
@@ -0,0 +1,93 @@
+/*
+ * 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.auth.hostbased;
+
+import java.security.KeyPair;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.Pair;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface HostKeyIdentityProvider {
+    /**
+     * @return The host keys as a {@link Pair} of key + certificates (which can be {@code null}/empty)
+     */
+    Iterable<Pair<KeyPair, List<X509Certificate>>> loadHostKeys();
+
+    /**
+     * A helper class for key identity provider related operations
+     * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+     */
+    // CHECKSTYLE:OFF
+    final class Utils {
+    // CHECKSTYLE:ON
+        private Utils() {
+            throw new UnsupportedOperationException("No instance allowed");
+        }
+
+        public static Iterator<Pair<KeyPair, List<X509Certificate>>> iteratorOf(HostKeyIdentityProvider provider) {
+            return GenericUtils.iteratorOf((provider == null) ? null : provider.loadHostKeys());
+        }
+
+        public static HostKeyIdentityProvider wrap(KeyPair ... pairs) {
+            return wrap(GenericUtils.isEmpty(pairs) ? Collections.<KeyPair>emptyList() : Arrays.asList(pairs));
+        }
+
+        public static HostKeyIdentityProvider wrap(final Iterable<? extends KeyPair> pairs) {
+            return new HostKeyIdentityProvider() {
+                @Override
+                public Iterable<Pair<KeyPair, List<X509Certificate>>> loadHostKeys() {
+                    return new Iterable<Pair<KeyPair, List<X509Certificate>>>() {
+                        @Override
+                        public Iterator<Pair<KeyPair, List<X509Certificate>>> iterator() {
+                            final Iterator<? extends KeyPair> iter = GenericUtils.iteratorOf(pairs);
+                            return new Iterator<Pair<KeyPair, List<X509Certificate>>>() {
+
+                                @Override
+                                public boolean hasNext() {
+                                    return iter.hasNext();
+                                }
+
+                                @Override
+                                public Pair<KeyPair, List<X509Certificate>> next() {
+                                    KeyPair kp = iter.next();
+                                    return new Pair<KeyPair, List<X509Certificate>>(kp, Collections.<X509Certificate>emptyList());
+                                }
+
+                                @Override
+                                public void remove() {
+                                    throw new UnsupportedOperationException("No removal allowed");
+                                }
+                            };
+                        }
+                    };
+                }
+
+            };
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/18335089/sshd-core/src/main/java/org/apache/sshd/client/auth/hostbased/UserAuthHostBased.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/hostbased/UserAuthHostBased.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/hostbased/UserAuthHostBased.java
new file mode 100644
index 0000000..a8313f3
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/client/auth/hostbased/UserAuthHostBased.java
@@ -0,0 +1,202 @@
+/*
+ * 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.auth.hostbased;
+
+import java.security.KeyPair;
+import java.security.PublicKey;
+import java.security.cert.X509Certificate;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.sshd.client.auth.AbstractUserAuth;
+import org.apache.sshd.client.session.ClientSession;
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.SshConstants;
+import org.apache.sshd.common.SshdSocketAddress;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.signature.Signature;
+import org.apache.sshd.common.signature.SignatureFactoriesManager;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.OsUtils;
+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;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class UserAuthHostBased extends AbstractUserAuth implements SignatureFactoriesManager {
+    public static final String NAME = UserAuthHostBasedFactory.NAME;
+
+    private Iterator<Pair<KeyPair, List<X509Certificate>>> keys;
+    private final HostKeyIdentityProvider clientHostKeys;
+    private List<NamedFactory<Signature>> factories;
+    private String clientUsername;
+    private String clientHostname;
+
+    public UserAuthHostBased(HostKeyIdentityProvider clientHostKeys) {
+        super(NAME);
+        this.clientHostKeys = clientHostKeys;   // OK if null
+    }
+
+    @Override
+    public void init(ClientSession session, String service) throws Exception {
+        super.init(session, service);
+        keys = HostKeyIdentityProvider.Utils.iteratorOf(clientHostKeys);  // in case multiple calls to the method
+    }
+
+    @Override
+    public List<NamedFactory<Signature>> getSignatureFactories() {
+        return factories;
+    }
+
+    @Override
+    public void setSignatureFactories(List<NamedFactory<Signature>> factories) {
+        this.factories = factories;
+    }
+
+    public String getClientUsername() {
+        return clientUsername;
+    }
+
+    public void setClientUsername(String clientUsername) {
+        this.clientUsername = clientUsername;
+    }
+
+    public String getClientHostname() {
+        return clientHostname;
+    }
+
+    public void setClientHostname(String clientHostname) {
+        this.clientHostname = clientHostname;
+    }
+
+    @Override
+    protected boolean sendAuthDataRequest(ClientSession session, String service) throws Exception {
+        if ((keys == null) || (!keys.hasNext())) {
+            if (log.isDebugEnabled()) {
+                log.debug("sendAuthDataRequest({})[{}] no more keys to send", session, service);
+            }
+
+            return false;
+        }
+
+        Pair<KeyPair, List<X509Certificate>> keyInfo = keys.next();
+        KeyPair kp = keyInfo.getFirst();
+        PublicKey pub = kp.getPublic();
+        String keyType = KeyUtils.getKeyType(pub);
+        if (log.isTraceEnabled()) {
+            log.trace("sendAuthDataRequest({})[{}] current key details: type={}, fingerprint={}",
+                      session, service, keyType, KeyUtils.getFingerPrint(pub));
+        }
+
+        Collection<NamedFactory<Signature>> factories =
+                ValidateUtils.checkNotNullAndNotEmpty(
+                        SignatureFactoriesManager.Utils.resolveSignatureFactories(this, session),
+                        "No signature factories for session=%s",
+                        session);
+        Signature verifier = ValidateUtils.checkNotNull(
+                NamedFactory.Utils.create(factories, keyType),
+                "No signer could be located for key type=%s",
+                keyType);
+
+        byte[] id = session.getSessionId();
+        String username = session.getUsername();
+        String clientUsername = resolveClientUsername();
+        String clientHostname = resolveClientHostname();
+        if (log.isDebugEnabled()) {
+            log.debug("sendAuthDataRequest({})[{}] client={}@{}",
+                      session, service, clientUsername, clientHostname);
+        }
+
+        Buffer buffer = session.createBuffer(SshConstants.SSH_MSG_USERAUTH_REQUEST,
+                id.length + username.length() + service.length() + clientUsername.length() + clientHostname.length()
+              + keyType.length() + ByteArrayBuffer.DEFAULT_SIZE + Long.SIZE);
+        buffer.clear();
+
+        buffer.putRawPublicKey(pub);
+
+        List<X509Certificate> certs = keyInfo.getSecond();
+        if (GenericUtils.size(certs) > 0) {
+            for (X509Certificate c : certs) {
+                // TODO make sure this yields DER encoding
+                buffer.putRawBytes(c.getEncoded());
+            }
+        }
+        byte[] keyBytes = buffer.getCompactData();
+
+        buffer.clear();
+        buffer.putBytes(id);
+        buffer.putByte(SshConstants.SSH_MSG_USERAUTH_REQUEST);
+        buffer.putString(username);
+        buffer.putString(getService());
+        buffer.putString(getName());
+        buffer.putString(keyType);
+        buffer.putBytes(keyBytes);
+        buffer.putString(clientHostname);
+        buffer.putString(clientUsername);
+
+        verifier.initSigner(kp.getPrivate());
+        verifier.update(buffer.array(), buffer.rpos(), buffer.available());
+        byte[] signature = verifier.sign();
+        if (log.isTraceEnabled()) {
+            log.trace("sendAuthDataRequest({})[{}] type={}, fingerprint={}, client={}@{}: signature={}",
+                      session, service, keyType, KeyUtils.getFingerPrint(pub),
+                      clientUsername, clientHostname, BufferUtils.printHex(signature));
+        }
+
+        buffer = session.prepareBuffer(SshConstants.SSH_MSG_USERAUTH_REQUEST, buffer);
+        buffer.putString(username);
+        buffer.putString(getService());
+        buffer.putString(getName());
+        buffer.putString(keyType);
+        buffer.putBytes(keyBytes);
+        buffer.putString(clientHostname);
+        buffer.putString(clientUsername);
+        buffer.putBytes(signature);
+
+        session.writePacket(buffer);
+        return true;
+    }
+
+    @Override
+    protected boolean processAuthDataRequest(ClientSession session, String service, Buffer buffer) throws Exception {
+        int cmd = buffer.getUByte();
+        throw new IllegalStateException("processAuthDataRequest(" + session + ")[" + service + "]"
+                    + " received unknown packet: cmd=" + SshConstants.getCommandMessageName(cmd));
+    }
+
+    protected String resolveClientUsername() {
+        String value = getClientUsername();
+        return GenericUtils.isEmpty(value) ? OsUtils.getCurrentUser() : value;
+    }
+
+    protected String resolveClientHostname() {
+        String value = getClientHostname();
+        if (GenericUtils.isEmpty(value)) {
+            value = SshdSocketAddress.toAddressString(SshdSocketAddress.getFirstExternalNetwork4Address());
+        }
+
+        return GenericUtils.isEmpty(value) ? SshdSocketAddress.LOCALHOST_IP : value;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/18335089/sshd-core/src/main/java/org/apache/sshd/client/auth/hostbased/UserAuthHostBasedFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/hostbased/UserAuthHostBasedFactory.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/hostbased/UserAuthHostBasedFactory.java
new file mode 100644
index 0000000..8ead888
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/client/auth/hostbased/UserAuthHostBasedFactory.java
@@ -0,0 +1,86 @@
+/*
+ * 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.auth.hostbased;
+
+import java.util.List;
+
+import org.apache.sshd.client.auth.AbstractUserAuthFactory;
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.signature.Signature;
+import org.apache.sshd.common.signature.SignatureFactoriesManager;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class UserAuthHostBasedFactory extends AbstractUserAuthFactory implements SignatureFactoriesManager {
+    public static final String NAME = HOST_BASED;
+
+    private List<NamedFactory<Signature>> factories;
+    private HostKeyIdentityProvider clientHostKeys;
+    private String clientUsername;
+    private String clientHostname;
+
+    public UserAuthHostBasedFactory() {
+        super(NAME);
+    }
+
+    @Override
+    public List<NamedFactory<Signature>> getSignatureFactories() {
+        return factories;
+    }
+
+    @Override
+    public void setSignatureFactories(List<NamedFactory<Signature>> factories) {
+        this.factories = factories;
+    }
+
+    public HostKeyIdentityProvider getClientHostKeys() {
+        return clientHostKeys;
+    }
+
+    public void setClientHostKeys(HostKeyIdentityProvider clientHostKeys) {
+        this.clientHostKeys = clientHostKeys;
+    }
+
+    public String getClientUsername() {
+        return clientUsername;
+    }
+
+    public void setClientUsername(String clientUsername) {
+        this.clientUsername = clientUsername;
+    }
+
+    public String getClientHostname() {
+        return clientHostname;
+    }
+
+    public void setClientHostname(String clientHostname) {
+        this.clientHostname = clientHostname;
+    }
+
+    @Override
+    public UserAuthHostBased create() {
+        UserAuthHostBased auth = new UserAuthHostBased(getClientHostKeys());
+        auth.setClientHostname(getClientHostname());
+        auth.setClientUsername(getClientUsername());
+        auth.setSignatureFactories(getSignatureFactories());
+        return auth;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/18335089/sshd-core/src/main/java/org/apache/sshd/client/auth/keyboard/UserAuthKeyboardInteractive.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/keyboard/UserAuthKeyboardInteractive.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/keyboard/UserAuthKeyboardInteractive.java
new file mode 100644
index 0000000..608f451
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/client/auth/keyboard/UserAuthKeyboardInteractive.java
@@ -0,0 +1,306 @@
+/*
+ * 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.auth.keyboard;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.sshd.client.ClientAuthenticationManager;
+import org.apache.sshd.client.auth.AbstractUserAuth;
+import org.apache.sshd.client.auth.password.PasswordIdentityProvider;
+import org.apache.sshd.client.session.ClientSession;
+import org.apache.sshd.common.PropertyResolverUtils;
+import org.apache.sshd.common.SshConstants;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.common.util.buffer.BufferUtils;
+
+/**
+ * Manages a &quot;keyboard-interactive&quot; exchange according to
+ * <A HREF="https://www.ietf.org/rfc/rfc4256.txt">RFC4256</A>
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class UserAuthKeyboardInteractive extends AbstractUserAuth {
+    public static final String NAME = UserAuthKeyboardInteractiveFactory.NAME;
+
+    public static final String INTERACTIVE_LANGUAGE_TAG = "kb-client-interactive-language-tag";
+
+    /*
+     * As per RFC-4256:
+     *
+     *      The language tag is deprecated and SHOULD be the empty string.  It
+     *      may be removed in a future revision of this specification.  Instead,
+     *      the server SHOULD select the language to be used based on the tags
+     *      communicated during key exchange
+     */
+    public static final String DEFAULT_INTERACTIVE_LANGUAGE_TAG = "";
+
+    public static final String INTERACTIVE_SUBMETHODS = "kb-client-interactive-sub-methods";
+
+    /*
+     * As per RFC-4256:
+     *
+     *      The submethods field is included so the user can give a hint of which
+     *      actual methods he wants to use.  It is a comma-separated list of
+     *      authentication submethods (software or hardware) that the user
+     *      prefers.  If the client has knowledge of the submethods preferred by
+     *      the user, presumably through a configuration setting, it MAY use the
+     *      submethods field to pass this information to the server.  Otherwise,
+     *      it MUST send the empty string.
+     *
+     *      The actual names of the submethods is something the user and the
+     *      server need to agree upon.
+     *
+     *      Server interpretation of the submethods field is implementation-
+     *      dependent.
+     */
+    public static final String DEFAULT_INTERACTIVE_SUBMETHODS = "";
+
+    private final AtomicBoolean requestPending = new AtomicBoolean(false);
+    private final AtomicInteger trialsCount = new AtomicInteger(0);
+    private Iterator<String> passwords;
+    private int maxTrials;
+
+    public UserAuthKeyboardInteractive() {
+        super(NAME);
+    }
+
+    @Override
+    public void init(ClientSession session, String service) throws Exception {
+        super.init(session, service);
+        passwords = PasswordIdentityProvider.Utils.iteratorOf(session);
+        maxTrials = PropertyResolverUtils.getIntProperty(session, ClientAuthenticationManager.PASSWORD_PROMPTS, ClientAuthenticationManager.DEFAULT_PASSWORD_PROMPTS);
+        ValidateUtils.checkTrue(maxTrials > 0, "Non-positive max. trials: %d", maxTrials);
+    }
+
+    @Override
+    protected boolean sendAuthDataRequest(ClientSession session, String service) throws Exception {
+        String name = getName();
+        if (requestPending.get()) {
+            if (log.isDebugEnabled()) {
+                log.debug("sendAuthDataRequest({})[{}] no reply for previous request for {}",
+                          session, service, name);
+            }
+            return false;
+        }
+
+        if (!verifyTrialsCount(session, service, SshConstants.SSH_MSG_USERAUTH_REQUEST, trialsCount.get(), maxTrials)) {
+            return false;
+        }
+
+        String username = session.getUsername();
+        String lang = getExchangeLanguageTag(session);
+        String subMethods = getExchangeSubMethods(session);
+        if (log.isDebugEnabled()) {
+            log.debug("sendAuthDataRequest({})[{}] send SSH_MSG_USERAUTH_REQUEST for {}: lang={}, methods={}",
+                      session, service, name, lang, subMethods);
+        }
+
+        Buffer buffer = session.createBuffer(SshConstants.SSH_MSG_USERAUTH_REQUEST,
+                            username.length() + service.length() + name.length()
+                          + GenericUtils.length(lang) + GenericUtils.length(subMethods)
+                          + Long.SIZE /* a bit extra for the lengths */);
+        buffer.putString(username);
+        buffer.putString(service);
+        buffer.putString(name);
+        buffer.putString(lang);
+        buffer.putString(subMethods);
+        requestPending.set(true);
+        session.writePacket(buffer);
+        return true;
+    }
+
+    @Override
+    protected boolean processAuthDataRequest(ClientSession session, String service, Buffer buffer) throws Exception {
+        int cmd = buffer.getUByte();
+        if (cmd != SshConstants.SSH_MSG_USERAUTH_INFO_REQUEST) {
+            throw new IllegalStateException("processAuthDataRequest(" + session + ")[" + service + "]"
+                            + " received unknown packet: cmd=" + SshConstants.getCommandMessageName(cmd));
+        }
+
+        requestPending.set(false);
+
+        if (!verifyTrialsCount(session, service, cmd, trialsCount.incrementAndGet(), maxTrials)) {
+            return false;
+        }
+
+        String name = buffer.getString();
+        String instruction = buffer.getString();
+        String lang = buffer.getString();
+        int num = buffer.getInt();
+        if (log.isDebugEnabled()) {
+            log.debug("processAuthDataRequest({})[{}] SSH_MSG_USERAUTH_INFO_REQUEST name={}, instruction={}, language={}, num-prompts={}",
+                      session, service, name, instruction, lang, num);
+        }
+
+        String[] prompt = new String[num];
+        boolean[] echo = new boolean[num];
+        for (int i = 0; i < num; i++) {
+            // according to RFC4256: "The prompt field(s) MUST NOT be empty strings."
+            prompt[i] = buffer.getString();
+            echo[i] = buffer.getBoolean();
+        }
+
+        if (log.isTraceEnabled()) {
+            log.trace("processAuthDataRequest({})[{}] Prompt: {}", session, service, Arrays.toString(prompt));
+            log.trace("processAuthDataRequest({})[{}] Echo: {}", session, service, Arrays.toString(echo));
+        }
+
+        String[] rep = getUserResponses(name, instruction, lang, prompt, echo);
+        if (rep == null) {
+            if (log.isDebugEnabled()) {
+                log.debug("processAuthDataRequest({})[{}] no responses for {}", session, service, name);
+            }
+            return false;
+        }
+
+        /*
+         * According to RFC4256:
+         *
+         *      If the num-responses field does not match the num-prompts
+         *      field in the request message, the server MUST send a failure
+         *      message.
+         *
+         * However it is the server's (!) responsibility to fail, so we only warn...
+         */
+        if (num != rep.length) {
+            log.warn("processAuthDataRequest({})[{}] Mismatched prompts ({}) vs. responses count ({})",
+                     session, service, num, rep.length);
+        }
+
+        buffer = session.prepareBuffer(SshConstants.SSH_MSG_USERAUTH_INFO_RESPONSE, BufferUtils.clear(buffer));
+        buffer.putInt(rep.length);
+        for (int index = 0; index < rep.length; index++) {
+            String r = rep[index];
+            if (log.isTraceEnabled()) {
+                log.trace("processAuthDataRequest({})[{}] response #{}: {}", session, service, index + 1, r);
+            }
+            buffer.putString(r);
+        }
+
+        session.writePacket(buffer);
+        return true;
+    }
+
+    protected String getExchangeLanguageTag(ClientSession session) {
+        return PropertyResolverUtils.getStringProperty(session, INTERACTIVE_LANGUAGE_TAG, DEFAULT_INTERACTIVE_LANGUAGE_TAG);
+    }
+
+    protected String getExchangeSubMethods(ClientSession session) {
+        return PropertyResolverUtils.getStringProperty(session, INTERACTIVE_SUBMETHODS, DEFAULT_INTERACTIVE_SUBMETHODS);
+    }
+
+    protected String getCurrentPasswordCandidate() {
+        if ((passwords != null) && passwords.hasNext()) {
+            return passwords.next();
+        } else {
+            return null;
+        }
+    }
+
+    protected boolean verifyTrialsCount(ClientSession session, String service, int cmd, int nbTrials, int maxAllowed) {
+        if (log.isDebugEnabled()) {
+            log.debug("verifyTrialsCount({})[{}] cmd={} - {} out of {}",
+                      session, service, getAuthCommandName(cmd), nbTrials, maxAllowed);
+        }
+
+        return nbTrials <= maxAllowed;
+    }
+
+    /**
+     * @param name        The interaction name - may be empty
+     * @param instruction The instruction - may be empty
+     * @param lang        The language tag - may be empty
+     * @param prompt      The prompts - may be empty
+     * @param echo        Whether to echo the response for the prompt or not - same
+     *                    length as the prompts
+     * @return The response for each prompt - if {@code null} then the assumption
+     * is that some internal error occurred and no response is sent. <B>Note:</B>
+     * according to <A HREF="https://www.ietf.org/rfc/rfc4256.txt">RFC4256</A>
+     * the number of responses should be <U>exactly</U> the same as the number
+     * of prompts. However, since it is the <U>server's</U> responsibility to
+     * enforce this we do not validate the response (other than logging it as
+     * a warning...)
+     */
+    protected String[] getUserResponses(String name, String instruction, String lang, String[] prompt, boolean[] echo) {
+        ClientSession session = getClientSession();
+        int num = GenericUtils.length(prompt);
+        if (num == 0) {
+            if (log.isDebugEnabled()) {
+                log.debug("getUserResponses({}) no prompts for interaction={}", session, name);
+            }
+            return GenericUtils.EMPTY_STRING_ARRAY;
+        }
+
+        String candidate = getCurrentPasswordCandidate();
+        if (useCurrentPassword(candidate, name, instruction, lang, prompt, echo)) {
+            if (log.isDebugEnabled()) {
+                log.debug("getUserResponses({}) use password candidate for interaction={}", session, name);
+            }
+            return new String[]{candidate};
+        }
+
+        UserInteraction ui = session.getUserInteraction();
+        if ((ui != null) && ui.isInteractionAllowed(session)) {
+            return ui.interactive(session, name, instruction, lang, prompt, echo);
+        }
+
+        if (log.isDebugEnabled()) {
+            log.debug("getUserResponses({}) no user interaction for name={}", session, name);
+        }
+
+        return null;
+    }
+
+    protected boolean useCurrentPassword(String password, String name, String instruction, String lang, String[] prompt, boolean[] echo) {
+        int num = GenericUtils.length(prompt);
+        if ((num != 1) || (password == null) || echo[0]) {
+            return false;
+        }
+
+        // check that prompt is something like "XXX password YYY:"
+        String value = GenericUtils.trimToEmpty(prompt[0]).toLowerCase();
+        int passPos = value.lastIndexOf("password");
+        if (passPos < 0) {  // no password keyword in prompt
+            return false;
+        }
+
+        int sepPos = value.lastIndexOf(':');
+        if (sepPos <= passPos) {    // no prompt separator or separator before the password keyword
+            return false;
+        }
+
+        return true;
+    }
+
+    public static String getAuthCommandName(int cmd) {
+        switch(cmd) {
+            case SshConstants.SSH_MSG_USERAUTH_REQUEST:
+                return "SSH_MSG_USERAUTH_REQUEST";
+            case SshConstants.SSH_MSG_USERAUTH_INFO_REQUEST:
+                return "SSH_MSG_USERAUTH_INFO_REQUEST";
+            default:
+                return SshConstants.getCommandMessageName(cmd);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/18335089/sshd-core/src/main/java/org/apache/sshd/client/auth/keyboard/UserAuthKeyboardInteractiveFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/keyboard/UserAuthKeyboardInteractiveFactory.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/keyboard/UserAuthKeyboardInteractiveFactory.java
new file mode 100644
index 0000000..86bf005
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/client/auth/keyboard/UserAuthKeyboardInteractiveFactory.java
@@ -0,0 +1,38 @@
+/*
+ * 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.auth.keyboard;
+
+import org.apache.sshd.client.auth.AbstractUserAuthFactory;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class UserAuthKeyboardInteractiveFactory extends AbstractUserAuthFactory {
+    public static final String NAME = KB_INTERACTIVE;
+    public static final UserAuthKeyboardInteractiveFactory INSTANCE = new UserAuthKeyboardInteractiveFactory();
+
+    public UserAuthKeyboardInteractiveFactory() {
+        super(NAME);
+    }
+
+    @Override
+    public UserAuthKeyboardInteractive create() {
+        return new UserAuthKeyboardInteractive();
+    }
+}
\ No newline at end of file