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/06/09 15:53:21 UTC

[6/6] mina-sshd git commit: [SSHD-488] Implement (a naive) ssh-keyscan

[SSHD-488] Implement (a naive) ssh-keyscan


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

Branch: refs/heads/master
Commit: 6432a7af36c3053af17cdd51a6dda4ff14af318d
Parents: ff6a4b0
Author: Lyor Goldstein <lg...@vmware.com>
Authored: Tue Jun 9 16:53:02 2015 +0300
Committer: Lyor Goldstein <lg...@vmware.com>
Committed: Tue Jun 9 16:53:02 2015 +0300

----------------------------------------------------------------------
 .../sshd/agent/common/AbstractAgentClient.java  |   2 +-
 .../sshd/agent/common/AbstractAgentProxy.java   |   2 +-
 .../sshd/agent/local/AgentServerProxy.java      |   2 +-
 .../org/apache/sshd/agent/unix/AgentServer.java |   2 +-
 .../sshd/agent/unix/AgentServerProxy.java       |   2 +-
 .../org/apache/sshd/client/ClientBuilder.java   |   2 +-
 .../java/org/apache/sshd/client/SshKeyScan.java | 508 ++++++++++
 .../auth/UserAuthKeyboardInteractive.java       |   2 +-
 .../sshd/client/auth/UserAuthPassword.java      |   2 +-
 .../sshd/client/auth/UserAuthPublicKey.java     |   2 +-
 .../client/channel/AbstractClientChannel.java   |   2 +-
 .../DelegatingServerKeyVerifier.java            |   2 +-
 .../keyverifier/RequiredServerKeyVerifier.java  |   2 +-
 .../keyverifier/StaticServerKeyVerifier.java    |   2 +-
 .../sshd/client/scp/AbstractScpClient.java      |   4 +-
 .../sshd/client/session/ClientSession.java      |  23 +
 .../sshd/client/session/ClientSessionImpl.java  |  99 +-
 .../client/session/ClientUserAuthService.java   |   2 +-
 .../sshd/client/sftp/AbstractSftpClient.java    |   2 +-
 .../client/sftp/SftpFileSystemProvider.java     |   2 +-
 .../org/apache/sshd/common/NamedFactory.java    |   1 +
 .../org/apache/sshd/common/NamedResource.java   |   1 +
 .../org/apache/sshd/common/SshConstants.java    |  29 +-
 .../org/apache/sshd/common/Transformer.java     |  58 --
 .../org/apache/sshd/common/channel/Window.java  |   2 +-
 .../sshd/common/cipher/CipherFactory.java       |   2 +-
 .../org/apache/sshd/common/cipher/ECCurves.java |  83 +-
 .../common/compression/CompressionFactory.java  |   2 +-
 .../sshd/common/config/SshConfigFileReader.java |   2 +-
 .../keys/AbstractPublicKeyEntryDecoder.java     |  90 +-
 .../config/keys/DSSPublicKeyEntryDecoder.java   |  41 +-
 .../config/keys/ECDSAPublicKeyEntryDecoder.java |  75 +-
 .../sshd/common/config/keys/KeyUtils.java       | 194 +++-
 .../sshd/common/config/keys/PublicKeyEntry.java |   4 +-
 .../config/keys/PublicKeyEntryDecoder.java      |  67 +-
 .../common/config/keys/RSAPublicKeyDecoder.java |  46 +-
 .../file/nativefs/NativeFileSystemFactory.java  |   2 +-
 .../file/root/RootedFileSystemProvider.java     |   2 +-
 .../sshd/common/future/DefaultSshFuture.java    |   2 +-
 .../io/AbstractIoServiceFactoryFactory.java     |   2 +-
 .../common/kex/dh/AbstractDHKeyExchange.java    |   2 +-
 .../keyprovider/AbstractKeyPairProvider.java    |   2 +-
 .../keyprovider/MappedKeyPairProvider.java      |   2 +-
 .../org/apache/sshd/common/mac/MacFactory.java  |   2 +-
 .../scp/LocalFileScpSourceStreamResolver.java   |   4 +-
 .../scp/LocalFileScpTargetStreamResolver.java   |   4 +-
 .../org/apache/sshd/common/scp/ScpHelper.java   |   4 +-
 .../sshd/common/session/AbstractSession.java    |   6 +-
 .../common/session/SessionTimeoutListener.java  |   2 +-
 .../sshd/common/signature/SignatureFactory.java |   2 +-
 .../sshd/common/util/AbstractLoggingBean.java   |  49 -
 .../apache/sshd/common/util/CloseableUtils.java |  11 +-
 .../sshd/common/util/EventListenerUtils.java    |   6 +-
 .../apache/sshd/common/util/GenericUtils.java   |  29 +-
 .../org/apache/sshd/common/util/IoUtils.java    | 291 ------
 .../org/apache/sshd/common/util/OsUtils.java    |   2 +-
 .../apache/sshd/common/util/SecurityUtils.java  |   5 +-
 .../apache/sshd/common/util/Transformer.java    |  58 ++
 .../apache/sshd/common/util/ValidateUtils.java  |   6 +-
 .../apache/sshd/common/util/buffer/Buffer.java  |  61 +-
 .../util/io/CloseableEmptyInputStream.java      |  94 ++
 .../sshd/common/util/io/EmptyInputStream.java   |  65 ++
 .../org/apache/sshd/common/util/io/IoUtils.java | 293 ++++++
 .../common/util/io/ModifiableFileWatcher.java   |   3 +-
 .../util/logging/AbstractLoggingBean.java       |  49 +
 .../util/logging/AbstractSimplifiedLog.java     |  36 +
 .../sshd/common/util/logging/LoggingUtils.java  | 144 +++
 .../sshd/common/util/logging/SimplifiedLog.java |  53 +
 .../sshd/server/PasswordAuthenticator.java      |   2 +-
 .../sshd/server/PublickeyAuthenticator.java     |   2 +-
 .../org/apache/sshd/server/ServerBuilder.java   |   2 +-
 .../sshd/server/auth/AbstractUserAuth.java      |   2 +-
 .../sshd/server/channel/ChannelSession.java     |   2 +-
 .../sshd/server/channel/PipeDataReceiver.java   |   2 +-
 .../server/channel/PuttyRequestHandler.java     |   2 +-
 .../apache/sshd/server/command/ScpCommand.java  |   2 +-
 .../server/config/keys/AuthorizedKeyEntry.java  |   2 +-
 .../keys/AuthorizedKeysAuthenticator.java       |   2 +-
 .../DefaultAuthorizedKeysAuthenticator.java     |   2 +-
 .../sshd/server/forward/ForwardingFilter.java   |   2 +-
 .../global/CancelTcpipForwardHandler.java       |   2 +-
 .../sshd/server/global/TcpipForwardHandler.java |   2 +-
 .../server/jaas/JaasPasswordAuthenticator.java  |   2 +-
 .../apache/sshd/server/sftp/SftpSubsystem.java  |   4 +-
 .../sshd/server/shell/ProcessShellFactory.java  |   2 +-
 .../org/apache/sshd/AbstractSessionTest.java    | 147 ---
 .../test/java/org/apache/sshd/AgentTest.java    | 212 ----
 .../test/java/org/apache/sshd/CipherTest.java   | 181 ----
 .../test/java/org/apache/sshd/ClientTest.java   | 958 -------------------
 .../src/test/java/org/apache/sshd/MacTest.java  | 188 ----
 .../test/java/org/apache/sshd/RandomTest.java   |  58 --
 .../test/java/org/apache/sshd/ServerMain.java   |  34 -
 .../test/java/org/apache/sshd/ServerTest.java   | 561 -----------
 .../java/org/apache/sshd/SshBuilderTest.java    | 136 ---
 .../java/org/apache/sshd/SshServerMain.java     |  35 -
 .../java/org/apache/sshd/SshServerTest.java     |  99 --
 .../java/org/apache/sshd/agent/AgentTest.java   | 212 ++++
 .../java/org/apache/sshd/client/ClientTest.java | 958 +++++++++++++++++++
 .../client/session/ClientSessionImplTest.java   | 103 ++
 .../sshd/client/sftp/SftpFileSystemTest.java    |   2 +-
 .../org/apache/sshd/client/sftp/SftpTest.java   |   2 +-
 .../org/apache/sshd/common/SshBuilderTest.java  | 136 +++
 .../apache/sshd/common/cipher/CipherTest.java   | 181 ++++
 .../common/config/SshConfigFileReaderTest.java  |   2 +-
 .../sshd/common/config/keys/KeyUtilsTest.java   | 138 +++
 .../org/apache/sshd/common/mac/MacTest.java     | 188 ++++
 .../apache/sshd/common/random/RandomTest.java   |  58 ++
 .../common/session/AbstractSessionTest.java     | 147 +++
 .../sshd/common/util/GenericUtilsTest.java      |  27 +
 .../common/util/io/EmptyInputStreamTest.java    | 117 +++
 .../sshd/deprecated/AbstractUserAuth.java       |   2 +-
 .../java/org/apache/sshd/server/ServerMain.java |  34 +
 .../java/org/apache/sshd/server/ServerTest.java | 561 +++++++++++
 .../org/apache/sshd/server/SshServerMain.java   |  35 +
 .../org/apache/sshd/server/SshServerTest.java   |  99 ++
 .../config/keys/AuthorizedKeyEntryTest.java     |   2 +-
 .../DefaultAuthorizedKeysAuthenticatorTest.java |   2 +-
 .../apache/sshd/util/BogusInvertedShell.java    |   2 +-
 .../sshd/util/BogusPasswordAuthenticator.java   |   2 +-
 119 files changed, 5113 insertions(+), 3198 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/agent/common/AbstractAgentClient.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/agent/common/AbstractAgentClient.java b/sshd-core/src/main/java/org/apache/sshd/agent/common/AbstractAgentClient.java
index 20510ec..c99977b 100644
--- a/sshd-core/src/main/java/org/apache/sshd/agent/common/AbstractAgentClient.java
+++ b/sshd-core/src/main/java/org/apache/sshd/agent/common/AbstractAgentClient.java
@@ -34,10 +34,10 @@ import java.util.List;
 
 import org.apache.sshd.agent.SshAgent;
 import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.util.AbstractLoggingBean;
 import org.apache.sshd.common.util.buffer.Buffer;
 import org.apache.sshd.common.util.buffer.BufferUtils;
 import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
 
 public abstract class AbstractAgentClient extends AbstractLoggingBean {
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/agent/common/AbstractAgentProxy.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/agent/common/AbstractAgentProxy.java b/sshd-core/src/main/java/org/apache/sshd/agent/common/AbstractAgentProxy.java
index 61df6ae..9ec0adf 100644
--- a/sshd-core/src/main/java/org/apache/sshd/agent/common/AbstractAgentProxy.java
+++ b/sshd-core/src/main/java/org/apache/sshd/agent/common/AbstractAgentProxy.java
@@ -37,11 +37,11 @@ import java.util.concurrent.ExecutorService;
 
 import org.apache.sshd.agent.SshAgent;
 import org.apache.sshd.common.SshException;
-import org.apache.sshd.common.util.AbstractLoggingBean;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.buffer.Buffer;
 import org.apache.sshd.common.util.buffer.BufferUtils;
 import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
 import org.apache.sshd.common.util.threads.ExecutorServiceConfigurer;
 
 public abstract class AbstractAgentProxy extends AbstractLoggingBean implements SshAgent, ExecutorServiceConfigurer {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/agent/local/AgentServerProxy.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/agent/local/AgentServerProxy.java b/sshd-core/src/main/java/org/apache/sshd/agent/local/AgentServerProxy.java
index 7df3f2b..7a6d5a6 100644
--- a/sshd-core/src/main/java/org/apache/sshd/agent/local/AgentServerProxy.java
+++ b/sshd-core/src/main/java/org/apache/sshd/agent/local/AgentServerProxy.java
@@ -26,9 +26,9 @@ import org.apache.sshd.agent.SshAgent;
 import org.apache.sshd.agent.SshAgentServer;
 import org.apache.sshd.client.future.OpenFuture;
 import org.apache.sshd.common.session.ConnectionService;
-import org.apache.sshd.common.util.AbstractLoggingBean;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
 
 /**
  * The server side fake agent, acting as an agent, but actually forwarding the requests to the auth channel on the client side.

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/agent/unix/AgentServer.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/agent/unix/AgentServer.java b/sshd-core/src/main/java/org/apache/sshd/agent/unix/AgentServer.java
index f176790..ca9ecdc 100644
--- a/sshd-core/src/main/java/org/apache/sshd/agent/unix/AgentServer.java
+++ b/sshd-core/src/main/java/org/apache/sshd/agent/unix/AgentServer.java
@@ -27,10 +27,10 @@ import java.util.concurrent.Future;
 import org.apache.sshd.agent.SshAgent;
 import org.apache.sshd.agent.common.AbstractAgentClient;
 import org.apache.sshd.agent.local.AgentImpl;
-import org.apache.sshd.common.util.AbstractLoggingBean;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.buffer.Buffer;
 import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
 import org.apache.sshd.common.util.threads.ExecutorServiceCarrier;
 import org.apache.sshd.common.util.threads.ThreadUtils;
 import org.apache.tomcat.jni.Local;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/agent/unix/AgentServerProxy.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/agent/unix/AgentServerProxy.java b/sshd-core/src/main/java/org/apache/sshd/agent/unix/AgentServerProxy.java
index 0907b63..1b02795 100644
--- a/sshd-core/src/main/java/org/apache/sshd/agent/unix/AgentServerProxy.java
+++ b/sshd-core/src/main/java/org/apache/sshd/agent/unix/AgentServerProxy.java
@@ -29,9 +29,9 @@ import org.apache.sshd.agent.SshAgentServer;
 import org.apache.sshd.client.future.OpenFuture;
 import org.apache.sshd.common.SshException;
 import org.apache.sshd.common.session.ConnectionService;
-import org.apache.sshd.common.util.AbstractLoggingBean;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.OsUtils;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
 import org.apache.sshd.common.util.threads.ExecutorServiceCarrier;
 import org.apache.sshd.common.util.threads.ThreadUtils;
 import org.apache.tomcat.jni.Local;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/client/ClientBuilder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/ClientBuilder.java b/sshd-core/src/main/java/org/apache/sshd/client/ClientBuilder.java
index ccea2d6..1ab0d28 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/ClientBuilder.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/ClientBuilder.java
@@ -27,11 +27,11 @@ import org.apache.sshd.client.kex.DHGEXClient;
 import org.apache.sshd.client.keyverifier.AcceptAllServerKeyVerifier;
 import org.apache.sshd.common.BaseBuilder;
 import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.Transformer;
 import org.apache.sshd.common.channel.Channel;
 import org.apache.sshd.common.kex.BuiltinDHFactories;
 import org.apache.sshd.common.kex.DHFactory;
 import org.apache.sshd.common.kex.KeyExchange;
+import org.apache.sshd.common.util.Transformer;
 import org.apache.sshd.server.forward.TcpipServerChannel;
 
 /**

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/client/SshKeyScan.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/SshKeyScan.java b/sshd-core/src/main/java/org/apache/sshd/client/SshKeyScan.java
new file mode 100644
index 0000000..e6b7cfa
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/client/SshKeyScan.java
@@ -0,0 +1,508 @@
+/*
+ * 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.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.Closeable;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.PrintStream;
+import java.io.Writer;
+import java.net.ConnectException;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.security.PublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.TreeMap;
+import java.util.UUID;
+import java.util.concurrent.Callable;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Level;
+
+import org.apache.sshd.client.future.ConnectFuture;
+import org.apache.sshd.client.session.ClientSession;
+import org.apache.sshd.common.SshConstants;
+import org.apache.sshd.common.SshdSocketAddress;
+import org.apache.sshd.common.cipher.ECCurves;
+import org.apache.sshd.common.config.SshConfigFileReader;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.config.keys.PublicKeyEntry;
+import org.apache.sshd.common.io.IoSession;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.session.Session;
+import org.apache.sshd.common.session.SessionListener;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.SecurityUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.io.NoCloseInputStream;
+import org.apache.sshd.common.util.logging.AbstractSimplifiedLog;
+import org.apache.sshd.common.util.logging.LoggingUtils;
+
+/**
+ * A naive implementation of <A HREF="https://www.freebsd.org/cgi/man.cgi?query=ssh-keyscan&sektion=1">ssh-keyscan(1)</A>
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class SshKeyScan extends AbstractSimplifiedLog
+                        implements Closeable, Callable<Void>, ServerKeyVerifier, SessionListener {
+    /**
+     * Default key types if not overridden from the command line
+     */
+    public static final List<String> DEFAULT_KEY_TYPES =
+            Collections.unmodifiableList(Arrays.asList("rsa", "ecdsa"));
+    public static final long DEFAULT_TIMEOUT = TimeUnit.SECONDS.toMillis(5L);
+    public static final Level DEFAULT_LEVEL = Level.INFO;
+
+    private int port;
+    private long timeout;
+    private List<String> keyTypes;
+    private InputStream input;
+    private Level level;
+    private final Map<String,String> currentHostFingerprints = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
+
+    public SshKeyScan() {
+        super();
+    }
+
+    public int getPort() {
+        return port;
+    }
+    
+    public void setPort(int port) {
+        this.port = port;
+    }
+
+    public InputStream getInputStream() {
+        return input;
+    }
+
+    public void setInputStream(InputStream input) {
+        this.input = input;
+    }
+
+    public List<String> getKeyTypes() {
+        return keyTypes;
+    }
+    
+    public void setKeyTypes(List<String> keyTypes) {
+        this.keyTypes = keyTypes;
+    }
+
+    public long getTimeout() {
+        return timeout;
+    }
+
+    public void setTimeout(long timeout) {
+        this.timeout = timeout;
+    }
+
+    public Level getLogLevel() {
+        return level;
+    }
+
+    public void setLogLevel(Level level) {
+        this.level = level;
+    }
+
+    @Override
+    public void log(Level level, Object message, Throwable t) {
+        if (isEnabled(level)) {
+            PrintStream ps = System.out;
+            if ((t != null) || Level.SEVERE.equals(level) || Level.WARNING.equals(level)) {
+                ps = System.err;
+            }
+            
+            ps.append('\t').println(message);
+            if (t != null) {
+                ps.append("\t\t").append(t.getClass().getSimpleName()).append(": ").println(t.getMessage());
+            }
+        }
+    }
+            
+    @Override
+    public boolean isEnabled(Level level) {
+        return LoggingUtils.isLoggable(level, getLogLevel());
+    }
+
+    @Override
+    public Void call() throws Exception {
+        Exception err = null;
+        try(SshClient   client = SshClient.setUpDefaultClient()) {
+            Collection<String> typeNames = getKeyTypes();
+            Map<String,KeyPair> pairsMap = createKeyPairs(typeNames);
+            client.setServerKeyVerifier(this);
+
+            try(BufferedReader rdr = new BufferedReader(new InputStreamReader(getInputStream(), StandardCharsets.UTF_8))) {
+                client.start();
+
+                for (String line = rdr.readLine(); line != null; line = rdr.readLine()) {
+                    line = GenericUtils.trimToEmpty(line);
+                    if (GenericUtils.isEmpty(line)) {
+                        continue;
+                    }
+                    
+                    try {
+                        resolveServerKeys(client, line, pairsMap);
+                    } catch(Exception e) {
+                        if (isEnabled(Level.FINE)) {
+                            log(Level.FINE, "Failed to retrieve keys from " + line, e);
+                        }
+                        err = GenericUtils.accumulateException(err, e);
+                    } finally {
+                        currentHostFingerprints.clear();
+                    }
+                }
+            } finally {
+                client.stop();
+            }
+        }
+        
+        if (err != null) {
+            throw err;
+        }
+
+        return null;
+    }
+
+    protected void resolveServerKeys(SshClient client, String host, Map<String,KeyPair> pairsMap) {
+        for (Map.Entry<String,KeyPair> pe : pairsMap.entrySet()) {
+            String kt = pe.getKey();
+            try {
+                resolveServerKeys(client, host, kt, pe.getValue());
+            } catch(Exception e) {
+                if (isEnabled(Level.FINE)) {
+                    log(Level.FINE, "Failed to resolve key=" + kt + " for " + host);
+                }
+                
+                if (e instanceof ConnectException) {
+                    return; // makes no sense to try again with another key type...
+                }
+            }
+        }
+    }
+    
+    protected void resolveServerKeys(SshClient client, String host, String kt, KeyPair kp) throws Exception {
+        int connectPort = getPort();
+        if (isEnabled(Level.FINE)) {
+            log(Level.FINE, "Connecting to " + host + ":" + connectPort);
+        }
+
+        ConnectFuture future = client.connect(UUID.randomUUID().toString(), host, connectPort);
+        long waitTime = getTimeout();
+        if (!future.await(waitTime)) {
+            throw new ConnectException("Failed to connect to " + host + ":" + connectPort + " within " + waitTime + " msec.");
+        }
+
+        try(ClientSession session = future.getSession()) {
+            IoSession ioSession = session.getIoSession();
+            SocketAddress remoteAddress = ioSession.getRemoteAddress();
+            String remoteLocation = toString(remoteAddress);
+            if (isEnabled(Level.FINE)) {
+                log(Level.FINE, "Connected to " + remoteLocation);
+            }
+
+            try {
+                session.addListener(this);
+                if (isEnabled(Level.FINER)) {
+                    log(Level.FINER, "Authenticating with " + kt + " to " + remoteLocation);
+                }
+
+                // shouldn't really succeed, but do it since key exchange occurs only on auth attempt
+                session.addPublicKeyIdentity(kp);
+                try {
+                    session.auth().verify(waitTime);
+                    log(Level.WARNING, "Unexpected authentication success using key type=" + kt + " with " + remoteLocation);
+                } catch(Exception e) {
+                    if (isEnabled(Level.FINER)) {
+                        log(Level.FINER, "Failed to authenticate using key type=" + kt + " with " + remoteLocation);
+                    }
+                }
+            } finally {
+                session.removeListener(this);
+            }
+        }
+    }
+
+    @Override
+    public void sessionCreated(Session session) {
+        logSessionEvent(session, "Created");
+    }
+
+    @Override
+    public void sessionEvent(Session session, Event event) {
+        logSessionEvent(session, event);
+        if (isEnabled(Level.FINEST) && Event.KexCompleted.equals(event)) {
+            IoSession ioSession = session.getIoSession();
+            SocketAddress remoteAddress = ioSession.getRemoteAddress();
+            String remoteLocation = toString(remoteAddress);
+            for (int paramType = 0; paramType < SshConstants.PROPOSAL_KEX_NAMES.size(); paramType++) {
+                String paramName = SshConstants.PROPOSAL_KEX_NAMES.get(paramType);
+                String paramValue = session.getNegotiatedKexParameter(paramType);
+                log(Level.FINEST, remoteLocation + "[" + paramName + "]: " + paramValue);
+            }
+        }
+    }
+
+    @Override
+    public void sessionClosed(Session session) {
+        logSessionEvent(session, "Closed");
+    }
+
+    protected void logSessionEvent(Session session, Object event) {
+        if (isEnabled(Level.FINEST)) {
+            IoSession ioSession = session.getIoSession();
+            SocketAddress remoteAddress = ioSession.getRemoteAddress();
+            log(Level.FINEST, "Session " + toString(remoteAddress) + " event: " + event);
+        }
+    }
+
+    @Override
+    public boolean verifyServerKey(ClientSession sshClientSession, SocketAddress remoteAddress, PublicKey serverKey) {
+        String remoteLocation = toString(remoteAddress);
+        try {
+            String extra = KeyUtils.getFingerPrint(serverKey);
+            String keyType = KeyUtils.getKeyType(serverKey);
+            String current = GenericUtils.isEmpty(keyType) ? null : currentHostFingerprints.get(keyType);
+            if (Objects.equals(current, extra)) {
+                if (isEnabled(Level.FINER)) {
+                    log(Level.FINER, "verifyServerKey(" + remoteLocation + ")[" + keyType + "] skip existing key: " + extra);
+                }
+            } else {
+                if (isEnabled(Level.FINE)) {
+                    log(Level.FINE, "verifyServerKey(" + remoteLocation + ")[" + keyType + "] found new key: " + extra);
+                }
+
+                StringBuilder   sb = new StringBuilder(256).append(remoteLocation).append(' ');
+                PublicKeyEntry.appendPublicKeyEntry(sb, serverKey);
+                log(Level.INFO, sb);
+                
+                if (!GenericUtils.isEmpty(keyType)) {
+                    currentHostFingerprints.put(keyType, extra);
+                }
+            }
+        } catch(Exception e) {
+            log(Level.SEVERE, "Failed to output the public key from " + remoteLocation, e);
+        }
+
+        return true;
+    }
+
+    private static final String toString(SocketAddress addr) {
+        if (addr == null) {
+            return null;
+        } else if (addr instanceof InetSocketAddress) {
+            return ((InetSocketAddress) addr).getHostString();
+        } else if (addr instanceof SshdSocketAddress) {
+            return ((SshdSocketAddress) addr).getHostName();
+        } else {
+            return addr.toString();
+        }
+    }
+
+    protected Map<String,KeyPair> createKeyPairs(Collection<String> typeNames) throws GeneralSecurityException {
+        if (GenericUtils.isEmpty(typeNames)) {
+            return Collections.emptyMap();
+        }
+
+        Map<String,KeyPair> pairsMap = new TreeMap<String, KeyPair>(String.CASE_INSENSITIVE_ORDER);
+        for (String kt : typeNames) {
+            if (isEnabled(Level.FINE)) {
+                log(Level.FINE, "Generate key pair for " + kt);
+            }
+
+            if ("rsa".equalsIgnoreCase(kt)) {
+                pairsMap.put(KeyPairProvider.SSH_RSA, KeyUtils.generateKeyPair(KeyPairProvider.SSH_RSA, 1024));
+            } else if ("dsa".equalsIgnoreCase(kt)) {
+                pairsMap.put(KeyPairProvider.SSH_DSS, KeyUtils.generateKeyPair(KeyPairProvider.SSH_DSS, 512));
+            } else if ("ecdsa".equalsIgnoreCase(kt)) {
+                if (!SecurityUtils.hasEcc()) {
+                    throw new InvalidKeySpecException("ECC not supported");
+                }
+
+                for (String curveName : ECCurves.NAMES) {
+                    Integer keySize = ECCurves.getCurveSize(curveName);
+                    if (keySize == null) {
+                        throw new InvalidKeySpecException("Unknown curve: " + curveName);
+                    }
+
+                    if (isEnabled(Level.FINER)) {
+                        log(Level.FINER, "Generate key pair for curve=" + curveName);
+                    }
+
+                    String keyName = ECCurves.ECDSA_SHA2_PREFIX + curveName;
+                    pairsMap.put(keyName, KeyUtils.generateKeyPair(keyName, keySize.intValue()));
+                }
+            }
+        }
+        
+        return pairsMap;
+    }
+
+    @Override
+    public void close() throws IOException {
+        IOException err = null;
+        if (input != null) {
+            try {
+                input.close();
+            } catch(IOException e) {
+                err = GenericUtils.accumulateException(err, e);
+            } finally {
+                input = null;
+            }
+        }
+    }
+
+    //////////////////////////////////////////////////////////////////////////
+
+    // returns a List of the hosts to be contacted
+    public static final List<String> parseCommandLineArguments(SshKeyScan scanner, String ... args) throws IOException {
+        int numArgs = GenericUtils.length(args);
+        for (int index=0; index < numArgs; index++) {
+            String optName = args[index];
+            if ("-f".equals(optName)) {
+                index++;
+                ValidateUtils.checkTrue(index < numArgs, "Missing %s option argument", optName);
+                ValidateUtils.checkTrue(scanner.getInputStream() == null, "%s option re-specified", optName);
+
+                String filePath = args[index];
+                if ("-".equals(filePath)) {
+                    scanner.setInputStream(new NoCloseInputStream(System.in));
+                } else {
+                    scanner.setInputStream(new FileInputStream(filePath));
+                }
+            } else if ("-t".equals(optName)) {
+                index++;
+                ValidateUtils.checkTrue(index < numArgs, "Missing %s option argument", optName);
+                ValidateUtils.checkTrue(GenericUtils.isEmpty(scanner.getKeyTypes()), "%s option re-specified", optName);
+                
+                String typeList = args[index];
+                String[] types = GenericUtils.split(typeList, ',');
+                ValidateUtils.checkTrue(GenericUtils.length(types) > 0, "No types specified for %s", optName);
+            } else if ("-p".equals(optName)) {
+                index++;
+                ValidateUtils.checkTrue(index < numArgs, "Missing %s option argument", optName);
+                ValidateUtils.checkTrue(scanner.getPort() <= 0, "%s option re-specified", optName);
+                
+                String portValue = args[index];
+                int port = Integer.parseInt(portValue);
+                ValidateUtils.checkTrue((port > 0) && (port <= 0xFFFF), "Bad port: %s", portValue);
+                scanner.setPort(port);
+            } else if ("-T".equals(optName)) {
+                index++;
+                ValidateUtils.checkTrue(index < numArgs, "Missing %s option argument", optName);
+                ValidateUtils.checkTrue(scanner.getTimeout() <= 0, "%s option re-specified", optName);
+                
+                String timeoutValue = args[index];
+                long timeout = Long.parseLong(timeoutValue);
+                ValidateUtils.checkTrue(timeout > 0L, "Bad timeout: %s", timeoutValue);
+                scanner.setTimeout(timeout);
+            } else if ("-v".equals(optName)) {
+                ValidateUtils.checkTrue(scanner.getLogLevel() == null, "%s option re-specified", optName);
+                scanner.setLogLevel(Level.FINEST);
+            } else {    // stop at first non-option - assume the rest are host names/addresses
+                ValidateUtils.checkTrue((optName.charAt(0) != '-'), "Unknown option: %s", optName);
+                
+                int remaining = numArgs - index;
+                if (remaining == 1) {
+                    return Collections.singletonList(optName);
+                }
+                
+                List<String> hosts = new ArrayList<String>(remaining);
+                for ( ; index < numArgs; index++) {
+                    hosts.add(args[index]);
+                }
+                
+                return hosts;
+            }
+        }
+        
+        return Collections.emptyList();
+    }
+
+    /* -------------------------------------------------------------------- */
+
+    public static final <S extends SshKeyScan> S setInputStream(S scanner, Collection<String> hosts) throws IOException {
+        if (GenericUtils.isEmpty(hosts)) {
+            ValidateUtils.checkNotNull(scanner.getInputStream(), "No hosts or file specified", GenericUtils.EMPTY_OBJECT_ARRAY);
+        } else {
+            ValidateUtils.checkTrue(scanner.getInputStream() == null, "Both hosts and file specified", GenericUtils.EMPTY_OBJECT_ARRAY);
+            
+            // convert the hosts from the arguments into a "file" - one host per line
+            try(ByteArrayOutputStream baos = new ByteArrayOutputStream(hosts.size() * 32)) {
+                try(Writer w = new OutputStreamWriter(baos, StandardCharsets.UTF_8)) {
+                    String EOL = System.getProperty("line.separator");
+                    for (String h : hosts) {
+                        w.append(h).append(EOL);
+                    }
+                }
+                
+                byte[] data = baos.toByteArray();
+                scanner.setInputStream(new ByteArrayInputStream(data));
+            }
+        }
+        
+        return scanner;
+    }
+
+    public static final <S extends SshKeyScan> S initializeScanner(S scanner, Collection<String> hosts) throws IOException {
+        setInputStream(scanner, hosts);
+        if (scanner.getPort() <= 0) {
+            scanner.setPort(SshConfigFileReader.DEFAULT_PORT);
+        }
+        
+        if (scanner.getTimeout() <= 0L) {
+            scanner.setTimeout(DEFAULT_TIMEOUT);
+        }
+        
+        if (GenericUtils.isEmpty(scanner.getKeyTypes())) {
+            scanner.setKeyTypes(DEFAULT_KEY_TYPES);
+        }
+
+        if (scanner.getLogLevel() == null) {
+            scanner.setLogLevel(DEFAULT_LEVEL);
+        }
+
+        return scanner;
+    }
+
+    /* -------------------------------------------------------------------- */
+
+    public static void main(String[] args) throws Exception {
+        try(SshKeyScan scanner = new SshKeyScan()) {
+            Collection<String> hosts = parseCommandLineArguments(scanner, args);
+            initializeScanner(scanner, hosts);
+            scanner.call();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/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 af17b13..6adc678 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
@@ -33,9 +33,9 @@ import org.apache.sshd.client.UserInteraction;
 import org.apache.sshd.client.session.ClientSession;
 import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.SshConstants;
-import org.apache.sshd.common.util.AbstractLoggingBean;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
 
 /**
  * TODO Add javadoc

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/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 ea3cb8a..b9e885b 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
@@ -27,8 +27,8 @@ import org.apache.sshd.client.UserAuth;
 import org.apache.sshd.client.session.ClientSession;
 import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.SshConstants;
-import org.apache.sshd.common.util.AbstractLoggingBean;
 import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
 
 /**
  * TODO Add javadoc

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/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 6bc90bf..2d2c95a 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
@@ -38,9 +38,9 @@ import org.apache.sshd.common.SshConstants;
 import org.apache.sshd.common.keyprovider.KeyPairProvider;
 import org.apache.sshd.common.session.AbstractSession;
 import org.apache.sshd.common.signature.Signature;
-import org.apache.sshd.common.util.AbstractLoggingBean;
 import org.apache.sshd.common.util.buffer.Buffer;
 import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
 
 /**
  * TODO Add javadoc

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/client/channel/AbstractClientChannel.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/channel/AbstractClientChannel.java b/sshd-core/src/main/java/org/apache/sshd/client/channel/AbstractClientChannel.java
index ddb16e3..b444c16 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/channel/AbstractClientChannel.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/channel/AbstractClientChannel.java
@@ -35,9 +35,9 @@ import org.apache.sshd.common.channel.ChannelAsyncOutputStream;
 import org.apache.sshd.common.channel.RequestHandler;
 import org.apache.sshd.common.io.IoInputStream;
 import org.apache.sshd.common.io.IoOutputStream;
-import org.apache.sshd.common.util.IoUtils;
 import org.apache.sshd.common.util.buffer.Buffer;
 import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
+import org.apache.sshd.common.util.io.IoUtils;
 
 /**
  * TODO Add javadoc

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/client/keyverifier/DelegatingServerKeyVerifier.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/keyverifier/DelegatingServerKeyVerifier.java b/sshd-core/src/main/java/org/apache/sshd/client/keyverifier/DelegatingServerKeyVerifier.java
index 6c58647..73ba903 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/keyverifier/DelegatingServerKeyVerifier.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/keyverifier/DelegatingServerKeyVerifier.java
@@ -24,7 +24,7 @@ import java.util.Map;
 
 import org.apache.sshd.client.ServerKeyVerifier;
 import org.apache.sshd.client.session.ClientSession;
-import org.apache.sshd.common.util.AbstractLoggingBean;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
 
 /*
  * A ServerKeyVerifier that delegates verification to the ServerKeyVerifier found in the ClientSession metadata

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/client/keyverifier/RequiredServerKeyVerifier.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/keyverifier/RequiredServerKeyVerifier.java b/sshd-core/src/main/java/org/apache/sshd/client/keyverifier/RequiredServerKeyVerifier.java
index 8342eb9..6cd5dbc 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/keyverifier/RequiredServerKeyVerifier.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/keyverifier/RequiredServerKeyVerifier.java
@@ -23,8 +23,8 @@ import java.security.PublicKey;
 
 import org.apache.sshd.client.ServerKeyVerifier;
 import org.apache.sshd.client.session.ClientSession;
-import org.apache.sshd.common.util.AbstractLoggingBean;
 import org.apache.sshd.common.util.buffer.BufferUtils;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
 
 /**
  * A ServerKeyVerifier that accepts one server key (specified in the constructor)

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/client/keyverifier/StaticServerKeyVerifier.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/keyverifier/StaticServerKeyVerifier.java b/sshd-core/src/main/java/org/apache/sshd/client/keyverifier/StaticServerKeyVerifier.java
index f80e82b..18bb93a 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/keyverifier/StaticServerKeyVerifier.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/keyverifier/StaticServerKeyVerifier.java
@@ -25,7 +25,7 @@ import java.security.PublicKey;
 import org.apache.sshd.client.ServerKeyVerifier;
 import org.apache.sshd.client.session.ClientSession;
 import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.util.AbstractLoggingBean;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
 
 /**
  * Returns the same constant answer {@code true/false} regardless

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/client/scp/AbstractScpClient.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/scp/AbstractScpClient.java b/sshd-core/src/main/java/org/apache/sshd/client/scp/AbstractScpClient.java
index d59000d..fcefa3e 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/scp/AbstractScpClient.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/scp/AbstractScpClient.java
@@ -36,10 +36,10 @@ import java.util.EnumSet;
 import org.apache.sshd.common.SshException;
 import org.apache.sshd.common.scp.ScpHelper;
 import org.apache.sshd.common.scp.ScpTimestamp;
-import org.apache.sshd.common.util.AbstractLoggingBean;
 import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.IoUtils;
 import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
 
 /**
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/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 d4bd108..6a885d4 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
@@ -68,9 +68,32 @@ public interface ClientSession extends Session {
     int WAIT_AUTH =   0x0004;
     int AUTHED =      0x0008;
 
+    /**
+     * @param password Password to be added - may not be {@code null}/empty
+     */
     void addPasswordIdentity(String password);
+
+    /**
+     * @param password The password to remove - ignored if {@code null}/empty
+     * @return The removed password - same one that was added via
+     * {@link #addPasswordIdentity(String)} - or {@code null} if no
+     * match found 
+     */
+    String removePasswordIdentity(String password);
+
+    /**
+     * @param key The {@link KeyPair} to add - may not be {@code null}
+     */
     void addPublicKeyIdentity(KeyPair key);
 
+    /**
+     * @param kp The {@link KeyPair} to remove - ignored if {@code null}
+     * @return The removed {@link KeyPair} - same one that was added via
+     * {@link #addPublicKeyIdentity(KeyPair)} - or {@code null} if no
+     * match found
+     */
+    KeyPair removePublicKeyIdentity(KeyPair kp);
+
     UserInteraction getUserInteraction();
     void setUserInteraction(UserInteraction userInteraction);
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/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 d29b350..eaf1901 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
@@ -23,6 +23,7 @@ import java.net.SocketAddress;
 import java.nio.file.FileSystem;
 import java.security.KeyPair;
 import java.util.ArrayList;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -50,6 +51,7 @@ import org.apache.sshd.common.SshConstants;
 import org.apache.sshd.common.SshException;
 import org.apache.sshd.common.SshdSocketAddress;
 import org.apache.sshd.common.cipher.CipherNone;
+import org.apache.sshd.common.config.keys.KeyUtils;
 import org.apache.sshd.common.future.DefaultSshFuture;
 import org.apache.sshd.common.future.SshFuture;
 import org.apache.sshd.common.io.IoSession;
@@ -58,6 +60,8 @@ 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;
+import org.apache.sshd.common.util.ValidateUtils;
 import org.apache.sshd.common.util.buffer.Buffer;
 
 /**
@@ -66,6 +70,37 @@ 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;
+                }
+            }
+        };
 
     /**
      * For clients to store their own metadata
@@ -85,12 +120,12 @@ public class ClientSessionImpl extends AbstractSession implements ClientSession
 
     public ClientSessionImpl(ClientFactoryManager client, IoSession session) throws Exception {
         super(false, client, session);
-        log.info("Client session created: {}", session);
+        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.
         List<ServiceFactory> factories = client.getServiceFactories();
-        if (factories == null || factories.isEmpty() || factories.size() > 2) {
+        if (GenericUtils.isEmpty(factories) || factories.size() > 2) {
             throw new IllegalArgumentException("One or two services must be configured");
         }
         currentServiceFactory = factories.get(0);
@@ -128,12 +163,62 @@ public class ClientSessionImpl extends AbstractSession implements ClientSession
 
     @Override
     public void addPasswordIdentity(String password) {
-        identities.add(password);
+        identities.add(ValidateUtils.checkNotNullAndNotEmpty(password, "No password provided", GenericUtils.EMPTY_OBJECT_ARRAY));
+        if (log.isDebugEnabled()) { // don't show the password in the log
+            log.debug("addPasswordIdentity(" + 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", GenericUtils.EMPTY_OBJECT_ARRAY);
+        ValidateUtils.checkNotNull(kp.getPublic(), "No public key", GenericUtils.EMPTY_OBJECT_ARRAY);
+        ValidateUtils.checkNotNull(kp.getPrivate(), "No private key", GenericUtils.EMPTY_OBJECT_ARRAY);
+
+        identities.add(kp);
+
+        if (log.isDebugEnabled()) {
+            log.debug("addPublicKeyIdentity(" + KeyUtils.getFingerPrint(kp.getPublic()) + ")");
+        }
     }
 
     @Override
-    public void addPublicKeyIdentity(KeyPair key) {
-        identities.add(key);
+    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
@@ -382,7 +467,7 @@ public class ClientSessionImpl extends AbstractSession implements ClientSession
         if (serverVersion == null) {
             return false;
         }
-        log.info("Server version string: {}", serverVersion);
+        log.debug("Server version string: {}", serverVersion);
         if (!(serverVersion.startsWith("SSH-2.0-") || serverVersion.startsWith("SSH-1.99-"))) {
             throw new SshException(SshConstants.SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED,
                                    "Unsupported protocol version: " + serverVersion);
@@ -390,7 +475,7 @@ public class ClientSessionImpl extends AbstractSession implements ClientSession
         return true;
     }
 
-    private void sendClientIdentification() {
+    protected void sendClientIdentification() {
         clientVersion = "SSH-2.0-" + getFactoryManager().getVersion();
         sendIdentification(clientVersion);
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/client/session/ClientUserAuthService.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientUserAuthService.java b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientUserAuthService.java
index 1bb5b58..72d8601 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientUserAuthService.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientUserAuthService.java
@@ -169,7 +169,7 @@ public class ClientUserAuthService extends CloseableUtils.AbstractCloseable impl
             return;
         }
         if (cmd == SshConstants.SSH_MSG_USERAUTH_FAILURE) {
-            log.info("Received SSH_MSG_USERAUTH_FAILURE");
+            log.debug("Received SSH_MSG_USERAUTH_FAILURE");
             String mths = buffer.getString();
             boolean partial = buffer.getBoolean();
             if (partial || serverMethods == null) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/client/sftp/AbstractSftpClient.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/sftp/AbstractSftpClient.java b/sshd-core/src/main/java/org/apache/sshd/client/sftp/AbstractSftpClient.java
index b532b9d..2bcb9ef 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/sftp/AbstractSftpClient.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/sftp/AbstractSftpClient.java
@@ -26,8 +26,8 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.EnumSet;
 
-import org.apache.sshd.common.util.AbstractLoggingBean;
 import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
 
 /**
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/client/sftp/SftpFileSystemProvider.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/sftp/SftpFileSystemProvider.java b/sshd-core/src/main/java/org/apache/sshd/client/sftp/SftpFileSystemProvider.java
index d4c171a..4f7b693 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/sftp/SftpFileSystemProvider.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/sftp/SftpFileSystemProvider.java
@@ -82,8 +82,8 @@ import org.apache.sshd.common.SshException;
 import org.apache.sshd.common.config.SshConfigFileReader;
 import org.apache.sshd.common.sftp.SftpConstants;
 import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.IoUtils;
 import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.io.IoUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/common/NamedFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/NamedFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/NamedFactory.java
index 3337e46..76c1e0c 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/NamedFactory.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/NamedFactory.java
@@ -23,6 +23,7 @@ import java.util.Collection;
 import java.util.List;
 
 import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.Transformer;
 
 /**
  * A named factory is a factory identified by a name.

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/common/NamedResource.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/NamedResource.java b/sshd-core/src/main/java/org/apache/sshd/common/NamedResource.java
index d57f433..b38670f 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/NamedResource.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/NamedResource.java
@@ -26,6 +26,7 @@ import java.util.Comparator;
 import java.util.List;
 
 import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.Transformer;
 
 /**
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/common/SshConstants.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/SshConstants.java b/sshd-core/src/main/java/org/apache/sshd/common/SshConstants.java
index eebd1b1..d6a41be 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/SshConstants.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/SshConstants.java
@@ -18,6 +18,10 @@
  */
 package org.apache.sshd.common;
 
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
 /**
  * This interface defines constants for the SSH protocol.
  *
@@ -100,18 +104,19 @@ public interface SshConstants {
      * list index matches the {@code PROPOSAL_XXX} constant
      * @see <A HREF="http://tools.ietf.org/html/rfc4253#section-7.1">RFC-4253 - section 7.1</A>
      */
-    static final String[] PROPOSAL_KEX_NAMES = {
-            "kex algorithms",
-            "server host key algorithms",
-            "encryption algorithms (client to server)",
-            "encryption algorithms (server to client)",
-            "mac algorithms (client to server)",
-            "mac algorithms (server to client)",
-            "compression algorithms (client to server)",
-            "compression algorithms (server to client)",
-            "languages (client to server)",
-            "languages (server to client)"
-    };
+    static final List<String> PROPOSAL_KEX_NAMES =
+            Collections.unmodifiableList(Arrays.asList(
+                    "kex algorithms",
+                    "server host key algorithms",
+                    "encryption algorithms (client to server)",
+                    "encryption algorithms (server to client)",
+                    "mac algorithms (client to server)",
+                    "mac algorithms (server to client)",
+                    "compression algorithms (client to server)",
+                    "compression algorithms (server to client)",
+                    "languages (client to server)",
+                    "languages (server to client)"
+            ));
 
 
     //

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/common/Transformer.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/Transformer.java b/sshd-core/src/main/java/org/apache/sshd/common/Transformer.java
deleted file mode 100644
index 952b2c0..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/Transformer.java
+++ /dev/null
@@ -1,58 +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.common;
-
-import java.util.Objects;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface Transformer<I, O> {
-    // TODO in JDK-8 replace this with Function
-    /**
-     * @param input Input value
-     * @return Transformed output value
-     */
-    O transform(I input);
-
-    /**
-     * Invokes {@link Objects#toString(Object)} on the argument
-     */
-    Transformer<Object,String> TOSTRING=new Transformer<Object,String>() {
-            @Override
-            public String transform(Object input) {
-                return Objects.toString(input);
-            }
-        };
-
-    /**
-     * Returns {@link Enum#name()} or {@code null} if argument is {@code null}
-     */
-    Transformer<Enum<?>,String> ENUM_NAME_EXTRACTOR=new Transformer<Enum<?>,String>() {
-            @Override
-            public String transform(Enum<?> input) {
-                if (input == null) {
-                    return null;
-                } else {
-                    return input.name();
-                }
-            }
-        };
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/common/channel/Window.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/channel/Window.java b/sshd-core/src/main/java/org/apache/sshd/common/channel/Window.java
index bf2a91a..d72395e 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/channel/Window.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/channel/Window.java
@@ -25,9 +25,9 @@ import java.util.Map;
 import org.apache.sshd.common.FactoryManager;
 import org.apache.sshd.common.FactoryManagerUtils;
 import org.apache.sshd.common.session.Session;
-import org.apache.sshd.common.util.AbstractLoggingBean;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
 
 /**
  * A Window for a given channel.

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherFactory.java
index 6f4963f..94834b4 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherFactory.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherFactory.java
@@ -21,7 +21,7 @@ package org.apache.sshd.common.cipher;
 
 import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.OptionalFeature;
-import org.apache.sshd.common.Transformer;
+import org.apache.sshd.common.util.Transformer;
 
 /**
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/common/cipher/ECCurves.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/cipher/ECCurves.java b/sshd-core/src/main/java/org/apache/sshd/common/cipher/ECCurves.java
index 0c0c66f..d3f0b34 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/cipher/ECCurves.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/cipher/ECCurves.java
@@ -24,7 +24,9 @@ import java.security.spec.ECFieldFp;
 import java.security.spec.ECParameterSpec;
 import java.security.spec.ECPoint;
 import java.security.spec.EllipticCurve;
+import java.util.ArrayList;
 import java.util.Collections;
+import java.util.List;
 import java.util.Map;
 import java.util.TreeMap;
 
@@ -58,16 +60,89 @@ public class ECCurves {
     }
 
     /**
+     * Key=curve name, value=num. of bits
+     */
+    private static final Map<String,Integer> CURVENAME2SIZE =
+            Collections.unmodifiableMap(new TreeMap<String,Integer>(String.CASE_INSENSITIVE_ORDER) {
+                private static final long serialVersionUID = 1L;    // we're not serializing it
+                
+                {
+                    put(NISTP256, Integer.valueOf(256));
+                    put(NISTP384, Integer.valueOf(384));
+                    put(NISTP521, Integer.valueOf(521));
+                }
+        
+            });
+
+    /**
+     * An un-modifiable {@link List} of all the known curve names
+     */
+    @SuppressWarnings("synthetic-access")
+    public static final List<String> NAMES =
+            Collections.unmodifiableList(new ArrayList<String>(CURVENAME2SIZE.size()) {
+                private static final long serialVersionUID = 1L;    // we're not serializing it
+                
+                {
+                    addAll(CURVENAME2SIZE.keySet());
+                    Collections.sort(this); // as a courtesy
+                }
+            });
+
+    /**
+     * An un-modifiable {@link List} of all the known curve types according to {@code OpenSSH}
+     */
+    public static final List<String> TYPES =
+            Collections.unmodifiableList(new ArrayList<String>(CURVENAME2SIZE.size()) {
+                private static final long serialVersionUID = 1L;    // we're not serializing it
+                
+                {
+                    for (String n : NAMES) {
+                        add(ECDSA_SHA2_PREFIX + n);
+                    }
+
+                    Collections.sort(this); // as a courtesy
+                }
+            });
+    /**
+     * An un-modifiable {@link List} of all the known curve sizes
+     */
+    @SuppressWarnings("synthetic-access")
+    public static final List<Integer> SIZES =
+            Collections.unmodifiableList(new ArrayList<Integer>(CURVENAME2SIZE.size()) {
+                    private static final long serialVersionUID = 1L;    // we're not serializing it
+                
+                    {
+                        addAll(CURVENAME2SIZE.values());
+                        Collections.sort(this); // as a courtesy
+                    }
+            });
+    
+    /**
+     * @param name The curve name - ignored if {@code null}/empty
+     * @return The curve size - {@code null} if unknown curve
+     */
+    public static Integer getCurveSize(String name) {
+        if (GenericUtils.isEmpty(name)) {
+            return null;
+        } else {
+            return CURVENAME2SIZE.get(name);
+        }
+    }
+
+    /**
      * Key=num. of bits, value=curve name
      */
+    @SuppressWarnings("synthetic-access")
     private static final Map<Integer, String> SIZE2CURVENAME = 
             Collections.unmodifiableMap(new TreeMap<Integer, String>() {
                 private static final long serialVersionUID = 1L;    // we're not serializing it
         
                 {
-                    put(Integer.valueOf(256), NISTP256);
-                    put(Integer.valueOf(384), NISTP384);
-                    put(Integer.valueOf(521), NISTP521);
+                    for (Map.Entry<String,Integer> e : CURVENAME2SIZE.entrySet()) {
+                        String name = e.getKey();
+                        Integer size = e.getValue();
+                        put(size, name);
+                    }
                 }
             });
 
@@ -234,7 +309,7 @@ public class ECCurves {
     
     private static final class LazySpecsMapHolder {
         private static final Map<String,ECParameterSpec> specsMap =
-                Collections.unmodifiableMap(new TreeMap<String, ECParameterSpec>(String.CASE_INSENSITIVE_ORDER) {
+                Collections.unmodifiableMap(new TreeMap<String,ECParameterSpec>(String.CASE_INSENSITIVE_ORDER) {
                         private static final long serialVersionUID = 1L;    // we're not serializing it
                     
                         {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionFactory.java
index 3af29d7..9895922 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionFactory.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionFactory.java
@@ -21,7 +21,7 @@ package org.apache.sshd.common.compression;
 
 import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.OptionalFeature;
-import org.apache.sshd.common.Transformer;
+import org.apache.sshd.common.util.Transformer;
 
 /**
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/common/config/SshConfigFileReader.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/SshConfigFileReader.java b/sshd-core/src/main/java/org/apache/sshd/common/config/SshConfigFileReader.java
index e15d16f..92587a3 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/SshConfigFileReader.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/config/SshConfigFileReader.java
@@ -39,7 +39,6 @@ import org.apache.sshd.client.ClientBuilder;
 import org.apache.sshd.client.SshClient;
 import org.apache.sshd.common.AbstractFactoryManager;
 import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.Transformer;
 import org.apache.sshd.common.cipher.BuiltinCiphers;
 import org.apache.sshd.common.cipher.Cipher;
 import org.apache.sshd.common.cipher.CipherFactory;
@@ -57,6 +56,7 @@ import org.apache.sshd.common.signature.BuiltinSignatures;
 import org.apache.sshd.common.signature.Signature;
 import org.apache.sshd.common.signature.SignatureFactory;
 import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.Transformer;
 import org.apache.sshd.common.util.ValidateUtils;
 import org.apache.sshd.common.util.io.NoCloseInputStream;
 import org.apache.sshd.common.util.io.NoCloseReader;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/common/config/keys/AbstractPublicKeyEntryDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/AbstractPublicKeyEntryDecoder.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/AbstractPublicKeyEntryDecoder.java
index 763a8ce..8c701a7 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/AbstractPublicKeyEntryDecoder.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/AbstractPublicKeyEntryDecoder.java
@@ -28,31 +28,80 @@ import java.math.BigInteger;
 import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
 import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
 import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.PrivateKey;
 import java.security.PublicKey;
 import java.security.spec.InvalidKeySpecException;
 import java.security.spec.KeySpec;
 import java.util.Collection;
 
 import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.IoUtils;
 import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.io.IoUtils;
 
 /**
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
-public abstract class AbstractPublicKeyEntryDecoder<K extends PublicKey> implements PublicKeyEntryDecoder<K> {
-    private final Class<K> keyType;
+public abstract class AbstractPublicKeyEntryDecoder<PUB extends PublicKey,PRV extends PrivateKey>
+               implements PublicKeyEntryDecoder<PUB,PRV> {
+    private final Class<PUB> pubType;
+    private final Class<PRV> prvType;
     private final Collection<String>    names;
     
-    protected AbstractPublicKeyEntryDecoder(Class<K> keyType, Collection<String> names) {
-        this.keyType = ValidateUtils.checkNotNull(keyType, "No key type specified", GenericUtils.EMPTY_OBJECT_ARRAY);
+    protected AbstractPublicKeyEntryDecoder(Class<PUB> pubType, Class<PRV> prvType, Collection<String> names) {
+        this.pubType = ValidateUtils.checkNotNull(pubType, "No public key type specified", GenericUtils.EMPTY_OBJECT_ARRAY);
+        this.prvType = ValidateUtils.checkNotNull(prvType, "No private key type specified", GenericUtils.EMPTY_OBJECT_ARRAY);
         this.names = ValidateUtils.checkNotNullAndNotEmpty(names, "No type names provided", GenericUtils.EMPTY_OBJECT_ARRAY);
     }
 
     @Override
-    public final Class<K> getKeyType() {
-        return keyType;
+    public final Class<PUB> getPublicKeyType() {
+        return pubType;
+    }
+
+    @Override
+    public final Class<PRV> getPrivateKeyType() {
+        return prvType;
+    }
+
+    @Override
+    public KeyPair cloneKeyPair(KeyPair kp) throws GeneralSecurityException {
+        if (kp == null) {
+            return null;
+        }
+        
+        PUB pubCloned = null;
+        {
+            PublicKey pubOriginal = kp.getPublic();
+            Class<PUB> pubExpected = getPublicKeyType();
+            if (pubOriginal != null) {
+                Class<?> orgType = pubOriginal.getClass();
+                if (!pubExpected.isAssignableFrom(orgType)) {
+                    throw new InvalidKeyException("Mismatched public key types: expected=" + pubExpected.getSimpleName() + ", actual=" + orgType.getSimpleName());
+                }
+                
+                pubCloned = clonePublicKey(pubExpected.cast(pubOriginal));
+            }
+        }
+
+        PRV prvCloned = null;
+        {
+            PrivateKey prvOriginal = kp.getPrivate();
+            Class<PRV> prvExpected = getPrivateKeyType();
+            if (prvOriginal != null) {
+                Class<?> orgType = prvOriginal.getClass();
+                if (!prvExpected.isAssignableFrom(orgType)) {
+                    throw new InvalidKeyException("Mismatched private key types: expected=" + prvExpected.getSimpleName() + ", actual=" + orgType.getSimpleName());
+                }
+                
+                prvCloned = clonePrivateKey(prvExpected.cast(prvOriginal));
+            }
+        }
+
+        return new KeyPair(pubCloned, prvCloned);
     }
 
     @Override
@@ -61,12 +110,12 @@ public abstract class AbstractPublicKeyEntryDecoder<K extends PublicKey> impleme
     }
 
     @Override
-    public K decodePublicKey(byte... keyData) throws IOException, GeneralSecurityException {
+    public PUB decodePublicKey(byte... keyData) throws IOException, GeneralSecurityException {
         return decodePublicKey(keyData, 0, GenericUtils.length(keyData));
     }
 
     @Override
-    public K decodePublicKey(byte[] keyData, int offset, int length) throws IOException, GeneralSecurityException {
+    public PUB decodePublicKey(byte[] keyData, int offset, int length) throws IOException, GeneralSecurityException {
         if (length <= 0) {
             return null;
         }
@@ -77,7 +126,7 @@ public abstract class AbstractPublicKeyEntryDecoder<K extends PublicKey> impleme
     }
 
     @Override
-    public K decodePublicKey(InputStream keyData) throws IOException, GeneralSecurityException {
+    public PUB decodePublicKey(InputStream keyData) throws IOException, GeneralSecurityException {
         // the actual data is preceded by a string that repeats the key type
         String type = decodeString(keyData);
         if (GenericUtils.isEmpty(type)) {
@@ -92,13 +141,17 @@ public abstract class AbstractPublicKeyEntryDecoder<K extends PublicKey> impleme
         return decodePublicKey(type, keyData);
     }
 
-    public K generatePublicKey(KeySpec keySpec) throws GeneralSecurityException {
+    public PUB generatePublicKey(KeySpec keySpec) throws GeneralSecurityException {
         KeyFactory  factory = getKeyFactoryInstance();
-        Class<K>    keyType = getKeyType();
+        Class<PUB>    keyType = getPublicKeyType();
         return keyType.cast(factory.generatePublic(keySpec));
     }
 
-    public abstract KeyFactory getKeyFactoryInstance() throws GeneralSecurityException;
+    public PRV generatePrivateKey(KeySpec keySpec) throws GeneralSecurityException {
+        KeyFactory  factory = getKeyFactoryInstance();
+        Class<PRV>    keyType = getPrivateKeyType();
+        return keyType.cast(factory.generatePrivate(keySpec));
+    }
 
     /**
      * @param keyType The reported / encode key type
@@ -108,11 +161,18 @@ public abstract class AbstractPublicKeyEntryDecoder<K extends PublicKey> impleme
      * @throws IOException If failed to read from the data stream
      * @throws GeneralSecurityException If failed to generate the key
      */
-    public abstract K decodePublicKey(String keyType, InputStream keyData) throws IOException, GeneralSecurityException;
+    public abstract PUB decodePublicKey(String keyType, InputStream keyData) throws IOException, GeneralSecurityException;
+
+    @Override
+    public KeyPair generateKeyPair(int keySize) throws GeneralSecurityException {
+        KeyPairGenerator    gen=getKeyPairGenerator();
+        gen.initialize(keySize);
+        return gen.generateKeyPair();
+    }
 
     @Override
     public String toString() {
-        return getKeyType().getSimpleName() + ": " + getSupportedTypeNames();
+        return getPublicKeyType().getSimpleName() + ": " + getSupportedTypeNames();
     }
 
     public static final int encodeString(OutputStream s, String v) throws IOException {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/common/config/keys/DSSPublicKeyEntryDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/DSSPublicKeyEntryDecoder.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/DSSPublicKeyEntryDecoder.java
index 05af8ba..b48d3cf 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/DSSPublicKeyEntryDecoder.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/DSSPublicKeyEntryDecoder.java
@@ -24,9 +24,13 @@ import java.io.InputStream;
 import java.io.OutputStream;
 import java.math.BigInteger;
 import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
 import java.security.KeyFactory;
+import java.security.KeyPairGenerator;
 import java.security.interfaces.DSAParams;
+import java.security.interfaces.DSAPrivateKey;
 import java.security.interfaces.DSAPublicKey;
+import java.security.spec.DSAPrivateKeySpec;
 import java.security.spec.DSAPublicKeySpec;
 import java.security.spec.InvalidKeySpecException;
 import java.util.Collections;
@@ -39,11 +43,11 @@ import org.apache.sshd.common.util.ValidateUtils;
 /**
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
-public class DSSPublicKeyEntryDecoder extends AbstractPublicKeyEntryDecoder<DSAPublicKey> {
+public class DSSPublicKeyEntryDecoder extends AbstractPublicKeyEntryDecoder<DSAPublicKey,DSAPrivateKey> {
     public static final DSSPublicKeyEntryDecoder INSTANCE = new DSSPublicKeyEntryDecoder();
 
     public DSSPublicKeyEntryDecoder() {
-        super(DSAPublicKey.class, Collections.unmodifiableList(Collections.singletonList(KeyPairProvider.SSH_DSS)));
+        super(DSAPublicKey.class, DSAPrivateKey.class, Collections.unmodifiableList(Collections.singletonList(KeyPairProvider.SSH_DSS)));
     }
 
     @Override
@@ -75,6 +79,39 @@ public class DSSPublicKeyEntryDecoder extends AbstractPublicKeyEntryDecoder<DSAP
     }
 
     @Override
+    public DSAPublicKey clonePublicKey(DSAPublicKey key) throws GeneralSecurityException {
+        if (key == null) {
+            return null;
+        }
+        
+        DSAParams params = key.getParams();
+        if (params == null) {
+            throw new InvalidKeyException("Missing parameters in key"); 
+        }
+
+        return generatePublicKey(new DSAPublicKeySpec(key.getY(), params.getP(), params.getQ(), params.getG()));
+    }
+
+    @Override
+    public DSAPrivateKey clonePrivateKey(DSAPrivateKey key) throws GeneralSecurityException {
+        if (key == null) {
+            return null;
+        }
+        
+        DSAParams params = key.getParams();
+        if (params == null) {
+            throw new InvalidKeyException("Missing parameters in key"); 
+        }
+
+        return generatePrivateKey(new DSAPrivateKeySpec(key.getX(), params.getP(), params.getQ(), params.getG()));
+    }
+
+    @Override
+    public KeyPairGenerator getKeyPairGenerator() throws GeneralSecurityException {
+        return SecurityUtils.getKeyPairGenerator("DSA");
+    }
+
+    @Override
     public KeyFactory getKeyFactoryInstance() throws GeneralSecurityException {
         return SecurityUtils.getKeyFactory("DSA");
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/common/config/keys/ECDSAPublicKeyEntryDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/ECDSAPublicKeyEntryDecoder.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/ECDSAPublicKeyEntryDecoder.java
index 3325a26..feba09c 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/ECDSAPublicKeyEntryDecoder.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/ECDSAPublicKeyEntryDecoder.java
@@ -26,20 +26,23 @@ import java.io.OutputStream;
 import java.io.StreamCorruptedException;
 import java.math.BigInteger;
 import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
 import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
 import java.security.NoSuchProviderException;
+import java.security.interfaces.ECPrivateKey;
 import java.security.interfaces.ECPublicKey;
 import java.security.spec.ECParameterSpec;
 import java.security.spec.ECPoint;
+import java.security.spec.ECPrivateKeySpec;
 import java.security.spec.ECPublicKeySpec;
 import java.security.spec.InvalidKeySpecException;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.EnumSet;
 import java.util.Set;
 
 import org.apache.sshd.common.cipher.ECCurves;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.SecurityUtils;
 import org.apache.sshd.common.util.ValidateUtils;
@@ -48,13 +51,11 @@ import org.apache.sshd.common.util.buffer.BufferUtils;
 /**
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
-public class ECDSAPublicKeyEntryDecoder extends AbstractPublicKeyEntryDecoder<ECPublicKey> {
+public class ECDSAPublicKeyEntryDecoder extends AbstractPublicKeyEntryDecoder<ECPublicKey,ECPrivateKey> {
     public static final ECDSAPublicKeyEntryDecoder INSTANCE = new ECDSAPublicKeyEntryDecoder();
 
     public ECDSAPublicKeyEntryDecoder() {
-        super(ECPublicKey.class,
-              Collections.unmodifiableList(
-                  Arrays.asList(KeyPairProvider.ECDSA_SHA2_NISTP256, KeyPairProvider.ECDSA_SHA2_NISTP384, KeyPairProvider.ECDSA_SHA2_NISTP521)));
+        super(ECPublicKey.class, ECPrivateKey.class, ECCurves.TYPES);
     }
 
     @Override
@@ -96,6 +97,42 @@ public class ECDSAPublicKeyEntryDecoder extends AbstractPublicKeyEntryDecoder<EC
     }
 
     @Override
+    public ECPublicKey clonePublicKey(ECPublicKey key) throws GeneralSecurityException {
+        if (!SecurityUtils.hasEcc()) {
+            throw new NoSuchProviderException("ECC not supported");
+        }
+
+        if (key == null) {
+            return null;
+        }
+        
+        ECParameterSpec params = key.getParams();
+        if (params == null) {
+            throw new InvalidKeyException("Missing parameters in key");
+        }
+
+        return generatePublicKey(new ECPublicKeySpec(key.getW(), params));
+    }
+
+    @Override
+    public ECPrivateKey clonePrivateKey(ECPrivateKey key) throws GeneralSecurityException {
+        if (!SecurityUtils.hasEcc()) {
+            throw new NoSuchProviderException("ECC not supported");
+        }
+
+        if (key == null) {
+            return null;
+        }
+        
+        ECParameterSpec params = key.getParams();
+        if (params == null) {
+            throw new InvalidKeyException("Missing parameters in key");
+        }
+
+        return generatePrivateKey(new ECPrivateKeySpec(key.getS(), params));
+    }
+
+    @Override
     public String encodePublicKey(OutputStream s, ECPublicKey key) throws IOException {
         ValidateUtils.checkNotNull(key, "No public key provided", GenericUtils.EMPTY_OBJECT_ARRAY);
         
@@ -118,6 +155,32 @@ public class ECDSAPublicKeyEntryDecoder extends AbstractPublicKeyEntryDecoder<EC
         }
     }
 
+    @Override
+    public KeyPair generateKeyPair(int keySize) throws GeneralSecurityException {
+        String curveName = ECCurves.getCurveName(keySize);
+        if (GenericUtils.isEmpty(curveName)) {
+            throw new InvalidKeySpecException("Unknown curve for key size=" + keySize);
+        }
+        
+        ECParameterSpec params = ECCurves.getECParameterSpec(curveName);
+        if (params == null) {
+            throw new InvalidKeySpecException("No curve parameters available for " + curveName);
+        }
+
+        KeyPairGenerator gen = getKeyPairGenerator();
+        gen.initialize(params);
+        return gen.generateKeyPair();
+    }
+
+    @Override
+    public KeyPairGenerator getKeyPairGenerator() throws GeneralSecurityException {
+        if (SecurityUtils.hasEcc()) {
+            return SecurityUtils.getKeyPairGenerator("EC");
+        } else {
+            throw new NoSuchProviderException("ECC not supported");
+        }
+    }
+
     // see rfc5480 section 2.2
     public static final byte    ECPOINT_UNCOMPRESSED_FORM_INDICATOR=0x04;
     public static final byte    ECPOINT_COMPRESSED_VARIANT_2=0x02;