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:16 UTC

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

Repository: mina-sshd
Updated Branches:
  refs/heads/master ff6a4b04f -> 6432a7af3


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/test/java/org/apache/sshd/common/random/RandomTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/random/RandomTest.java b/sshd-core/src/test/java/org/apache/sshd/common/random/RandomTest.java
new file mode 100644
index 0000000..bd293f6
--- /dev/null
+++ b/sshd-core/src/test/java/org/apache/sshd/common/random/RandomTest.java
@@ -0,0 +1,58 @@
+/*
+ * 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.random;
+
+import org.apache.sshd.common.random.BouncyCastleRandom;
+import org.apache.sshd.common.random.JceRandom;
+import org.apache.sshd.common.random.Random;
+import org.apache.sshd.util.BaseTestSupport;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+/**
+ * TODO Add javadoc
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class RandomTest extends BaseTestSupport {
+
+    @Test
+    public void testJce() {
+        long t = test(new JceRandom());
+        System.out.println("JCE: " + t + " micro");
+    }
+
+    @Test
+    public void testBc() {
+        long t = test(new BouncyCastleRandom());
+        System.out.println("BC:  " + t + " micro");
+    }
+
+    protected long test(Random random) {
+        byte[] bytes = new byte[32];
+        long l0 = System.nanoTime();
+        for (int i = 0; i < 1000; i++) {
+            random.fill(bytes, 8, 16);
+        }
+        long l1 = System.nanoTime();
+        return (l1 - l0) / 1000;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/test/java/org/apache/sshd/common/session/AbstractSessionTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/session/AbstractSessionTest.java b/sshd-core/src/test/java/org/apache/sshd/common/session/AbstractSessionTest.java
new file mode 100644
index 0000000..7ee8e11
--- /dev/null
+++ b/sshd-core/src/test/java/org/apache/sshd/common/session/AbstractSessionTest.java
@@ -0,0 +1,147 @@
+/*
+ * 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.session;
+
+import java.io.IOException;
+
+import org.apache.sshd.common.session.AbstractSession;
+import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
+import org.apache.sshd.server.SshServer;
+import org.apache.sshd.util.BaseTestSupport;
+import org.junit.Before;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+/**
+ * Test basic stuff on AbstractSession.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class AbstractSessionTest extends BaseTestSupport {
+
+    private MySession session;
+
+    @Before
+    public void setUp() throws Exception {
+        session = new MySession();
+    }
+
+    @Test
+    public void testReadIdentSimple() {
+        Buffer buf = new ByteArrayBuffer("SSH-2.0-software\r\n".getBytes());
+        String ident = session.doReadIdentification(buf);
+        assertEquals("SSH-2.0-software", ident);
+    }
+
+    @Test
+    public void testReadIdentWithoutCR() {
+        Buffer buf = new ByteArrayBuffer("SSH-2.0-software\n".getBytes());
+        String ident = session.doReadIdentification(buf);
+        assertEquals("SSH-2.0-software", ident);
+    }
+
+    @Test
+    public void testReadIdentWithHeaders() {
+        Buffer buf = new ByteArrayBuffer(("a header line\r\nSSH-2.0-software\r\n").getBytes());
+        String ident = session.doReadIdentification(buf);
+        assertEquals("SSH-2.0-software", ident);
+    }
+
+    @Test
+    public void testReadIdentWithSplitPackets() {
+        Buffer buf = new ByteArrayBuffer("header line\r\nSSH".getBytes());
+        String ident = session.doReadIdentification(buf);
+        assertNull(ident);
+        buf.putRawBytes("-2.0-software\r\n".getBytes());
+        ident = session.doReadIdentification(buf);
+        assertEquals("SSH-2.0-software", ident);
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testReadIdentBadLineEnding() {
+        Buffer buf = new ByteArrayBuffer(("SSH-2.0-software\ra").getBytes());
+        String ident = session.doReadIdentification(buf);
+        fail("Unexpected success: " + ident);
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testReadIdentLongLine() {
+        Buffer buf = new ByteArrayBuffer(("SSH-2.0-software" +
+                "01234567890123456789012345678901234567890123456789" +
+                "01234567890123456789012345678901234567890123456789" +
+                "01234567890123456789012345678901234567890123456789" +
+                "01234567890123456789012345678901234567890123456789" +
+                "01234567890123456789012345678901234567890123456789" +
+                "01234567890123456789012345678901234567890123456789").getBytes());
+        String ident = session.doReadIdentification(buf);
+        fail("Unexpected success: " + ident);
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testReadIdentLongHeader() {
+        StringBuilder sb = new StringBuilder(32768);
+        for (int i = 0; i < 500; i++) {
+            sb.append("01234567890123456789012345678901234567890123456789\r\n");
+        }
+        sb.append("SSH-2.0-software\r\n");
+        Buffer buf = new ByteArrayBuffer(sb.toString().getBytes());
+        String ident = session.doReadIdentification(buf);
+        fail("Unexpected success: " + ident);
+    }
+
+    public static class MySession extends AbstractSession {
+        public MySession() {
+            super(true, SshServer.setUpDefaultServer(), null);
+        }
+        @Override
+        protected void handleMessage(Buffer buffer) throws Exception {
+            // ignored
+        }
+        @Override
+        protected boolean readIdentification(Buffer buffer) {
+            return false;
+        }
+        public String doReadIdentification(Buffer buffer) {
+            return super.doReadIdentification(buffer, false);
+        }
+        @Override
+        protected void sendKexInit() throws IOException {
+            // ignored
+        }
+        @Override
+        protected void checkKeys() {
+            // ignored
+        }
+        @Override
+        protected void receiveKexInit(Buffer buffer) throws IOException {
+            // ignored
+        }
+        @Override
+        public void startService(String name) throws Exception {
+            // ignored
+        }
+        @Override
+        public void resetIdleTimeout() {
+            // ignored
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/test/java/org/apache/sshd/common/util/GenericUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/GenericUtilsTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/GenericUtilsTest.java
index 7d3628a..5504365 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/GenericUtilsTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/util/GenericUtilsTest.java
@@ -22,6 +22,7 @@ package org.apache.sshd.common.util;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+import java.util.NoSuchElementException;
 
 import org.apache.sshd.util.BaseTestSupport;
 import org.junit.FixMethodOrder;
@@ -116,4 +117,30 @@ public class GenericUtilsTest extends BaseTestSupport {
             assertSame("Mismatched result for delim at index=" + index, expected, GenericUtils.stripDelimiters(expected, delim));
         }            
     }
+
+    @Test
+    public void testAccumulateExceptionOnNullValues() {
+        assertNull("Unexpected null/null result", GenericUtils.accumulateException(null, null));
+        
+        Throwable expected=new NoSuchMethodException(getClass().getName() + "#" + getCurrentTestName());
+        assertSame("Mismatched null/extra result", expected, GenericUtils.accumulateException(null, expected));
+        assertSame("Mismatched current/null result", expected, GenericUtils.accumulateException(expected, null));
+    }
+
+    @Test
+    public void testAccumulateExceptionOnExistingCurrent() {
+        RuntimeException[] expected=new RuntimeException[] {
+                new IllegalArgumentException(getCurrentTestName()),
+                new ClassCastException(getClass().getName()),
+                new NoSuchElementException(getClass().getPackage().getName())
+            };
+        RuntimeException    current=new UnsupportedOperationException("top");
+        for (RuntimeException extra : expected) {
+            RuntimeException    actual=GenericUtils.accumulateException(current, extra);
+            assertSame("Mismatched returned actual exception", current, actual);
+        }
+        
+        Throwable[] actual=current.getSuppressed();
+        assertArrayEquals("Suppressed", expected, actual);
+    }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/test/java/org/apache/sshd/common/util/io/EmptyInputStreamTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/io/EmptyInputStreamTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/io/EmptyInputStreamTest.java
new file mode 100644
index 0000000..f7c1e56
--- /dev/null
+++ b/sshd-core/src/test/java/org/apache/sshd/common/util/io/EmptyInputStreamTest.java
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   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.util.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.sshd.common.util.buffer.BufferUtils;
+import org.apache.sshd.util.BaseTestSupport;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class EmptyInputStreamTest extends BaseTestSupport {
+    public EmptyInputStreamTest() {
+        super();
+    }
+
+    @Test
+    public void testEmptyInputStream() throws IOException {
+        try(EmptyInputStream in = new EmptyInputStream()) {
+            testEmptyInputStream(in, false);
+        }
+    }
+
+    @Test
+    public void testCloseableEmptyInputStream() throws IOException {
+        try(EmptyInputStream in = new CloseableEmptyInputStream()) {
+            testEmptyInputStream(in, true);
+        }
+    }
+
+    private void testEmptyInputStream(InputStream in, boolean failAfterClose) throws IOException {
+        testEmptyInputStream("open", in, false);
+        in.close();
+        testEmptyInputStream("closed", in, failAfterClose);
+    }
+
+    private void testEmptyInputStream(String message, InputStream in, boolean errorExpected) {
+        assertFalse(message + ": unexpected markSupported()", in.markSupported());
+        try {
+            in.mark(Long.SIZE);
+            fail(message + ": unexpected mark success");
+        } catch(UnsupportedOperationException e) {
+            // expected
+        }
+
+        try {
+            int len = in.available();
+            assertFalse(message + ": Unexpected success in available(): " + len, errorExpected);
+            assertEquals(message + ": Mismatched available() result", 0, len);
+        } catch(IOException e) {
+            assertTrue(message + ": Unexpected error on available(): " + e.getMessage(), errorExpected);
+        }
+
+        try {
+            int data = in.read();
+            assertFalse(message + ": Unexpected success in read(): " + data, errorExpected);
+            assertEquals(message + ": Mismatched read() result", (-1), data);
+        } catch(IOException e) {
+            assertTrue(message + ": Unexpected error on read(): " + e.getMessage(), errorExpected);
+        }
+
+        byte[] bytes = new byte[Byte.SIZE];
+        try {
+            int len = in.read(bytes);
+            assertFalse(message + ": Unexpected success in read([]): " + BufferUtils.printHex(':', bytes), errorExpected);
+            assertEquals(message + ": Mismatched read([]) result", (-1), len);
+        } catch(IOException e) {
+            assertTrue(message + ": Unexpected error on read([]): " + e.getMessage(), errorExpected);
+        }
+
+        try {
+            int len = in.read(bytes, 0, bytes.length);
+            assertFalse(message + ": Unexpected success in read([],int,int): " + BufferUtils.printHex(':', bytes), errorExpected);
+            assertEquals(message + ": Mismatched read([],int,int) result", (-1), len);
+        } catch(IOException e) {
+            assertTrue(message + ": Unexpected error on read([],int,int): " + e.getMessage(), errorExpected);
+        }
+
+        try {
+            long len = in.skip(Byte.MAX_VALUE);
+            assertFalse(message + ": Unexpected success in skip(): " + len, errorExpected);
+            assertEquals(message + ": Mismatched skip() result", 0L, len);
+        } catch(IOException e) {
+            assertTrue(message + ": Unexpected error on skip(): " + e.getMessage(), errorExpected);
+        }
+
+        try {
+            in.reset();
+            assertFalse(message + ": Unexpected success in reset()", errorExpected);
+        } catch(IOException e) {
+            assertTrue(message + ": Unexpected error on reset(): " + e.getMessage(), errorExpected);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/test/java/org/apache/sshd/deprecated/AbstractUserAuth.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/deprecated/AbstractUserAuth.java b/sshd-core/src/test/java/org/apache/sshd/deprecated/AbstractUserAuth.java
index e7ab084..3cbd5a4 100644
--- a/sshd-core/src/test/java/org/apache/sshd/deprecated/AbstractUserAuth.java
+++ b/sshd-core/src/test/java/org/apache/sshd/deprecated/AbstractUserAuth.java
@@ -19,7 +19,7 @@
 package org.apache.sshd.deprecated;
 
 import org.apache.sshd.client.session.ClientSessionImpl;
-import org.apache.sshd.common.util.AbstractLoggingBean;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
 
 /**
  */

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/test/java/org/apache/sshd/server/ServerMain.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/server/ServerMain.java b/sshd-core/src/test/java/org/apache/sshd/server/ServerMain.java
new file mode 100644
index 0000000..6c9fb10
--- /dev/null
+++ b/sshd-core/src/test/java/org/apache/sshd/server/ServerMain.java
@@ -0,0 +1,34 @@
+/*
+ * 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.server;
+
+import org.apache.sshd.server.SshServer;
+
+
+/**
+ * An activator for the {@link SshServer#main(String[])} - the reason it is
+ * here is because the logging configuration is available only for test scope
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class ServerMain {
+    public static void main(String[] args) throws Throwable {
+        SshServer.main(args);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/test/java/org/apache/sshd/server/ServerTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/server/ServerTest.java b/sshd-core/src/test/java/org/apache/sshd/server/ServerTest.java
new file mode 100644
index 0000000..8cc32ea
--- /dev/null
+++ b/sshd-core/src/test/java/org/apache/sshd/server/ServerTest.java
@@ -0,0 +1,561 @@
+/*
+ * 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.server;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import java.net.SocketAddress;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.sshd.client.SessionFactory;
+import org.apache.sshd.client.SshClient;
+import org.apache.sshd.client.channel.ChannelExec;
+import org.apache.sshd.client.channel.ChannelShell;
+import org.apache.sshd.client.future.AuthFuture;
+import org.apache.sshd.client.session.ClientConnectionService;
+import org.apache.sshd.client.session.ClientSession;
+import org.apache.sshd.client.session.ClientSessionImpl;
+import org.apache.sshd.common.FactoryManager;
+import org.apache.sshd.common.FactoryManagerUtils;
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.SshConstants;
+import org.apache.sshd.common.channel.Channel;
+import org.apache.sshd.common.channel.WindowClosedException;
+import org.apache.sshd.common.io.IoSession;
+import org.apache.sshd.common.session.AbstractConnectionService;
+import org.apache.sshd.common.session.AbstractSession;
+import org.apache.sshd.common.session.Session;
+import org.apache.sshd.common.session.SessionListener;
+import org.apache.sshd.deprecated.ClientUserAuthServiceOld;
+import org.apache.sshd.deprecated.UserAuthPassword;
+import org.apache.sshd.server.Command;
+import org.apache.sshd.server.CommandFactory;
+import org.apache.sshd.server.Environment;
+import org.apache.sshd.server.ExitCallback;
+import org.apache.sshd.server.ServerFactoryManager;
+import org.apache.sshd.server.SshServer;
+import org.apache.sshd.server.command.ScpCommandFactory;
+import org.apache.sshd.server.sftp.SftpSubsystemFactory;
+import org.apache.sshd.util.BaseTestSupport;
+import org.apache.sshd.util.BogusPasswordAuthenticator;
+import org.apache.sshd.util.EchoShellFactory;
+import org.apache.sshd.util.Utils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * TODO Add javadoc
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class ServerTest extends BaseTestSupport {
+
+    private SshServer sshd;
+    private SshClient client;
+    private int port;
+
+    @Before
+    public void setUp() throws Exception {
+        sshd = SshServer.setUpDefaultServer();
+        sshd.setKeyPairProvider(Utils.createTestHostKeyProvider());
+        sshd.setShellFactory(new TestEchoShellFactory());
+        sshd.setPasswordAuthenticator(BogusPasswordAuthenticator.INSTANCE);
+        sshd.setSessionFactory(new org.apache.sshd.server.session.SessionFactory());
+        sshd.start();
+        port = sshd.getPort();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        if (sshd != null) {
+            sshd.stop(true);
+        }
+        if (client != null) {
+            client.stop();
+        }
+    }
+
+    /**
+     * Send bad password.  The server should disconnect after a few attempts
+     * @throws Exception
+     */
+    @Test
+    public void testFailAuthenticationWithWaitFor() throws Exception {
+        final int   MAX_AUTH_REQUESTS=10;
+        FactoryManagerUtils.updateProperty(sshd, ServerFactoryManager.MAX_AUTH_REQUESTS, MAX_AUTH_REQUESTS);
+
+        client = SshClient.setUpDefaultClient();
+        client.setServiceFactories(Arrays.asList(
+                new ClientUserAuthServiceOld.Factory(),
+                new ClientConnectionService.Factory()
+        ));
+        client.start();
+        
+        try(ClientSession s = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
+            int nbTrials = 0;
+            int res = 0;
+            while ((res & ClientSession.CLOSED) == 0) {
+                nbTrials ++;
+                s.getService(ClientUserAuthServiceOld.class)
+                        .auth(new UserAuthPassword((ClientSessionImpl) s, "ssh-connection", "buggy"));
+                res = s.waitFor(ClientSession.CLOSED | ClientSession.WAIT_AUTH, 5000);
+                if (res == ClientSession.TIMEOUT) {
+                    throw new TimeoutException();
+                }
+            }
+            assertTrue("Number trials (" + nbTrials + ") below min.=" + MAX_AUTH_REQUESTS, nbTrials > MAX_AUTH_REQUESTS);
+        } finally {
+            client.stop();
+        }
+    }
+
+    @Test
+    public void testFailAuthenticationWithFuture() throws Exception {
+        final int   MAX_AUTH_REQUESTS=10;
+        FactoryManagerUtils.updateProperty(sshd, ServerFactoryManager.MAX_AUTH_REQUESTS, MAX_AUTH_REQUESTS);
+
+        client = SshClient.setUpDefaultClient();
+        client.setServiceFactories(Arrays.asList(
+                new ClientUserAuthServiceOld.Factory(),
+                new ClientConnectionService.Factory()
+        ));
+        client.start();
+        try(ClientSession s = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
+            int nbTrials = 0;
+            AuthFuture authFuture;
+            do {
+                nbTrials++;
+                assertTrue(nbTrials < 100);
+                authFuture = s.getService(ClientUserAuthServiceOld.class)
+                        .auth(new UserAuthPassword((ClientSessionImpl) s, "ssh-connection", "buggy"));
+                assertTrue("Authentication wait failed", authFuture.await(5000));
+                assertTrue("Authentication not done", authFuture.isDone());
+                assertFalse("Authentication unexpectedly successful", authFuture.isSuccess());
+            }
+            while (authFuture.isFailure());
+            assertNotNull("Missing auth future exception", authFuture.getException());
+            assertTrue("Number trials (" + nbTrials + ") below min.=" + MAX_AUTH_REQUESTS, nbTrials > MAX_AUTH_REQUESTS);
+        } finally {
+            client.stop();
+        }
+    }
+
+    @Test
+    public void testAuthenticationTimeout() throws Exception {
+        final int   AUTH_TIMEOUT=5000;
+        FactoryManagerUtils.updateProperty(sshd, FactoryManager.AUTH_TIMEOUT, AUTH_TIMEOUT);
+
+        client = SshClient.setUpDefaultClient();
+        client.start();
+        try(ClientSession s = client.connect("test", "localhost", port).await().getSession()) {
+            int res = s.waitFor(ClientSession.CLOSED, 2 * AUTH_TIMEOUT);
+            assertEquals("Session should be closed", ClientSession.CLOSED | ClientSession.WAIT_AUTH, res);
+        } finally {
+            client.stop();
+        }
+    }
+
+    @Test
+    public void testIdleTimeout() throws Exception {
+        final CountDownLatch latch = new CountDownLatch(1);
+        TestEchoShellFactory.TestEchoShell.latch = new CountDownLatch(1);
+        final int   IDLE_TIMEOUT=2500;
+        FactoryManagerUtils.updateProperty(sshd, FactoryManager.IDLE_TIMEOUT, IDLE_TIMEOUT);
+
+        sshd.getSessionFactory().addListener(new SessionListener() {
+            @Override
+            public void sessionCreated(Session session) {
+                System.out.println("Session created");
+            }
+            @Override
+            public void sessionEvent(Session session, Event event) {
+                System.out.println("Session event: " + event);
+            }
+            @Override
+            public void sessionClosed(Session session) {
+                System.out.println("Session closed");
+                latch.countDown();
+            }
+        });
+
+        client = SshClient.setUpDefaultClient();
+        client.start();
+        try(ClientSession s = client.connect("test", "localhost", port).await().getSession()) {
+            s.addPasswordIdentity("test");
+            s.auth().verify(5L, TimeUnit.SECONDS);
+
+            try(ChannelShell shell = s.createShellChannel();
+                ByteArrayOutputStream out = new ByteArrayOutputStream();
+                ByteArrayOutputStream err = new ByteArrayOutputStream()) {
+                shell.setOut(out);
+                shell.setErr(err);
+                shell.open().await();
+                int res = s.waitFor(ClientSession.CLOSED, 2 * IDLE_TIMEOUT);
+                assertEquals("Session should be closed", ClientSession.CLOSED | ClientSession.AUTHED, res);
+            }
+        } finally {
+            client.stop();
+        }
+
+        assertTrue(latch.await(1, TimeUnit.SECONDS));
+        assertTrue(TestEchoShellFactory.TestEchoShell.latch.await(1, TimeUnit.SECONDS));
+    }
+
+    /**
+     * The scenario is the following:
+     *  - create a command that sends continuous data to the client
+     *  - the client does not read the data, filling the ssh window and the tcp socket
+     *  - the server session becomes idle, but the ssh disconnect message can't be written
+     *  - the server session is forcibly closed
+     */
+    @Test
+    public void testServerIdleTimeoutWithForce() throws Exception {
+        final CountDownLatch latch = new CountDownLatch(1);
+
+        sshd.setCommandFactory(new StreamCommand.Factory());
+        
+        FactoryManagerUtils.updateProperty(sshd, FactoryManager.IDLE_TIMEOUT, 5000);
+        FactoryManagerUtils.updateProperty(sshd, FactoryManager.DISCONNECT_TIMEOUT, 2000);
+        sshd.getSessionFactory().addListener(new SessionListener() {
+            @Override
+            public void sessionCreated(Session session) {
+                System.out.println("Session created");
+            }
+
+            @Override
+            public void sessionEvent(Session session, Event event) {
+                System.out.println("Session event: " + event);
+            }
+
+            @Override
+            public void sessionClosed(Session session) {
+                System.out.println("Session closed");
+                latch.countDown();
+            }
+        });
+
+        client = SshClient.setUpDefaultClient();
+        client.start();
+
+        try(ClientSession s = client.connect("test", "localhost", port).await().getSession()) {
+            s.addPasswordIdentity("test");
+            s.auth().verify(5L, TimeUnit.SECONDS);
+
+            try(ChannelExec shell = s.createExecChannel("normal");
+                // Create a pipe that will block reading when the buffer is full
+                PipedInputStream pis = new PipedInputStream();
+                PipedOutputStream pos = new PipedOutputStream(pis)) {
+
+                shell.setOut(pos);
+                shell.open().verify(5L, TimeUnit.SECONDS);
+        
+                try(AbstractSession serverSession = sshd.getActiveSessions().iterator().next();
+                    Channel channel = serverSession.getService(AbstractConnectionService.class).getChannels().iterator().next()) {
+
+                    while (channel.getRemoteWindow().getSize() > 0) {
+                        Thread.sleep(1);
+                    }
+        
+                    LoggerFactory.getLogger(getClass()).info("Waiting for session idle timeouts");
+            
+                    long t0 = System.currentTimeMillis();
+                    latch.await(1, TimeUnit.MINUTES);
+                    long t1 = System.currentTimeMillis(), diff = t1 - t0;
+                    assertTrue("Wait time too low: " + diff, diff > 7000);
+                    assertTrue("Wait time too high: " + diff, diff < 10000);
+                }
+            }
+        } finally {
+            client.stop();
+        }
+    }
+
+    @Test
+    public void testLanguage() throws Exception {
+        client = SshClient.setUpDefaultClient();
+        client.setSessionFactory(new SessionFactory() {
+            @Override
+            protected AbstractSession createSession(IoSession ioSession) throws Exception {
+                return new ClientSessionImpl(client, ioSession) {
+                    @Override
+                    protected String[] createProposal(String hostKeyTypes) {
+                        String[] proposal = super.createProposal(hostKeyTypes);
+                        proposal[SshConstants.PROPOSAL_LANG_CTOS] = "en-US";
+                        proposal[SshConstants.PROPOSAL_LANG_STOC] = "en-US";
+                        return proposal;
+                    }
+                };
+            }
+        });
+        client.start();
+        try(ClientSession s = client.connect("test", "localhost", port).await().getSession()) {
+            s.close(false);
+        } finally {
+            client.stop();
+        }
+    }
+
+    @Test
+    public void testKexCompletedEvent() throws Exception {
+    	final AtomicInteger	serverEventCount=new AtomicInteger(0);
+        sshd.getSessionFactory().addListener(new SessionListener() {
+	            @Override
+                public void sessionCreated(Session session) {
+	            	// ignored
+	            }
+	
+	            @Override
+                public void sessionEvent(Session session, Event event) {
+	            	if (event == Event.KexCompleted) {
+	            		serverEventCount.incrementAndGet();
+	            	}
+	            }
+	
+	            @Override
+                public void sessionClosed(Session session) {
+	            	// ignored
+	            }
+	        });
+
+        client = SshClient.setUpDefaultClient();
+        client.start();
+    	final AtomicInteger	clientEventCount=new AtomicInteger(0);
+        client.getSessionFactory().addListener(new SessionListener() {
+	            @Override
+                public void sessionCreated(Session session) {
+	            	// ignored
+	            }
+	
+	            @Override
+                public void sessionEvent(Session session, Event event) {
+	            	if (event == Event.KexCompleted) {
+	            		clientEventCount.incrementAndGet();
+	            	}
+	            }
+	
+	            @Override
+                public void sessionClosed(Session session) {
+	            	// ignored
+	            }
+	        });
+
+        try(ClientSession s = client.connect("test", "localhost", port).await().getSession()) {
+            s.addPasswordIdentity("test");
+            s.auth().verify(5L, TimeUnit.SECONDS);
+            assertEquals("Mismatched client events count", 1, clientEventCount.get());
+            assertEquals("Mismatched server events count", 1, serverEventCount.get());
+            s.close(false);
+        } finally {
+            client.stop();
+        }
+    }
+
+    @Test   // see https://issues.apache.org/jira/browse/SSHD-456
+    public void testServerStillListensIfSessionListenerThrowsException() throws InterruptedException {
+        final Map<String,SocketAddress> eventsMap = new TreeMap<String, SocketAddress>(String.CASE_INSENSITIVE_ORDER);
+        sshd.getSessionFactory().addListener(new SessionListener() {
+            private final Logger log=LoggerFactory.getLogger(getClass());
+            @Override
+            public void sessionCreated(Session session) {
+                throwException("SessionCreated", session);
+            }
+
+            @Override
+            public void sessionEvent(Session session, Event event) {
+                throwException("SessionEvent", session);
+            }
+
+            @Override
+            public void sessionClosed(Session session) {
+                throwException("SessionClosed", session);
+            }
+            
+            private void throwException(String phase, Session session) {
+                IoSession       ioSession = session.getIoSession();
+                SocketAddress   addr = ioSession.getRemoteAddress();
+                synchronized (eventsMap) {
+                    if (eventsMap.put(phase, addr) != null) {
+                        return; // already generated an event for this phase
+                    }
+                }
+                
+                RuntimeException e = new RuntimeException("Synthetic exception at phase=" + phase + ": " + addr);
+                log.info(e.getMessage());
+                throw e;
+            }
+        });
+        
+        client = SshClient.setUpDefaultClient();
+        client.start();
+        
+        int curCount=0;
+        for (int retryCount=0; retryCount < Byte.SIZE; retryCount++){
+            synchronized(eventsMap) {
+                if ((curCount=eventsMap.size()) >= 3) {
+                    return;
+                }
+            }
+            
+            try {
+                try(ClientSession s = client.connect("test", "localhost", port).await().getSession()) {
+                    s.addPasswordIdentity("test");
+                    s.auth().verify(5L, TimeUnit.SECONDS);
+                }
+                
+                synchronized(eventsMap) {
+                    assertTrue("Unexpected premature success: " + eventsMap, eventsMap.size() >= 3);
+                }
+            } catch(IOException e) {
+                // expected - ignored
+                synchronized(eventsMap) {
+                    int nextCount=eventsMap.size();
+                    assertTrue("No session event generated", nextCount > curCount);
+                }
+            }
+        }
+        
+        fail("No success to authenticate");
+    }
+
+    public static class TestEchoShellFactory extends EchoShellFactory {
+        @Override
+        public Command create() {
+            return new TestEchoShell();
+        }
+        public static class TestEchoShell extends EchoShell {
+
+            public static CountDownLatch latch = new CountDownLatch(1);
+
+            @Override
+            public void destroy() {
+                if (latch != null) {
+                    latch.countDown();
+                }
+                super.destroy();
+            }
+        }
+    }
+
+    public static class StreamCommand implements Command, Runnable {
+
+        public static class Factory implements CommandFactory {
+            @Override
+            public Command createCommand(String name) {
+                return new StreamCommand(name);
+            }
+        }
+
+        public static CountDownLatch latch;
+
+        private final String name;
+        private OutputStream out;
+
+        public StreamCommand(String name) {
+            this.name = name;
+        }
+
+        @Override
+        public void setInputStream(InputStream in) {
+            // ignored
+        }
+
+        @Override
+        public void setOutputStream(OutputStream out) {
+            this.out = out;
+        }
+
+        @Override
+        public void setErrorStream(OutputStream err) {
+            // ignored
+        }
+
+        @Override
+        public void setExitCallback(ExitCallback callback) {
+            // ignored
+        }
+
+        @Override
+        public void start(Environment env) throws IOException {
+            new Thread(this).start();
+        }
+
+        @Override
+        public void destroy() {
+            synchronized (name) {
+                if ("block".equals(name)) {
+                    try {
+                        name.wait();
+                    } catch (InterruptedException e) {
+                        e.printStackTrace();
+                    }
+                }
+            }
+        }
+
+        @Override
+        public void run() {
+            try {
+                Thread.sleep(5000);
+                while (true) {
+                    for (int i = 0; i < 100; i++) {
+                        out.write("0123456789\n".getBytes());
+                    }
+                    out.flush();
+                }
+            } catch (WindowClosedException e) {
+                // ok, do nothing
+            } catch (Throwable e) {
+                e.printStackTrace();
+            } finally {
+                if (latch != null) {
+                    latch.countDown();
+                }
+            }
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        SshServer sshd = SshServer.setUpDefaultServer();
+        FactoryManagerUtils.updateProperty(sshd, FactoryManager.IDLE_TIMEOUT, TimeUnit.SECONDS.toMillis(10L));
+        sshd.setPort(8001);
+        sshd.setKeyPairProvider(Utils.createTestHostKeyProvider());
+        sshd.setSubsystemFactories(Arrays.<NamedFactory<Command>>asList(new SftpSubsystemFactory()));
+        sshd.setShellFactory(new EchoShellFactory());
+        sshd.setCommandFactory(new ScpCommandFactory());
+        sshd.setPasswordAuthenticator(BogusPasswordAuthenticator.INSTANCE);
+        sshd.start();
+        Thread.sleep(100000);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/test/java/org/apache/sshd/server/SshServerMain.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/server/SshServerMain.java b/sshd-core/src/test/java/org/apache/sshd/server/SshServerMain.java
new file mode 100644
index 0000000..5422c51
--- /dev/null
+++ b/sshd-core/src/test/java/org/apache/sshd/server/SshServerMain.java
@@ -0,0 +1,35 @@
+/*
+ * 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.server;
+
+import org.apache.sshd.server.SshServer;
+
+/**
+ * Just a test class used to invoke {@link SshServer#main(String[])} in
+ * order to have logging - which is in {@code test} scope
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class SshServerMain {
+
+    public static void main(String[] args) throws Exception {
+        SshServer.main(args);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/test/java/org/apache/sshd/server/SshServerTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/server/SshServerTest.java b/sshd-core/src/test/java/org/apache/sshd/server/SshServerTest.java
new file mode 100644
index 0000000..5c8e08e
--- /dev/null
+++ b/sshd-core/src/test/java/org/apache/sshd/server/SshServerTest.java
@@ -0,0 +1,99 @@
+/*
+ * 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.server;
+
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+
+import org.apache.sshd.server.SshServer;
+import org.apache.sshd.util.BaseTestSupport;
+import org.apache.sshd.util.BogusPasswordAuthenticator;
+import org.apache.sshd.util.EchoShellFactory;
+import org.apache.sshd.util.Utils;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author Kohsuke Kawaguchi
+ * @author Michael Heemskerk
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class SshServerTest extends BaseTestSupport {
+
+    @Test
+    public void stopMethodShouldBeIdempotent() throws Exception {
+        try(SshServer sshd = new SshServer()) {
+            sshd.stop();
+            sshd.stop();
+            sshd.stop();
+        }
+    }
+
+    @Test
+    public void testExecutorShutdownFalse() throws Exception {
+        ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
+
+        try(SshServer sshd = createTestServer()) {
+            sshd.setScheduledExecutorService(executorService);
+    
+            sshd.start();
+            sshd.stop();
+    
+            assertFalse(executorService.isShutdown());
+            executorService.shutdownNow();
+        }
+    }
+
+    @Test
+    public void testExecutorShutdownTrue() throws Exception {
+        ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
+
+        try(SshServer sshd = createTestServer()) {
+            sshd.setScheduledExecutorService(executorService, true);
+    
+            sshd.start();
+            sshd.stop();
+    
+            assertTrue(executorService.isShutdown());
+        }
+    }
+
+    @Test
+    public void testDynamicPort() throws Exception {
+        try(SshServer sshd = createTestServer()) {
+            sshd.setHost("localhost");
+            sshd.start();
+    
+            assertNotEquals(0, sshd.getPort());
+    
+            sshd.stop();
+        }
+    }
+
+
+    private SshServer createTestServer() {
+        SshServer sshd = SshServer.setUpDefaultServer();
+        sshd.setKeyPairProvider(Utils.createTestHostKeyProvider());
+        sshd.setShellFactory(new EchoShellFactory());
+        sshd.setPasswordAuthenticator(BogusPasswordAuthenticator.INSTANCE);
+
+        return sshd;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/test/java/org/apache/sshd/server/config/keys/AuthorizedKeyEntryTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/server/config/keys/AuthorizedKeyEntryTest.java b/sshd-core/src/test/java/org/apache/sshd/server/config/keys/AuthorizedKeyEntryTest.java
index 328a257..0740d4c 100644
--- a/sshd-core/src/test/java/org/apache/sshd/server/config/keys/AuthorizedKeyEntryTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/server/config/keys/AuthorizedKeyEntryTest.java
@@ -33,8 +33,8 @@ import java.util.Collection;
 import org.apache.sshd.common.config.keys.KeyUtils;
 import org.apache.sshd.common.config.keys.PublicKeyEntry;
 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.server.PublickeyAuthenticator;
 import org.apache.sshd.util.BaseTestSupport;
 import org.junit.FixMethodOrder;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/test/java/org/apache/sshd/server/config/keys/DefaultAuthorizedKeysAuthenticatorTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/server/config/keys/DefaultAuthorizedKeysAuthenticatorTest.java b/sshd-core/src/test/java/org/apache/sshd/server/config/keys/DefaultAuthorizedKeysAuthenticatorTest.java
index a1101f7..9fb9de3 100644
--- a/sshd-core/src/test/java/org/apache/sshd/server/config/keys/DefaultAuthorizedKeysAuthenticatorTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/server/config/keys/DefaultAuthorizedKeysAuthenticatorTest.java
@@ -28,7 +28,7 @@ import java.nio.file.Path;
 import java.security.PublicKey;
 import java.util.Collection;
 
-import org.apache.sshd.common.util.IoUtils;
+import org.apache.sshd.common.util.io.IoUtils;
 import org.apache.sshd.server.PublickeyAuthenticator;
 import org.apache.sshd.util.BaseTestSupport;
 import org.junit.FixMethodOrder;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/test/java/org/apache/sshd/util/BogusInvertedShell.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/util/BogusInvertedShell.java b/sshd-core/src/test/java/org/apache/sshd/util/BogusInvertedShell.java
index 4d38f6a..28e1abf 100644
--- a/sshd-core/src/test/java/org/apache/sshd/util/BogusInvertedShell.java
+++ b/sshd-core/src/test/java/org/apache/sshd/util/BogusInvertedShell.java
@@ -24,7 +24,7 @@ import java.io.OutputStream;
 import java.util.Collections;
 import java.util.Map;
 
-import org.apache.sshd.common.util.IoUtils;
+import org.apache.sshd.common.util.io.IoUtils;
 import org.apache.sshd.server.shell.InvertedShell;
 
 public class BogusInvertedShell implements InvertedShell {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/test/java/org/apache/sshd/util/BogusPasswordAuthenticator.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/util/BogusPasswordAuthenticator.java b/sshd-core/src/test/java/org/apache/sshd/util/BogusPasswordAuthenticator.java
index 40a3162..cff5a72 100644
--- a/sshd-core/src/test/java/org/apache/sshd/util/BogusPasswordAuthenticator.java
+++ b/sshd-core/src/test/java/org/apache/sshd/util/BogusPasswordAuthenticator.java
@@ -18,7 +18,7 @@
  */
 package org.apache.sshd.util;
 
-import org.apache.sshd.common.util.AbstractLoggingBean;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
 import org.apache.sshd.server.PasswordAuthenticator;
 import org.apache.sshd.server.session.ServerSession;
 


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

Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/common/util/io/IoUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/io/IoUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/io/IoUtils.java
new file mode 100644
index 0000000..9bae57e
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/io/IoUtils.java
@@ -0,0 +1,293 @@
+/*
+ * 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.util.io;
+
+import java.io.Closeable;
+import java.io.EOFException;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.file.FileSystem;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.Path;
+import java.nio.file.attribute.PosixFilePermission;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.sshd.common.util.OsUtils;
+
+/**
+ * TODO Add javadoc
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class IoUtils {
+
+    public static final LinkOption[] EMPTY_OPTIONS = new LinkOption[0];
+    private static final LinkOption[] NO_FOLLOW_OPTIONS = new LinkOption[] { LinkOption.NOFOLLOW_LINKS };
+
+    public static LinkOption[] getLinkOptions(boolean followLinks) {
+        if (followLinks) {
+            return EMPTY_OPTIONS;
+        } else {    // return a clone that modifications to the array will not affect others
+            return NO_FOLLOW_OPTIONS.clone();
+        }
+    }
+
+    public static final int DEFAULT_COPY_SIZE=8192;
+
+    public static long copy(InputStream source, OutputStream sink) throws IOException {
+        return copy(source, sink, DEFAULT_COPY_SIZE);
+    }
+
+    public static long copy(InputStream source, OutputStream sink, int bufferSize) throws IOException {
+        long nread = 0L;
+        byte[] buf = new byte[bufferSize];
+        int n;
+        while ((n = source.read(buf)) > 0) {
+            sink.write(buf, 0, n);
+            nread += n;
+        }
+        return nread;
+    }
+
+    public static void closeQuietly(Closeable... closeables) {
+        for (Closeable c : closeables) {
+            try {
+                if (c != null) {
+                    c.close();
+                }
+            } catch (IOException e) {
+                // Ignore
+            }
+        }
+    }
+
+    public static final List<String> WINDOWS_EXECUTABLE_EXTENSIONS = Collections.unmodifiableList(Arrays.asList(".bat", ".exe", ".cmd"));
+
+    /**
+     * @param fileName The file name to be evaluated - ignored if {@code null}/empty
+     * @return {@code true} if the file ends in one of the {@link #WINDOWS_EXECUTABLE_EXTENSIONS}
+     */
+    public static boolean isWindowsExecutable(String fileName) {
+        if ((fileName == null) || (fileName.length() <= 0)) {
+            return false;
+        }
+        for (String suffix : WINDOWS_EXECUTABLE_EXTENSIONS) {
+            if (fileName.endsWith(suffix)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * If the &quot;posix&quot; view is supported, then it returns
+     * {@link Files#getPosixFilePermissions(Path, LinkOption...)}, otherwise
+     * uses the {@link #getPermissionsFromFile(File)} method
+     * @param path The {@link Path}
+     * @return A {@link Set} of {@link PosixFilePermission}
+     * @throws IOException If failed to access the file system in order to
+     * retrieve the permissions
+     */
+    public static Set<PosixFilePermission> getPermissions(Path path) throws IOException {
+        FileSystem          fs = path.getFileSystem();
+        Collection<String>  views = fs.supportedFileAttributeViews();
+        if (views.contains("posix")) {
+            return Files.getPosixFilePermissions(path, getLinkOptions(false));
+        } else {
+            return getPermissionsFromFile(path.toFile());
+        }
+    }
+
+    /**
+     * @param f The {@link File} to be checked
+     * @return A {@link Set} of {@link PosixFilePermission}s based on whether
+     * the file is readable/writable/executable. If so, then <U>all</U> the
+     * relevant permissions are set (i.e., owner, group and others)
+     */
+    public static Set<PosixFilePermission> getPermissionsFromFile(File f) {
+        Set<PosixFilePermission> perms = EnumSet.noneOf(PosixFilePermission.class);
+        if (f.canRead()) {
+            perms.add(PosixFilePermission.OWNER_READ);
+            perms.add(PosixFilePermission.GROUP_READ);
+            perms.add(PosixFilePermission.OTHERS_READ);
+        }
+
+        if (f.canWrite()) {
+            perms.add(PosixFilePermission.OWNER_WRITE);
+            perms.add(PosixFilePermission.GROUP_WRITE);
+            perms.add(PosixFilePermission.OTHERS_WRITE);
+        }
+
+        if (f.canExecute() || (OsUtils.isWin32() && isWindowsExecutable(f.getName()))) {
+            perms.add(PosixFilePermission.OWNER_EXECUTE);
+            perms.add(PosixFilePermission.GROUP_EXECUTE);
+            perms.add(PosixFilePermission.OTHERS_EXECUTE);
+        }
+
+        return perms;
+    }
+
+    /**
+     * If the &quot;posix&quot; view is supported, then it invokes
+     * {@link Files#setPosixFilePermissions(Path, Set)}, otherwise
+     * uses the {@link #setPermissionsToFile(File, Collection)} method
+     * @param path The {@link Path}
+     * @param perms The {@link Set} of {@link PosixFilePermission}s
+     * @throws IOException If failed to access the file system
+     */
+    public static void setPermissions(Path path, Set<PosixFilePermission> perms) throws IOException {
+        FileSystem          fs = path.getFileSystem();
+        Collection<String>  views = fs.supportedFileAttributeViews();
+        if (views.contains("posix")) {
+            Files.setPosixFilePermissions(path, perms);
+        } else {
+            setPermissionsToFile(path.toFile(), perms);
+        }
+    }
+
+    /**
+     * @param f The {@link File}
+     * @param perms A {@link Collection} of {@link PosixFilePermission}s to set on it.
+     * <B>Note:</B> the file is set to readable/writable/executable not only by the
+     * owner if <U>any</U> of relevant the owner/group/others permission is set
+     */
+    public static void setPermissionsToFile(File f, Collection<PosixFilePermission> perms) {
+        boolean readable = perms != null &&
+                  (perms.contains(PosixFilePermission.OWNER_READ)
+                || perms.contains(PosixFilePermission.GROUP_READ)
+                || perms.contains(PosixFilePermission.OTHERS_READ));
+        f.setReadable(readable, false);
+
+        boolean writable = perms != null &&
+                  (perms.contains(PosixFilePermission.OWNER_WRITE)
+                || perms.contains(PosixFilePermission.GROUP_WRITE)
+                || perms.contains(PosixFilePermission.OTHERS_WRITE));
+        f.setWritable(writable, false);
+
+        boolean executable = perms != null &&
+                  (perms.contains(PosixFilePermission.OWNER_EXECUTE)
+                || perms.contains(PosixFilePermission.GROUP_EXECUTE)
+                || perms.contains(PosixFilePermission.OTHERS_EXECUTE));
+        f.setExecutable(executable, false);
+    }
+
+    /**
+     * <P>Checks if a file exists - <B>Note:</B> according to the
+     * <A HREF="http://docs.oracle.com/javase/tutorial/essential/io/check.html">Java tutorial - Checking a File or Directory</A>:
+     * </P></BR>
+     * <PRE>
+     *      The methods in the Path class are syntactic, meaning that they operate
+     *      on the Path instance. But eventually you must access the file system
+     *      to verify that a particular Path exists, or does not exist. You can do
+     *      so with the exists(Path, LinkOption...) and the notExists(Path, LinkOption...)
+     *      methods. Note that !Files.exists(path) is not equivalent to Files.notExists(path).
+     *      When you are testing a file's existence, three results are possible:
+     *
+     *      - The file is verified to exist.
+     *      - The file is verified to not exist.
+     *      - The file's status is unknown.
+     *      
+     *      This result can occur when the program does not have access to the file.
+     *      If both exists and notExists return false, the existence of the file cannot
+     *      be verified.
+     * </PRE>
+     * @param path The {@link Path} to be tested
+     * @param options The {@link LinkOption}s to use
+     * @return {@link Boolean#TRUE}/{@link Boolean#FALSE} or {@code null}
+     * according to the file status as explained above
+     */
+    public static Boolean checkFileExists(Path path, LinkOption ... options) {
+        if (Files.exists(path, options)) {
+            return Boolean.TRUE;
+        } else if (Files.notExists(path, options)) {
+            return Boolean.FALSE;
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Read the requested number of bytes or fail if there are not enough left.
+     * @param input where to read input from
+     * @param buffer destination
+     * @throws IOException if there is a problem reading the file
+     * @throws EOFException if the number of bytes read was incorrect
+     */
+    public static void readFully(InputStream input, byte[] buffer) throws IOException {
+        readFully(input, buffer, 0, buffer.length);
+    }
+
+    /**
+     * Read the requested number of bytes or fail if there are not enough left.
+     * @param input where to read input from
+     * @param buffer destination
+     * @param offset initial offset into buffer
+     * @param length length to read, must be >= 0
+     * @throws IOException if there is a problem reading the file
+     * @throws EOFException if the number of bytes read was incorrect
+     */
+    public static void readFully(InputStream input, byte[] buffer, int offset, int length) throws IOException {
+        int actual = read(input, buffer, offset, length);
+        if (actual != length) {
+            throw new EOFException("Premature EOF - expected=" + length + ", actual=" + actual);
+        }
+    }
+
+    /**
+     * Read as many bytes as possible until EOF or achieved required length
+     * @param input where to read input from
+     * @param buffer destination
+     * @return actual length read; may be less than requested if EOF was reached
+     * @throws IOException if a read error occurs
+     */
+    public static int read(InputStream input, byte[] buffer) throws IOException {
+        return read(input, buffer, 0, buffer.length);
+    }
+
+    /**
+     * Read as many bytes as possible until EOF or achieved required length
+     * @param input where to read input from
+     * @param buffer destination
+     * @param offset initial offset into buffer
+     * @param length length to read - ignored if non-positive
+     * @return actual length read; may be less than requested if EOF was reached
+     * @throws IOException if a read error occurs
+     */
+    public static int read(InputStream input, byte[] buffer, int offset, int length) throws IOException {
+        for (int remaining = length, curOffset = offset; remaining > 0; ) {
+            int count = input.read(buffer, curOffset, remaining);
+            if (count == (-1)) { // EOF before achieved required length
+                return curOffset - offset;
+            }
+
+            remaining -= count;
+            curOffset += count;
+        }
+
+        return length;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/common/util/io/ModifiableFileWatcher.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/io/ModifiableFileWatcher.java b/sshd-core/src/main/java/org/apache/sshd/common/util/io/ModifiableFileWatcher.java
index 83c1656..ddf0efb 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/io/ModifiableFileWatcher.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/io/ModifiableFileWatcher.java
@@ -29,10 +29,9 @@ import java.nio.file.attribute.FileTime;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicLong;
 
-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.logging.AbstractLoggingBean;
 
 /**
  * Watches over changes for a file and re-loads them if file has changed - including

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/common/util/logging/AbstractLoggingBean.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/logging/AbstractLoggingBean.java b/sshd-core/src/main/java/org/apache/sshd/common/util/logging/AbstractLoggingBean.java
new file mode 100644
index 0000000..c041750
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/logging/AbstractLoggingBean.java
@@ -0,0 +1,49 @@
+/*
+ * 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.util.logging;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Serves as a common base class for the vast majority of classes that require
+ * some kind of logging. Facilitates quick and easy replacement of the actual used
+ * logger from one framework to another
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractLoggingBean {
+    protected final Logger log;
+    
+    /**
+     * Default constructor - creates a logger using the full class name
+     */
+    protected AbstractLoggingBean() {
+        log = LoggerFactory.getLogger(getClass());
+    }
+    
+    /**
+     * Create a logger for instances of the same class for which we might
+     * want to have a &quot;discriminator&quot; for them
+     * @param discriminator The discriminator value
+     */
+    protected AbstractLoggingBean(String discriminator) {
+        log = LoggerFactory.getLogger(getClass().getName() + "[" + discriminator + "]");
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/common/util/logging/AbstractSimplifiedLog.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/logging/AbstractSimplifiedLog.java b/sshd-core/src/main/java/org/apache/sshd/common/util/logging/AbstractSimplifiedLog.java
new file mode 100644
index 0000000..8492cfe
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/logging/AbstractSimplifiedLog.java
@@ -0,0 +1,36 @@
+/*
+ * 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.util.logging;
+
+import java.util.logging.Level;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractSimplifiedLog implements SimplifiedLog {
+    protected AbstractSimplifiedLog() {
+        super();
+    }
+
+    @Override
+    public void log(Level level, Object message) {
+        log(level, message, null);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/common/util/logging/LoggingUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/logging/LoggingUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/logging/LoggingUtils.java
new file mode 100644
index 0000000..9467ee5
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/logging/LoggingUtils.java
@@ -0,0 +1,144 @@
+/*
+ * 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.util.logging;
+
+import java.util.Objects;
+import java.util.logging.Level;
+
+import org.slf4j.Logger;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class LoggingUtils {
+    private LoggingUtils() {
+        throw new UnsupportedOperationException("No instance");
+    }
+
+    /**
+     * Verifies if the given level is above the required threshold for logging.
+     * @param level The {@link Level} to evaluate
+     * @param threshold The threshold {@link Level}
+     * @return {@code true} if the evaluated level is above the required
+     * threshold.</BR>
+     * <P><B>Note(s):</B></BR></P>
+     * <P><UL>
+     *      <LI>
+     *      If either argument is {@code null} then result is {@code false}.
+     *      </LI>
+     *
+     *      <LI>
+     *      If the evaluated level is {@link Level#OFF} then result is {@code false}
+     *      regardless of the threshold.
+     *      </LI>
+     *
+     *      <LI>
+     *      If the threshold is {@link Level#ALL} and the evaluated level is
+     *      <U>not</U> {@link Level#OFF} the result is {@code true}.
+     *      </LI>
+     *
+     *      <LI>
+     *      Otherwise, the evaluated level {@link Level#intValue()} must be
+     *      greater or equal to the threshold.
+     *      </LI>
+     * </UL></P>
+     */
+    public static final boolean isLoggable(Level level, Level threshold) {
+        if ((level == null) || (threshold == null)) {
+            return false;
+        } else if (Level.OFF.equals(level) || Level.OFF.equals(threshold)) {
+            return false;
+        } else if (Level.ALL.equals(threshold)) {
+            return true;
+        } else if (level.intValue() < threshold.intValue()) {
+            return false;
+        } else {
+            return true;
+        }
+    }
+
+    public static final SimplifiedLog wrap(final Logger logger) {
+        if (logger == null) {
+            return SimplifiedLog.EMPTY;
+        } else {
+            return new AbstractSimplifiedLog() {
+                @Override
+                public void log(Level level, Object message, Throwable t) {
+                    if (isEnabled(level)) {
+                        logMessage(logger, level, message, t);
+                    }
+                    
+                }
+                
+                @Override
+                public boolean isEnabled(Level level) {
+                    return isLoggable(logger, level);
+                }
+            };
+        }
+    }
+
+    // NOTE: assume that level enabled has been checked !!!
+    public static final void logMessage(Logger logger, Level level, Object message, Throwable t) {
+        if ((logger == null) || (level == null) || Level.OFF.equals(level)) {
+            return;
+        } else if (Level.SEVERE.equals(level)) {
+            logger.error(Objects.toString(message), t);
+        } else if (Level.WARNING.equals(level)) {
+            logger.warn(Objects.toString(message), t);
+        } else if (Level.INFO.equals(level) || Level.ALL.equals(level)) {
+            logger.info(Objects.toString(message), t);
+        } else if (Level.CONFIG.equals(level) || Level.FINE.equals(level)) {
+            logger.debug(Objects.toString(message), t);
+        } else {
+            logger.trace(Objects.toString(message), t);
+        }
+    }
+
+    /**
+     * @param logger The {@link Logger} instance - ignored if {@code null}
+     * @param level The validate log {@link Level} - ignored if {@code null}
+     * @return <P>{@code true} if the level is enabled for the logger. The
+     * mapping of the level to the logger is as follows:</P></BR>
+     * <UL>
+     *      <LI>{@link Level#OFF} always returns {@code false}</LI>
+     *      <LI>{@link Level#SEVERE} returns {@link Logger#isErrorEnabled()}</LI>
+     *      <LI>{@link Level#WARNING} returns {@link Logger#isWarnEnabled()}</LI>
+     *      <LI>{@link Level#INFO} and {@link Level#ALL} returns {@link Logger#isInfoEnabled()}</LI>
+     *      <LI>{@link Level#CONFIG} and {@link Level#FINE} returns {@link Logger#isDebugEnabled()}</LI>
+     *      <LI>All other levels return {@link Logger#isTraceEnabled()}</LI>
+     * </UL>
+     */
+    public static final boolean isLoggable(Logger logger, Level level) {
+        if ((logger == null) || (level == null) || Level.OFF.equals(level)) {
+            return false;
+        } else if (Level.SEVERE.equals(level)) {
+            return logger.isErrorEnabled();
+        } else if (Level.WARNING.equals(level)) {
+            return logger.isWarnEnabled();
+        } else if (Level.INFO.equals(level) || Level.ALL.equals(level)) {
+            return logger.isInfoEnabled();
+        } else if (Level.CONFIG.equals(level) || Level.FINE.equals(level)) {
+            return logger.isDebugEnabled();
+        } else {
+            return logger.isTraceEnabled();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/common/util/logging/SimplifiedLog.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/logging/SimplifiedLog.java b/sshd-core/src/main/java/org/apache/sshd/common/util/logging/SimplifiedLog.java
new file mode 100644
index 0000000..0599d37
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/logging/SimplifiedLog.java
@@ -0,0 +1,53 @@
+/*
+ * 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.util.logging;
+
+import java.util.logging.Level;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface SimplifiedLog {
+    boolean isEnabled(Level level);
+
+    void log(Level level, Object message);
+
+    void log(Level level, Object message, Throwable t);
+
+    /**
+     * An &quot;empty&quot; {@link SimplifiedLog} that does nothing
+     */
+    SimplifiedLog   EMPTY=new AbstractSimplifiedLog() {
+            @Override
+            public boolean isEnabled(Level level) {
+                return false;
+            }
+
+            @Override
+            public void log(Level level, Object message, Throwable t) {
+                return;
+            }
+
+            @Override
+            public String toString() {
+                return "EMPTY";
+            }
+        };
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/server/PasswordAuthenticator.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/PasswordAuthenticator.java b/sshd-core/src/main/java/org/apache/sshd/server/PasswordAuthenticator.java
index 9f44851..9164e07 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/PasswordAuthenticator.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/PasswordAuthenticator.java
@@ -18,7 +18,7 @@
  */
 package org.apache.sshd.server;
 
-import org.apache.sshd.common.util.AbstractLoggingBean;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
 import org.apache.sshd.server.session.ServerSession;
 
 /**

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/server/PublickeyAuthenticator.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/PublickeyAuthenticator.java b/sshd-core/src/main/java/org/apache/sshd/server/PublickeyAuthenticator.java
index d6224fb..b924bb2 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/PublickeyAuthenticator.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/PublickeyAuthenticator.java
@@ -23,8 +23,8 @@ import java.util.Collection;
 import java.util.Collections;
 
 import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.util.AbstractLoggingBean;
 import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
 import org.apache.sshd.server.session.ServerSession;
 
 /**

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/server/ServerBuilder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/ServerBuilder.java b/sshd-core/src/main/java/org/apache/sshd/server/ServerBuilder.java
index 93a9b37..d8ea7b0 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/ServerBuilder.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/ServerBuilder.java
@@ -24,10 +24,10 @@ import java.util.List;
 
 import org.apache.sshd.common.BaseBuilder;
 import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.Transformer;
 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.channel.ChannelSession;
 import org.apache.sshd.server.config.keys.DefaultAuthorizedKeysAuthenticator;
 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/server/auth/AbstractUserAuth.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/auth/AbstractUserAuth.java b/sshd-core/src/main/java/org/apache/sshd/server/auth/AbstractUserAuth.java
index 9293ff2..d334edb 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/auth/AbstractUserAuth.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/auth/AbstractUserAuth.java
@@ -18,8 +18,8 @@
  */
 package org.apache.sshd.server.auth;
 
-import org.apache.sshd.common.util.AbstractLoggingBean;
 import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
 import org.apache.sshd.server.UserAuth;
 import org.apache.sshd.server.session.ServerSession;
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java b/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java
index 74329db..44069cb 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java
@@ -50,9 +50,9 @@ import org.apache.sshd.common.future.DefaultCloseFuture;
 import org.apache.sshd.common.future.SshFutureListener;
 import org.apache.sshd.common.util.CloseableUtils;
 import org.apache.sshd.common.util.GenericUtils;
-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;
 import org.apache.sshd.common.util.io.LoggingFilterOutputStream;
 import org.apache.sshd.server.AsyncCommand;
 import org.apache.sshd.server.ChannelSessionAware;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/server/channel/PipeDataReceiver.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/channel/PipeDataReceiver.java b/sshd-core/src/main/java/org/apache/sshd/server/channel/PipeDataReceiver.java
index 677ae72..05a4e7e 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/channel/PipeDataReceiver.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/channel/PipeDataReceiver.java
@@ -25,8 +25,8 @@ import java.io.OutputStream;
 import org.apache.sshd.common.channel.ChannelPipedInputStream;
 import org.apache.sshd.common.channel.ChannelPipedOutputStream;
 import org.apache.sshd.common.channel.Window;
-import org.apache.sshd.common.util.AbstractLoggingBean;
 import org.apache.sshd.common.util.io.LoggingFilterOutputStream;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
 
 /**
  * {@link ChannelDataReceiver} that buffers the received data into byte buffer

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/server/channel/PuttyRequestHandler.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/channel/PuttyRequestHandler.java b/sshd-core/src/main/java/org/apache/sshd/server/channel/PuttyRequestHandler.java
index c8b4ae3..ea2d2d6 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/channel/PuttyRequestHandler.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/channel/PuttyRequestHandler.java
@@ -20,8 +20,8 @@ package org.apache.sshd.server.channel;
 
 import org.apache.sshd.common.channel.Channel;
 import org.apache.sshd.common.channel.RequestHandler;
-import org.apache.sshd.common.util.AbstractLoggingBean;
 import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
 
 /**
  * Handles Putty specific channel requests as indicated by

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/server/command/ScpCommand.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/command/ScpCommand.java b/sshd-core/src/main/java/org/apache/sshd/server/command/ScpCommand.java
index baf6ca2..acf9c7f 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/command/ScpCommand.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/command/ScpCommand.java
@@ -30,7 +30,7 @@ import java.util.concurrent.Future;
 import org.apache.sshd.common.file.FileSystemAware;
 import org.apache.sshd.common.scp.ScpHelper;
 import org.apache.sshd.common.scp.ScpTransferEventListener;
-import org.apache.sshd.common.util.AbstractLoggingBean;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
 import org.apache.sshd.common.util.threads.ThreadUtils;
 import org.apache.sshd.server.Command;
 import org.apache.sshd.server.Environment;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/server/config/keys/AuthorizedKeyEntry.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/config/keys/AuthorizedKeyEntry.java b/sshd-core/src/main/java/org/apache/sshd/server/config/keys/AuthorizedKeyEntry.java
index e817405..b5e37e0 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/config/keys/AuthorizedKeyEntry.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/config/keys/AuthorizedKeyEntry.java
@@ -317,7 +317,7 @@ public class AuthorizedKeyEntry extends PublicKeyEntry {
         }
 
         String keyType = line.substring(0, startPos);
-        PublicKeyEntryDecoder<? extends PublicKey> decoder = KeyUtils.getPublicKeyEntryDecoder(keyType);
+        PublicKeyEntryDecoder<?,?> decoder = KeyUtils.getPublicKeyEntryDecoder(keyType);
         final AuthorizedKeyEntry    entry;
         if (decoder == null) {  // assume this is due to the fact that it starts with login options
             if ((entry=parseAuthorizedKeyEntry(line.substring(startPos + 1).trim())) == null) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/server/config/keys/AuthorizedKeysAuthenticator.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/config/keys/AuthorizedKeysAuthenticator.java b/sshd-core/src/main/java/org/apache/sshd/server/config/keys/AuthorizedKeysAuthenticator.java
index 177686e..69d7107 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/config/keys/AuthorizedKeysAuthenticator.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/config/keys/AuthorizedKeysAuthenticator.java
@@ -29,8 +29,8 @@ import java.util.Collection;
 import java.util.concurrent.atomic.AtomicReference;
 
 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.io.ModifiableFileWatcher;
 import org.apache.sshd.server.PublickeyAuthenticator;
 import org.apache.sshd.server.session.ServerSession;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/server/config/keys/DefaultAuthorizedKeysAuthenticator.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/config/keys/DefaultAuthorizedKeysAuthenticator.java b/sshd-core/src/main/java/org/apache/sshd/server/config/keys/DefaultAuthorizedKeysAuthenticator.java
index 6bad436..ddecc16 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/config/keys/DefaultAuthorizedKeysAuthenticator.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/config/keys/DefaultAuthorizedKeysAuthenticator.java
@@ -31,9 +31,9 @@ import java.util.EnumSet;
 import java.util.Set;
 
 import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.IoUtils;
 import org.apache.sshd.common.util.OsUtils;
 import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.io.IoUtils;
 import org.apache.sshd.server.session.ServerSession;
 
 /**

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/server/forward/ForwardingFilter.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/forward/ForwardingFilter.java b/sshd-core/src/main/java/org/apache/sshd/server/forward/ForwardingFilter.java
index 4d20ee1..0e0fcc3 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/forward/ForwardingFilter.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/forward/ForwardingFilter.java
@@ -26,8 +26,8 @@ import org.apache.sshd.agent.SshAgent;
 import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.SshdSocketAddress;
 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.logging.AbstractLoggingBean;
 
 /**
  * Determines if a forwarding request will be permitted.

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/server/global/CancelTcpipForwardHandler.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/global/CancelTcpipForwardHandler.java b/sshd-core/src/main/java/org/apache/sshd/server/global/CancelTcpipForwardHandler.java
index 383991e..2edd32c 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/global/CancelTcpipForwardHandler.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/global/CancelTcpipForwardHandler.java
@@ -23,8 +23,8 @@ import org.apache.sshd.common.SshdSocketAddress;
 import org.apache.sshd.common.channel.RequestHandler;
 import org.apache.sshd.common.session.ConnectionService;
 import org.apache.sshd.common.session.Session;
-import org.apache.sshd.common.util.AbstractLoggingBean;
 import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
 
 /**
  * Handler for cancel-tcpip-forward global request.

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/server/global/TcpipForwardHandler.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/global/TcpipForwardHandler.java b/sshd-core/src/main/java/org/apache/sshd/server/global/TcpipForwardHandler.java
index f51f1d3..dc6adcb 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/global/TcpipForwardHandler.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/global/TcpipForwardHandler.java
@@ -23,8 +23,8 @@ import org.apache.sshd.common.SshdSocketAddress;
 import org.apache.sshd.common.channel.RequestHandler;
 import org.apache.sshd.common.session.ConnectionService;
 import org.apache.sshd.common.session.Session;
-import org.apache.sshd.common.util.AbstractLoggingBean;
 import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
 
 /**
  * Handler for tcpip-forward global request.

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/server/jaas/JaasPasswordAuthenticator.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/jaas/JaasPasswordAuthenticator.java b/sshd-core/src/main/java/org/apache/sshd/server/jaas/JaasPasswordAuthenticator.java
index 7200083..0f4ea2a 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/jaas/JaasPasswordAuthenticator.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/jaas/JaasPasswordAuthenticator.java
@@ -28,7 +28,7 @@ import javax.security.auth.callback.PasswordCallback;
 import javax.security.auth.callback.UnsupportedCallbackException;
 import javax.security.auth.login.LoginContext;
 
-import org.apache.sshd.common.util.AbstractLoggingBean;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
 import org.apache.sshd.server.PasswordAuthenticator;
 import org.apache.sshd.server.session.ServerSession;
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java b/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java
index ecc8d29..e3f4152 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java
@@ -80,13 +80,13 @@ import java.util.concurrent.TimeUnit;
 
 import org.apache.sshd.common.FactoryManagerUtils;
 import org.apache.sshd.common.file.FileSystemAware;
-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.OsUtils;
 import org.apache.sshd.common.util.SelectorUtils;
 import org.apache.sshd.common.util.buffer.Buffer;
 import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
+import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
 import org.apache.sshd.common.util.threads.ThreadUtils;
 import org.apache.sshd.server.Command;
 import org.apache.sshd.server.Environment;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/server/shell/ProcessShellFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/shell/ProcessShellFactory.java b/sshd-core/src/main/java/org/apache/sshd/server/shell/ProcessShellFactory.java
index 0555186..9e5161a 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/shell/ProcessShellFactory.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/shell/ProcessShellFactory.java
@@ -27,9 +27,9 @@ import java.util.EnumSet;
 import java.util.Map;
 
 import org.apache.sshd.common.Factory;
-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;
 import org.apache.sshd.server.Command;
 
 /**

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/test/java/org/apache/sshd/AbstractSessionTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/AbstractSessionTest.java b/sshd-core/src/test/java/org/apache/sshd/AbstractSessionTest.java
deleted file mode 100644
index 099c002..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/AbstractSessionTest.java
+++ /dev/null
@@ -1,147 +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;
-
-import java.io.IOException;
-
-import org.apache.sshd.common.session.AbstractSession;
-import org.apache.sshd.common.util.buffer.Buffer;
-import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
-import org.apache.sshd.server.SshServer;
-import org.apache.sshd.util.BaseTestSupport;
-import org.junit.Before;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.runners.MethodSorters;
-
-/**
- * Test basic stuff on AbstractSession.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-public class AbstractSessionTest extends BaseTestSupport {
-
-    private MySession session;
-
-    @Before
-    public void setUp() throws Exception {
-        session = new MySession();
-    }
-
-    @Test
-    public void testReadIdentSimple() {
-        Buffer buf = new ByteArrayBuffer("SSH-2.0-software\r\n".getBytes());
-        String ident = session.doReadIdentification(buf);
-        assertEquals("SSH-2.0-software", ident);
-    }
-
-    @Test
-    public void testReadIdentWithoutCR() {
-        Buffer buf = new ByteArrayBuffer("SSH-2.0-software\n".getBytes());
-        String ident = session.doReadIdentification(buf);
-        assertEquals("SSH-2.0-software", ident);
-    }
-
-    @Test
-    public void testReadIdentWithHeaders() {
-        Buffer buf = new ByteArrayBuffer(("a header line\r\nSSH-2.0-software\r\n").getBytes());
-        String ident = session.doReadIdentification(buf);
-        assertEquals("SSH-2.0-software", ident);
-    }
-
-    @Test
-    public void testReadIdentWithSplitPackets() {
-        Buffer buf = new ByteArrayBuffer("header line\r\nSSH".getBytes());
-        String ident = session.doReadIdentification(buf);
-        assertNull(ident);
-        buf.putRawBytes("-2.0-software\r\n".getBytes());
-        ident = session.doReadIdentification(buf);
-        assertEquals("SSH-2.0-software", ident);
-    }
-
-    @Test(expected = IllegalStateException.class)
-    public void testReadIdentBadLineEnding() {
-        Buffer buf = new ByteArrayBuffer(("SSH-2.0-software\ra").getBytes());
-        String ident = session.doReadIdentification(buf);
-        fail("Unexpected success: " + ident);
-    }
-
-    @Test(expected = IllegalStateException.class)
-    public void testReadIdentLongLine() {
-        Buffer buf = new ByteArrayBuffer(("SSH-2.0-software" +
-                "01234567890123456789012345678901234567890123456789" +
-                "01234567890123456789012345678901234567890123456789" +
-                "01234567890123456789012345678901234567890123456789" +
-                "01234567890123456789012345678901234567890123456789" +
-                "01234567890123456789012345678901234567890123456789" +
-                "01234567890123456789012345678901234567890123456789").getBytes());
-        String ident = session.doReadIdentification(buf);
-        fail("Unexpected success: " + ident);
-    }
-
-    @Test(expected = IllegalStateException.class)
-    public void testReadIdentLongHeader() {
-        StringBuilder sb = new StringBuilder(32768);
-        for (int i = 0; i < 500; i++) {
-            sb.append("01234567890123456789012345678901234567890123456789\r\n");
-        }
-        sb.append("SSH-2.0-software\r\n");
-        Buffer buf = new ByteArrayBuffer(sb.toString().getBytes());
-        String ident = session.doReadIdentification(buf);
-        fail("Unexpected success: " + ident);
-    }
-
-    public static class MySession extends AbstractSession {
-        public MySession() {
-            super(true, SshServer.setUpDefaultServer(), null);
-        }
-        @Override
-        protected void handleMessage(Buffer buffer) throws Exception {
-            // ignored
-        }
-        @Override
-        protected boolean readIdentification(Buffer buffer) {
-            return false;
-        }
-        public String doReadIdentification(Buffer buffer) {
-            return super.doReadIdentification(buffer, false);
-        }
-        @Override
-        protected void sendKexInit() throws IOException {
-            // ignored
-        }
-        @Override
-        protected void checkKeys() {
-            // ignored
-        }
-        @Override
-        protected void receiveKexInit(Buffer buffer) throws IOException {
-            // ignored
-        }
-        @Override
-        public void startService(String name) throws Exception {
-            // ignored
-        }
-        @Override
-        public void resetIdleTimeout() {
-            // ignored
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/test/java/org/apache/sshd/AgentTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/AgentTest.java b/sshd-core/src/test/java/org/apache/sshd/AgentTest.java
deleted file mode 100644
index abdd37e..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/AgentTest.java
+++ /dev/null
@@ -1,212 +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;
-
-import static org.apache.sshd.util.Utils.createTestKeyPairProvider;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.security.KeyPair;
-import java.security.PublicKey;
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-
-import org.apache.sshd.agent.SshAgent;
-import org.apache.sshd.agent.local.LocalAgentFactory;
-import org.apache.sshd.agent.local.ProxyAgentFactory;
-import org.apache.sshd.agent.unix.AgentClient;
-import org.apache.sshd.agent.unix.AgentServer;
-import org.apache.sshd.client.SshClient;
-import org.apache.sshd.client.channel.ChannelShell;
-import org.apache.sshd.client.session.ClientSession;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.SecurityUtils;
-import org.apache.sshd.server.Command;
-import org.apache.sshd.server.Environment;
-import org.apache.sshd.server.SshServer;
-import org.apache.sshd.server.PublickeyAuthenticator.AcceptAllPublickeyAuthenticator;
-import org.apache.sshd.server.forward.ForwardingFilter;
-import org.apache.sshd.util.BaseTestSupport;
-import org.apache.sshd.util.BogusPasswordAuthenticator;
-import org.apache.sshd.util.EchoShellFactory;
-import org.apache.sshd.util.Utils;
-import org.junit.Assume;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.runners.MethodSorters;
-
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-public class AgentTest extends BaseTestSupport {
-    public AgentTest() {
-        super();
-    }
-
-    @Test
-    public void testAgentServer() throws Exception {
-        // TODO: revisit this test to work without BC
-        Assume.assumeTrue("BouncyCastle not registered", SecurityUtils.isBouncyCastleRegistered());
-
-        try(AgentServer agent = new AgentServer()) {
-            String authSocket;
-            try {
-                authSocket = agent.start();
-            } catch (UnsatisfiedLinkError e) {
-                // the native library is not available, so these tests should be skipped
-                authSocket = null;
-            }
-            Assume.assumeTrue("Native library N/A", authSocket != null);
-    
-            try(SshAgent client = new AgentClient(authSocket)) {
-                List<SshAgent.Pair<PublicKey, String>> keys = client.getIdentities();
-                assertNotNull("No initial identities", keys);
-                assertEquals("Unexpected initial identities size", 0, keys.size());
-        
-                KeyPair k = Utils.createTestHostKeyProvider().loadKey(KeyPairProvider.SSH_RSA);
-                client.addIdentity(k, "");
-                keys = client.getIdentities();
-                assertNotNull("No registered identities after add", keys);
-                assertEquals("Mismatched registered keys size", 1, keys.size());
-        
-                client.removeIdentity(k.getPublic());
-                keys = client.getIdentities();
-                assertNotNull("No registered identities after remove", keys);
-                assertEquals("Registered keys size not empty", 0, keys.size());
-        
-                client.removeAllIdentities();
-            }    
-        }
-    }
-
-    @Test
-    public void testAgentForwarding() throws Exception {
-        // TODO: revisit this test to work without BC
-        Assume.assumeTrue("BouncyCastle not registered", SecurityUtils.isBouncyCastleRegistered());
-
-        TestEchoShellFactory shellFactory = new TestEchoShellFactory();
-        ProxyAgentFactory agentFactory = new ProxyAgentFactory();
-        LocalAgentFactory localAgentFactory = new LocalAgentFactory();
-        String username = getCurrentTestName();
-        KeyPair pair = createTestKeyPairProvider("dsaprivkey.pem").loadKey(KeyPairProvider.SSH_DSS);
-        localAgentFactory.getAgent().addIdentity(pair, username);
-
-        try(SshServer sshd1 = SshServer.setUpDefaultServer()) {
-            sshd1.setKeyPairProvider(Utils.createTestHostKeyProvider());
-            sshd1.setShellFactory(shellFactory);
-            sshd1.setPasswordAuthenticator(BogusPasswordAuthenticator.INSTANCE);
-            sshd1.setPublickeyAuthenticator(AcceptAllPublickeyAuthenticator.INSTANCE);
-            sshd1.setAgentFactory(agentFactory);
-            sshd1.setTcpipForwardingFilter(ForwardingFilter.AcceptAllForwardingFilter.INSTANCE);
-            sshd1.start();
-            
-            final int port1 = sshd1.getPort();
-            try(SshServer sshd2 = SshServer.setUpDefaultServer()) {
-                sshd2.setKeyPairProvider(Utils.createTestHostKeyProvider());
-                sshd2.setShellFactory(new TestEchoShellFactory());
-                sshd2.setPasswordAuthenticator(BogusPasswordAuthenticator.INSTANCE);
-                sshd2.setPublickeyAuthenticator(AcceptAllPublickeyAuthenticator.INSTANCE);
-                sshd1.setTcpipForwardingFilter(ForwardingFilter.AcceptAllForwardingFilter.INSTANCE);
-                sshd2.setAgentFactory(new ProxyAgentFactory());
-                sshd2.start();
-    
-                final int port2 = sshd2.getPort();
-                try(SshClient client1 = SshClient.setUpDefaultClient()) {
-                    client1.setAgentFactory(localAgentFactory);
-                    client1.start();
-                    
-                    try(ClientSession session1 = client1.connect(username, "localhost", port1).await().getSession()) {
-                        session1.auth().verify(15L, TimeUnit.SECONDS);
-
-                        try(ChannelShell channel1 = session1.createShellChannel();
-                            ByteArrayOutputStream out = new ByteArrayOutputStream();
-                            ByteArrayOutputStream err = new ByteArrayOutputStream()) {
-
-                            channel1.setOut(out);
-                            channel1.setErr(err);
-                            channel1.setAgentForwarding(true);
-                            channel1.open().await();
-                            
-                            try(OutputStream pipedIn = channel1.getInvertedIn()) {
-                                synchronized (shellFactory.shell) {
-                                    System.out.println("Possibly waiting for remote shell to start");
-                                    if (!shellFactory.shell.started) {
-                                        shellFactory.shell.wait();
-                                    }
-                                }
-                        
-                                try(SshClient client2 = SshClient.setUpDefaultClient()) {
-                                    client2.setAgentFactory(agentFactory);
-                                    client2.getProperties().putAll(shellFactory.shell.getEnvironment().getEnv());
-                                    client2.start();
-                                    
-                                    try(ClientSession session2 = client2.connect(username, "localhost", port2).await().getSession()) {
-                                        session2.auth().verify(15L, TimeUnit.SECONDS);
-
-                                        try(ChannelShell channel2 = session2.createShellChannel()) {
-                                            channel2.setIn(shellFactory.shell.getIn());
-                                            channel2.setOut(shellFactory.shell.getOut());
-                                            channel2.setErr(shellFactory.shell.getErr());
-                                            channel2.setAgentForwarding(true);
-                                            channel2.open().await();
-                                    
-                                            pipedIn.write("foo\n".getBytes());
-                                            pipedIn.flush();
-                                        }
-                                
-                                        Thread.sleep(1000);
-                                
-                                        System.out.println(out.toString());
-                                        System.err.println(err.toString());
-                            
-                                        sshd1.stop(true);
-                                        sshd2.stop(true);
-                                        client1.stop();
-                                        client2.stop();
-                                    }
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    public static class TestEchoShellFactory extends EchoShellFactory {
-
-        TestEchoShell shell = new TestEchoShell();
-
-        @Override
-        public Command create() {
-            return shell;
-        }
-
-        public class TestEchoShell extends EchoShell {
-
-            boolean started;
-
-            @Override
-            public synchronized void start(Environment env) throws IOException {
-                super.start(env);
-                started = true;
-                notifyAll();
-            }
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/test/java/org/apache/sshd/CipherTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/CipherTest.java b/sshd-core/src/test/java/org/apache/sshd/CipherTest.java
deleted file mode 100644
index c8e3ef2..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/CipherTest.java
+++ /dev/null
@@ -1,181 +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;
-
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.Arrays;
-
-import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.cipher.BuiltinCiphers;
-import org.apache.sshd.common.cipher.Cipher;
-import org.apache.sshd.common.random.BouncyCastleRandom;
-import org.apache.sshd.common.random.Random;
-import org.apache.sshd.server.SshServer;
-import org.apache.sshd.util.BaseTestSupport;
-import org.apache.sshd.util.BogusPasswordAuthenticator;
-import org.apache.sshd.util.EchoShellFactory;
-import org.apache.sshd.util.JSchLogger;
-import org.apache.sshd.util.SimpleUserInfo;
-import org.apache.sshd.util.Utils;
-import org.junit.After;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.runners.MethodSorters;
-
-import com.jcraft.jsch.JSch;
-
-/**
- * Test Cipher algorithms.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-public class CipherTest extends BaseTestSupport {
-
-    private SshServer sshd;
-    private int port;
-
-    @Test
-    public void testAES128CBC() throws Exception {
-        if (BuiltinCiphers.aes128cbc.isSupported()
-                && checkCipher(com.jcraft.jsch.jce.AES128CBC.class.getName())) {
-            setUp(BuiltinCiphers.aes128cbc);
-            runTest();
-        }
-    }
-
-    @Test
-    public void testAES192CBC() throws Exception {
-        if (BuiltinCiphers.aes192cbc.isSupported()
-                && checkCipher(com.jcraft.jsch.jce.AES192CBC.class.getName())) {
-            setUp(BuiltinCiphers.aes192cbc);
-            runTest();
-        }
-    }
-
-    @Test
-    public void testAES256CBC() throws Exception {
-        if (BuiltinCiphers.aes256cbc.isSupported()
-                && checkCipher(com.jcraft.jsch.jce.AES256CBC.class.getName())) {
-            setUp(BuiltinCiphers.aes256cbc);
-            runTest();
-        }
-    }
-
-    @Test
-    public void testBlowfishCBC() throws Exception {
-        if (BuiltinCiphers.blowfishcbc.isSupported()
-                && checkCipher(com.jcraft.jsch.jce.BlowfishCBC.class.getName())) {
-            setUp(BuiltinCiphers.blowfishcbc);
-            runTest();
-        }
-    }
-
-    @Test
-    public void testTripleDESCBC() throws Exception {
-        if (BuiltinCiphers.tripledescbc.isSupported()
-                && checkCipher(com.jcraft.jsch.jce.TripleDESCBC.class.getName())) {
-            setUp(BuiltinCiphers.tripledescbc);
-            runTest();
-        }
-    }
-
-    @Test
-    public void loadTest() throws Exception {
-        Random random = new BouncyCastleRandom();
-        loadTest(BuiltinCiphers.aes128cbc, random);
-        loadTest(BuiltinCiphers.blowfishcbc, random);
-        loadTest(BuiltinCiphers.tripledescbc, random);
-    }
-
-    protected void loadTest(NamedFactory<Cipher> factory, Random random) throws Exception {
-        Cipher cipher = factory.create();
-        byte[] key = new byte[cipher.getBlockSize()];
-        byte[] iv = new byte[cipher.getIVSize()];
-        random.fill(key, 0, key.length);
-        random.fill(iv, 0, iv.length);
-        cipher.init(Cipher.Mode.Encrypt, key, iv);
-
-        byte[] input = new byte[cipher.getBlockSize()];
-        random.fill(input, 0, input.length);
-        long t0 = System.currentTimeMillis();
-        for (int i = 0; i < 100000; i++) {
-            cipher.update(input, 0, input.length);
-        }
-        long t1 = System.currentTimeMillis();
-        System.err.println(factory.getName() + ": " + (t1 - t0) + " ms");
-    }
-
-
-    protected void setUp(NamedFactory<org.apache.sshd.common.cipher.Cipher> cipher) throws Exception {
-        sshd = SshServer.setUpDefaultServer();
-        sshd.setKeyPairProvider(Utils.createTestHostKeyProvider());
-        sshd.setCipherFactories(Arrays.<NamedFactory<org.apache.sshd.common.cipher.Cipher>>asList(cipher));
-        sshd.setShellFactory(new EchoShellFactory());
-        sshd.setPasswordAuthenticator(BogusPasswordAuthenticator.INSTANCE);
-        sshd.start();
-        port = sshd.getPort();
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        if (sshd != null) {
-            sshd.stop(true);
-        }
-    }
-
-    protected void runTest() throws Exception {
-        JSchLogger.init();
-        JSch sch = new JSch();
-        JSch.setConfig("cipher.s2c", "aes128-cbc,3des-cbc,blowfish-cbc,aes192-cbc,aes256-cbc,none");
-        JSch.setConfig("cipher.c2s", "aes128-cbc,3des-cbc,blowfish-cbc,aes192-cbc,aes256-cbc,none");
-        com.jcraft.jsch.Session s = sch.getSession(getCurrentTestName(), "localhost", port);
-        s.setUserInfo(new SimpleUserInfo(getCurrentTestName()));
-        s.connect();
-        com.jcraft.jsch.Channel c = s.openChannel("shell");
-        c.connect();
-        OutputStream os = c.getOutputStream();
-        InputStream is = c.getInputStream();
-        for (int i = 0; i < 10; i++) {
-            os.write("this is my command\n".getBytes());
-            os.flush();
-            byte[] data = new byte[512];
-            int len = is.read(data);
-            String str = new String(data, 0, len);
-            assertEquals("this is my command\n", str);
-        }
-        c.disconnect();
-        s.disconnect();
-    }
-
-    static boolean checkCipher(String cipher){
-        try{
-            Class<?> c=Class.forName(cipher);
-            com.jcraft.jsch.Cipher _c = (com.jcraft.jsch.Cipher)(c.newInstance());
-            _c.init(com.jcraft.jsch.Cipher.ENCRYPT_MODE,
-                    new byte[_c.getBlockSize()],
-                    new byte[_c.getIVSize()]);
-            return true;
-        }
-        catch(Exception e){
-            return false;
-        }
-    }
-}


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

Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/test/java/org/apache/sshd/agent/AgentTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/agent/AgentTest.java b/sshd-core/src/test/java/org/apache/sshd/agent/AgentTest.java
new file mode 100644
index 0000000..dc7d958
--- /dev/null
+++ b/sshd-core/src/test/java/org/apache/sshd/agent/AgentTest.java
@@ -0,0 +1,212 @@
+/*
+ * 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.agent;
+
+import static org.apache.sshd.util.Utils.createTestKeyPairProvider;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.security.KeyPair;
+import java.security.PublicKey;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.sshd.agent.SshAgent;
+import org.apache.sshd.agent.local.LocalAgentFactory;
+import org.apache.sshd.agent.local.ProxyAgentFactory;
+import org.apache.sshd.agent.unix.AgentClient;
+import org.apache.sshd.agent.unix.AgentServer;
+import org.apache.sshd.client.SshClient;
+import org.apache.sshd.client.channel.ChannelShell;
+import org.apache.sshd.client.session.ClientSession;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.SecurityUtils;
+import org.apache.sshd.server.Command;
+import org.apache.sshd.server.Environment;
+import org.apache.sshd.server.SshServer;
+import org.apache.sshd.server.PublickeyAuthenticator.AcceptAllPublickeyAuthenticator;
+import org.apache.sshd.server.forward.ForwardingFilter;
+import org.apache.sshd.util.BaseTestSupport;
+import org.apache.sshd.util.BogusPasswordAuthenticator;
+import org.apache.sshd.util.EchoShellFactory;
+import org.apache.sshd.util.Utils;
+import org.junit.Assume;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class AgentTest extends BaseTestSupport {
+    public AgentTest() {
+        super();
+    }
+
+    @Test
+    public void testAgentServer() throws Exception {
+        // TODO: revisit this test to work without BC
+        Assume.assumeTrue("BouncyCastle not registered", SecurityUtils.isBouncyCastleRegistered());
+
+        try(AgentServer agent = new AgentServer()) {
+            String authSocket;
+            try {
+                authSocket = agent.start();
+            } catch (UnsatisfiedLinkError e) {
+                // the native library is not available, so these tests should be skipped
+                authSocket = null;
+            }
+            Assume.assumeTrue("Native library N/A", authSocket != null);
+    
+            try(SshAgent client = new AgentClient(authSocket)) {
+                List<SshAgent.Pair<PublicKey, String>> keys = client.getIdentities();
+                assertNotNull("No initial identities", keys);
+                assertEquals("Unexpected initial identities size", 0, keys.size());
+        
+                KeyPair k = Utils.createTestHostKeyProvider().loadKey(KeyPairProvider.SSH_RSA);
+                client.addIdentity(k, "");
+                keys = client.getIdentities();
+                assertNotNull("No registered identities after add", keys);
+                assertEquals("Mismatched registered keys size", 1, keys.size());
+        
+                client.removeIdentity(k.getPublic());
+                keys = client.getIdentities();
+                assertNotNull("No registered identities after remove", keys);
+                assertEquals("Registered keys size not empty", 0, keys.size());
+        
+                client.removeAllIdentities();
+            }    
+        }
+    }
+
+    @Test
+    public void testAgentForwarding() throws Exception {
+        // TODO: revisit this test to work without BC
+        Assume.assumeTrue("BouncyCastle not registered", SecurityUtils.isBouncyCastleRegistered());
+
+        TestEchoShellFactory shellFactory = new TestEchoShellFactory();
+        ProxyAgentFactory agentFactory = new ProxyAgentFactory();
+        LocalAgentFactory localAgentFactory = new LocalAgentFactory();
+        String username = getCurrentTestName();
+        KeyPair pair = createTestKeyPairProvider("dsaprivkey.pem").loadKey(KeyPairProvider.SSH_DSS);
+        localAgentFactory.getAgent().addIdentity(pair, username);
+
+        try(SshServer sshd1 = SshServer.setUpDefaultServer()) {
+            sshd1.setKeyPairProvider(Utils.createTestHostKeyProvider());
+            sshd1.setShellFactory(shellFactory);
+            sshd1.setPasswordAuthenticator(BogusPasswordAuthenticator.INSTANCE);
+            sshd1.setPublickeyAuthenticator(AcceptAllPublickeyAuthenticator.INSTANCE);
+            sshd1.setAgentFactory(agentFactory);
+            sshd1.setTcpipForwardingFilter(ForwardingFilter.AcceptAllForwardingFilter.INSTANCE);
+            sshd1.start();
+            
+            final int port1 = sshd1.getPort();
+            try(SshServer sshd2 = SshServer.setUpDefaultServer()) {
+                sshd2.setKeyPairProvider(Utils.createTestHostKeyProvider());
+                sshd2.setShellFactory(new TestEchoShellFactory());
+                sshd2.setPasswordAuthenticator(BogusPasswordAuthenticator.INSTANCE);
+                sshd2.setPublickeyAuthenticator(AcceptAllPublickeyAuthenticator.INSTANCE);
+                sshd1.setTcpipForwardingFilter(ForwardingFilter.AcceptAllForwardingFilter.INSTANCE);
+                sshd2.setAgentFactory(new ProxyAgentFactory());
+                sshd2.start();
+    
+                final int port2 = sshd2.getPort();
+                try(SshClient client1 = SshClient.setUpDefaultClient()) {
+                    client1.setAgentFactory(localAgentFactory);
+                    client1.start();
+                    
+                    try(ClientSession session1 = client1.connect(username, "localhost", port1).await().getSession()) {
+                        session1.auth().verify(15L, TimeUnit.SECONDS);
+
+                        try(ChannelShell channel1 = session1.createShellChannel();
+                            ByteArrayOutputStream out = new ByteArrayOutputStream();
+                            ByteArrayOutputStream err = new ByteArrayOutputStream()) {
+
+                            channel1.setOut(out);
+                            channel1.setErr(err);
+                            channel1.setAgentForwarding(true);
+                            channel1.open().await();
+                            
+                            try(OutputStream pipedIn = channel1.getInvertedIn()) {
+                                synchronized (shellFactory.shell) {
+                                    System.out.println("Possibly waiting for remote shell to start");
+                                    if (!shellFactory.shell.started) {
+                                        shellFactory.shell.wait();
+                                    }
+                                }
+                        
+                                try(SshClient client2 = SshClient.setUpDefaultClient()) {
+                                    client2.setAgentFactory(agentFactory);
+                                    client2.getProperties().putAll(shellFactory.shell.getEnvironment().getEnv());
+                                    client2.start();
+                                    
+                                    try(ClientSession session2 = client2.connect(username, "localhost", port2).await().getSession()) {
+                                        session2.auth().verify(15L, TimeUnit.SECONDS);
+
+                                        try(ChannelShell channel2 = session2.createShellChannel()) {
+                                            channel2.setIn(shellFactory.shell.getIn());
+                                            channel2.setOut(shellFactory.shell.getOut());
+                                            channel2.setErr(shellFactory.shell.getErr());
+                                            channel2.setAgentForwarding(true);
+                                            channel2.open().await();
+                                    
+                                            pipedIn.write("foo\n".getBytes());
+                                            pipedIn.flush();
+                                        }
+                                
+                                        Thread.sleep(1000);
+                                
+                                        System.out.println(out.toString());
+                                        System.err.println(err.toString());
+                            
+                                        sshd1.stop(true);
+                                        sshd2.stop(true);
+                                        client1.stop();
+                                        client2.stop();
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    public static class TestEchoShellFactory extends EchoShellFactory {
+
+        TestEchoShell shell = new TestEchoShell();
+
+        @Override
+        public Command create() {
+            return shell;
+        }
+
+        public class TestEchoShell extends EchoShell {
+
+            boolean started;
+
+            @Override
+            public synchronized void start(Environment env) throws IOException {
+                super.start(env);
+                started = true;
+                notifyAll();
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/test/java/org/apache/sshd/client/ClientTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/ClientTest.java b/sshd-core/src/test/java/org/apache/sshd/client/ClientTest.java
new file mode 100644
index 0000000..8285002
--- /dev/null
+++ b/sshd-core/src/test/java/org/apache/sshd/client/ClientTest.java
@@ -0,0 +1,958 @@
+/*
+ * 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.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import java.net.SocketAddress;
+import java.security.KeyPair;
+import java.security.PublicKey;
+import java.util.Arrays;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.sshd.client.ClientFactoryManager;
+import org.apache.sshd.client.ServerKeyVerifier;
+import org.apache.sshd.client.SshClient;
+import org.apache.sshd.client.UserAuth;
+import org.apache.sshd.client.UserInteraction;
+import org.apache.sshd.client.auth.UserAuthKeyboardInteractive;
+import org.apache.sshd.client.auth.UserAuthPassword;
+import org.apache.sshd.client.auth.UserAuthPublicKey;
+import org.apache.sshd.client.channel.ChannelExec;
+import org.apache.sshd.client.channel.ChannelShell;
+import org.apache.sshd.client.channel.ClientChannel;
+import org.apache.sshd.client.future.AuthFuture;
+import org.apache.sshd.client.future.OpenFuture;
+import org.apache.sshd.client.session.ClientSession;
+import org.apache.sshd.common.FactoryManager;
+import org.apache.sshd.common.FactoryManagerUtils;
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.RuntimeSshException;
+import org.apache.sshd.common.Service;
+import org.apache.sshd.common.SshConstants;
+import org.apache.sshd.common.SshException;
+import org.apache.sshd.common.channel.Channel;
+import org.apache.sshd.common.cipher.BuiltinCiphers;
+import org.apache.sshd.common.future.CloseFuture;
+import org.apache.sshd.common.future.SshFutureListener;
+import org.apache.sshd.common.io.IoReadFuture;
+import org.apache.sshd.common.io.IoSession;
+import org.apache.sshd.common.io.IoWriteFuture;
+import org.apache.sshd.common.io.mina.MinaSession;
+import org.apache.sshd.common.io.nio2.Nio2Session;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.session.AbstractSession;
+import org.apache.sshd.common.session.ConnectionService;
+import org.apache.sshd.common.session.Session;
+import org.apache.sshd.common.sftp.SftpConstants;
+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.io.NoCloseOutputStream;
+import org.apache.sshd.server.Command;
+import org.apache.sshd.server.CommandFactory;
+import org.apache.sshd.server.PublickeyAuthenticator;
+import org.apache.sshd.server.SshServer;
+import org.apache.sshd.server.PublickeyAuthenticator.AcceptAllPublickeyAuthenticator;
+import org.apache.sshd.server.channel.ChannelSession;
+import org.apache.sshd.server.command.UnknownCommand;
+import org.apache.sshd.server.forward.TcpipServerChannel;
+import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
+import org.apache.sshd.server.session.ServerConnectionService;
+import org.apache.sshd.server.session.ServerSession;
+import org.apache.sshd.server.session.ServerUserAuthService;
+import org.apache.sshd.util.AsyncEchoShellFactory;
+import org.apache.sshd.util.BaseTestSupport;
+import org.apache.sshd.util.BogusPasswordAuthenticator;
+import org.apache.sshd.util.EchoShellFactory;
+import org.apache.sshd.util.TeeOutputStream;
+import org.apache.sshd.util.Utils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+/**
+ * TODO Add javadoc
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class ClientTest extends BaseTestSupport {
+
+    private SshServer sshd;
+    private SshClient client;
+    private int port;
+    private CountDownLatch authLatch;
+    private CountDownLatch channelLatch;
+
+    public ClientTest() {
+        super();
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        authLatch = new CountDownLatch(0);
+        channelLatch = new CountDownLatch(0);
+
+        sshd = SshServer.setUpDefaultServer();
+        sshd.setKeyPairProvider(Utils.createTestHostKeyProvider());
+        sshd.setShellFactory(new TestEchoShellFactory());
+        sshd.setCommandFactory(new CommandFactory() {
+            @Override
+            public Command createCommand(String command) {
+                return new UnknownCommand(command);
+            }
+        });
+        sshd.setPasswordAuthenticator(BogusPasswordAuthenticator.INSTANCE);
+        sshd.setPublickeyAuthenticator(AcceptAllPublickeyAuthenticator.INSTANCE);
+        sshd.setServiceFactories(Arrays.asList(
+                new ServerUserAuthService.Factory() {
+                    @Override
+                    public Service create(Session session) throws IOException {
+                        return new ServerUserAuthService(session) {
+                            @SuppressWarnings("synthetic-access")
+                            @Override
+                            public void process(byte cmd, Buffer buffer) throws Exception {
+                                authLatch.await();
+                                super.process(cmd, buffer);
+                            }
+                        };
+                    }
+                },
+                new ServerConnectionService.Factory()
+        ));
+        sshd.setChannelFactories(Arrays.<NamedFactory<Channel>>asList(
+                new ChannelSession.ChannelSessionFactory() {
+                    @Override
+                    public Channel create() {
+                        return new ChannelSession() {
+                            @SuppressWarnings("synthetic-access")
+                            @Override
+                            public OpenFuture open(int recipient, int rwsize, int rmpsize, Buffer buffer) {
+                                try {
+                                    channelLatch.await();
+                                } catch (InterruptedException e) {
+                                    throw new RuntimeSshException(e);
+                                }
+                                return super.open(recipient, rwsize, rmpsize, buffer);
+                            }
+
+                            @Override
+                            public String toString() {
+                                return "ChannelSession" + "[id=" + id + ", recipient=" + recipient + "]";
+                            }
+                        };
+                    }
+                },
+                TcpipServerChannel.DirectTcpipFactory.INSTANCE));
+        sshd.start();
+        port = sshd.getPort();
+
+        client = SshClient.setUpDefaultClient();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        if (sshd != null) {
+            sshd.stop(true);
+        }
+        if (client != null) {
+            client.stop();
+        }
+    }
+
+    @Test
+    public void testAsyncClient() throws Exception {
+        FactoryManagerUtils.updateProperty(sshd, FactoryManager.WINDOW_SIZE, 1024);
+        sshd.setShellFactory(new AsyncEchoShellFactory());
+
+        FactoryManagerUtils.updateProperty(client, FactoryManager.WINDOW_SIZE, 1024);
+        client.start();
+
+        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
+            session.addPasswordIdentity(getCurrentTestName());
+            session.auth().verify(5L, TimeUnit.SECONDS);
+
+            try(final ChannelShell channel = session.createShellChannel()) {
+                channel.setStreaming(ClientChannel.Streaming.Async);
+                channel.open().verify(5L, TimeUnit.SECONDS);
+        
+                final byte[] message = "0123456789\n".getBytes();
+                final int nbMessages = 1000;
+        
+                try(final ByteArrayOutputStream baosOut = new ByteArrayOutputStream();
+                    final ByteArrayOutputStream baosErr = new ByteArrayOutputStream()) {
+                    final AtomicInteger writes = new AtomicInteger(nbMessages);
+            
+                    channel.getAsyncIn().write(new ByteArrayBuffer(message))
+                            .addListener(new SshFutureListener<IoWriteFuture>() {
+                                @Override
+                                public void operationComplete(IoWriteFuture future) {
+                                    try {
+                                        if (future.isWritten()) {
+                                            if (writes.decrementAndGet() > 0) {
+                                                channel.getAsyncIn().write(new ByteArrayBuffer(message)).addListener(this);
+                                            } else {
+                                                channel.getAsyncIn().close(false);
+                                            }
+                                        } else {
+                                            throw new SshException("Error writing", future.getException());
+                                        }
+                                    } catch (IOException e) {
+                                        if (!channel.isClosing()) {
+                                            e.printStackTrace();
+                                            channel.close(true);
+                                        }
+                                    }
+                                }
+                            });
+                    channel.getAsyncOut().read(new ByteArrayBuffer())
+                            .addListener(new SshFutureListener<IoReadFuture>() {
+                                @Override
+                                public void operationComplete(IoReadFuture future) {
+                                    try {
+                                        future.verify(5L, TimeUnit.SECONDS);
+                                        Buffer buffer = future.getBuffer();
+                                        baosOut.write(buffer.array(), buffer.rpos(), buffer.available());
+                                        buffer.rpos(buffer.rpos() + buffer.available());
+                                        buffer.compact();
+                                        channel.getAsyncOut().read(buffer).addListener(this);
+                                    } catch (IOException e) {
+                                        if (!channel.isClosing()) {
+                                            e.printStackTrace();
+                                            channel.close(true);
+                                        }
+                                    }
+                                }
+                            });
+                    channel.getAsyncErr().read(new ByteArrayBuffer())
+                            .addListener(new SshFutureListener<IoReadFuture>() {
+                                @Override
+                                public void operationComplete(IoReadFuture future) {
+                                    try {
+                                        future.verify(5L, TimeUnit.SECONDS);
+                                        Buffer buffer = future.getBuffer();
+                                        baosErr.write(buffer.array(), buffer.rpos(), buffer.available());
+                                        buffer.rpos(buffer.rpos() + buffer.available());
+                                        buffer.compact();
+                                        channel.getAsyncErr().read(buffer).addListener(this);
+                                    } catch (IOException e) {
+                                        if (!channel.isClosing()) {
+                                            e.printStackTrace();
+                                            channel.close(true);
+                                        }
+                                    }
+                                }
+                            });
+        
+                    channel.waitFor(ClientChannel.CLOSED, 0);
+        
+                    assertEquals(nbMessages * message.length, baosOut.size());
+                }
+            }    
+
+            client.close(true);
+        } finally {
+            client.stop();
+        }
+    }
+
+    @Test
+    public void testCommandDeadlock() throws Exception {
+        client.start();
+
+        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
+            session.addPasswordIdentity(getCurrentTestName());
+            session.auth().verify(5L, TimeUnit.SECONDS);
+            
+            try(ChannelExec channel = session.createExecChannel("test");
+                OutputStream stdout = new NoCloseOutputStream(System.out);
+                OutputStream stderr = new NoCloseOutputStream(System.err)) {
+
+                channel.setOut(stdout);
+                channel.setErr(stderr);
+                channel.open().await();
+                Thread.sleep(100);
+                try {
+                    for (int i = 0; i < 100; i++) {
+                        channel.getInvertedIn().write("a".getBytes());
+                        channel.getInvertedIn().flush();
+                    }
+                } catch (SshException e) {
+                    // That's ok, the channel is being closed by the other side
+                }
+                assertEquals(ClientChannel.CLOSED, channel.waitFor(ClientChannel.CLOSED, 0) & ClientChannel.CLOSED);
+                session.close(false).await();
+            }
+        } finally {
+            client.stop();
+        }
+    }
+
+    @Test
+    public void testClient() throws Exception {
+        client.start();
+
+        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
+            session.addPasswordIdentity(getCurrentTestName());
+            session.auth().verify(5L, TimeUnit.SECONDS);
+            
+            try(ClientChannel channel = session.createChannel(ClientChannel.CHANNEL_SHELL);
+                ByteArrayOutputStream sent = new ByteArrayOutputStream();
+                PipedOutputStream pipedIn = new PipedOutputStream();
+                PipedInputStream pipedOut = new PipedInputStream(pipedIn)) {
+
+                channel.setIn(pipedOut);
+
+                try(OutputStream teeOut = new TeeOutputStream(sent, pipedIn);
+                    ByteArrayOutputStream out = new ByteArrayOutputStream();
+                    ByteArrayOutputStream err = new ByteArrayOutputStream()) {
+
+                    channel.setOut(out);
+                    channel.setErr(err);
+                    channel.open();
+            
+                    teeOut.write("this is my command\n".getBytes());
+                    teeOut.flush();
+            
+                    StringBuilder sb = new StringBuilder();
+                    for (int i = 0; i < 1000; i++) {
+                        sb.append("0123456789");
+                    }
+                    sb.append("\n");
+                    teeOut.write(sb.toString().getBytes());
+            
+                    teeOut.write("exit\n".getBytes());
+                    teeOut.flush();
+            
+                    channel.waitFor(ClientChannel.CLOSED, 0);
+            
+                    channel.close(false);
+                    client.stop();
+            
+                    assertArrayEquals(sent.toByteArray(), out.toByteArray());
+                }
+            }
+        } finally {
+            client.stop();
+        }
+    }
+
+    @Test
+    public void testClientInverted() throws Exception {
+        client.start();
+        
+        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
+            session.addPasswordIdentity(getCurrentTestName());
+            session.auth().verify(5L, TimeUnit.SECONDS);
+            
+            try(ClientChannel channel = session.createChannel(ClientChannel.CHANNEL_SHELL);
+                ByteArrayOutputStream sent = new ByteArrayOutputStream();
+                ByteArrayOutputStream out = new ByteArrayOutputStream();
+                ByteArrayOutputStream err = new ByteArrayOutputStream()) {
+
+                channel.setOut(out);
+                channel.setErr(err);
+                channel.open().await();
+        
+                try(OutputStream pipedIn = new TeeOutputStream(sent, channel.getInvertedIn())) {
+                    pipedIn.write("this is my command\n".getBytes());
+                    pipedIn.flush();
+            
+                    StringBuilder sb = new StringBuilder();
+                    for (int i = 0; i < 1000; i++) {
+                        sb.append("0123456789");
+                    }
+                    sb.append("\n");
+                    pipedIn.write(sb.toString().getBytes());
+            
+                    pipedIn.write("exit\n".getBytes());
+                    pipedIn.flush();
+                }
+        
+                channel.waitFor(ClientChannel.CLOSED, 0);
+        
+                channel.close(false);
+                client.stop();
+        
+                assertArrayEquals(sent.toByteArray(), out.toByteArray());
+            }
+        } finally {
+            client.stop();
+        }
+    }
+
+    @Test
+    public void testClientWithCustomChannel() throws Exception {
+        client.start();
+        
+        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
+            session.addPasswordIdentity(getCurrentTestName());
+            session.auth().verify(5L, TimeUnit.SECONDS);
+    
+            try(ChannelShell channel = new ChannelShell();
+                ByteArrayOutputStream sent = new ByteArrayOutputStream();
+                ByteArrayOutputStream out = new ByteArrayOutputStream();
+                ByteArrayOutputStream err = new ByteArrayOutputStream()) {
+
+                session.getService(ConnectionService.class).registerChannel(channel);
+                channel.setOut(out);
+                channel.setErr(err);
+                channel.open().verify(5L, TimeUnit.SECONDS);
+                channel.close(false).await();
+            }
+        } finally {
+            client.stop();
+        }
+    }
+
+    @Test
+    public void testClientClosingStream() throws Exception {
+        client.start();
+        
+        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
+            session.addPasswordIdentity(getCurrentTestName());
+            session.auth().verify(5L, TimeUnit.SECONDS);
+    
+            try(ClientChannel channel = session.createChannel(ClientChannel.CHANNEL_SHELL);
+                ByteArrayOutputStream sent = new ByteArrayOutputStream();
+                PipedOutputStream pipedIn = new PipedOutputStream();
+                InputStream inPipe = new PipedInputStream(pipedIn);
+                ByteArrayOutputStream out = new ByteArrayOutputStream();
+                ByteArrayOutputStream err = new ByteArrayOutputStream()) {
+
+                channel.setIn(inPipe);
+                channel.setOut(out);
+                channel.setErr(err);
+                channel.open();
+
+                try(OutputStream teeOut = new TeeOutputStream(sent, pipedIn)) {
+                    teeOut.write("this is my command\n".getBytes());
+                    teeOut.flush();
+        
+                    StringBuilder sb = new StringBuilder();
+                    for (int i = 0; i < 1000; i++) {
+                        sb.append("0123456789");
+                    }
+                    sb.append("\n");
+                    teeOut.write(sb.toString().getBytes());
+                }    
+    
+                channel.waitFor(ClientChannel.CLOSED, 0);
+        
+                channel.close(false);
+                client.stop();
+        
+                assertArrayEquals(sent.toByteArray(), out.toByteArray());
+            }
+        } finally {
+            client.stop();
+        }
+    }
+
+    @Test
+    public void testClientWithLengthyDialog() throws Exception {
+        // Reduce window size and packet size
+//        FactoryManagerUtils.updateProperty(client, SshClient.WINDOW_SIZE, 0x20000);
+//        FactoryManagerUtils.updateProperty(client, SshClient.MAX_PACKET_SIZE, 0x1000);
+//        FactoryManagerUtils.updateProperty(sshd, SshServer.WINDOW_SIZE, 0x20000);
+//        FactoryManagerUtils.updateProperty(sshd, SshServer.MAX_PACKET_SIZE, 0x1000);
+        client.start();
+        
+        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
+            session.addPasswordIdentity(getCurrentTestName());
+            session.auth().verify(5L, TimeUnit.SECONDS);
+
+            try(ClientChannel channel = session.createChannel(ClientChannel.CHANNEL_SHELL);
+                ByteArrayOutputStream sent = new ByteArrayOutputStream();
+                PipedOutputStream pipedIn = new PipedOutputStream();
+                InputStream inPipe = new PipedInputStream(pipedIn); 
+                ByteArrayOutputStream out = new ByteArrayOutputStream();
+                ByteArrayOutputStream err = new ByteArrayOutputStream()) {
+
+                channel.setIn(inPipe);
+                channel.setOut(out);
+                channel.setErr(err);
+                channel.open().await();
+        
+        
+                int bytes = 0;
+                byte[] data = "01234567890123456789012345678901234567890123456789\n".getBytes();
+                long t0 = System.currentTimeMillis();
+                try(OutputStream teeOut = new TeeOutputStream(sent, pipedIn)) {
+                    for (int i = 0; i < 10000; i++) {
+                        teeOut.write(data);
+                        teeOut.flush();
+                        bytes += data.length;
+                        if ((bytes & 0xFFF00000) != ((bytes - data.length) & 0xFFF00000)) {
+                            System.out.println("Bytes written: " + bytes);
+                        }
+                    }
+                    teeOut.write("exit\n".getBytes());
+                    teeOut.flush();
+                }        
+                long t1 = System.currentTimeMillis();
+        
+                System.out.println("Sent " + (bytes / 1024) + " Kb in " + (t1 - t0) + " ms");
+        
+                System.out.println("Waiting for channel to be closed");
+        
+                channel.waitFor(ClientChannel.CLOSED, 0);
+        
+                channel.close(false);
+                client.stop();
+        
+                assertArrayEquals(sent.toByteArray(), out.toByteArray());
+                //assertArrayEquals(sent.toByteArray(), out.toByteArray());
+            }
+        } finally {
+            client.stop();
+        }
+    }
+
+    @Test(expected = SshException.class)
+    public void testOpenChannelOnClosedSession() throws Exception {
+        client.start();
+        
+        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
+            session.addPasswordIdentity(getCurrentTestName());
+            session.auth().verify(5L, TimeUnit.SECONDS);
+            
+            try(ClientChannel channel = session.createChannel(ClientChannel.CHANNEL_SHELL)) {
+                session.close(false);
+        
+                try(PipedOutputStream pipedIn = new PipedOutputStream();
+                    InputStream inPipe = new PipedInputStream(pipedIn);
+                    ByteArrayOutputStream out = new ByteArrayOutputStream();
+                    ByteArrayOutputStream err = new ByteArrayOutputStream()) {
+
+                    channel.setIn(inPipe);
+                    channel.setOut(out);
+                    channel.setErr(err);
+                    channel.open();
+                }
+            }
+        }
+    }
+
+    @Test
+    public void testCloseBeforeAuthSucceed() throws Exception {
+        authLatch = new CountDownLatch(1);
+        client.start();
+        
+        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
+            session.addPasswordIdentity(getCurrentTestName());
+
+            AuthFuture authFuture = session.auth();
+            CloseFuture closeFuture = session.close(false);
+            authLatch.countDown();
+            authFuture.await();
+            closeFuture.await();
+            assertNotNull("No authentication exception", authFuture.getException());
+            assertTrue("Future not closed", closeFuture.isClosed());
+        } finally {
+            client.stop();
+        }
+    }
+
+    @Test
+    public void testCloseCleanBeforeChannelOpened() throws Exception {
+        client.start();
+        
+        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
+            session.addPasswordIdentity(getCurrentTestName());
+            session.auth().verify(5L, TimeUnit.SECONDS);
+
+            try(ClientChannel channel = session.createChannel(ClientChannel.CHANNEL_SHELL);
+                InputStream inp = new ByteArrayInputStream(GenericUtils.EMPTY_BYTE_ARRAY);
+                OutputStream out = new ByteArrayOutputStream();
+                OutputStream err = new ByteArrayOutputStream()) { 
+
+                channel.setIn(inp);
+                channel.setOut(out);
+                channel.setErr(err);
+
+                OpenFuture openFuture = channel.open();
+                CloseFuture closeFuture = session.close(false);
+                openFuture.await();
+                closeFuture.await();
+                assertTrue("Not open", openFuture.isOpened());
+                assertTrue("Not closed", closeFuture.isClosed());
+            }
+        } finally {
+            client.stop();
+        }
+    }
+
+    @Test
+    public void testCloseImmediateBeforeChannelOpened() throws Exception {
+        channelLatch = new CountDownLatch(1);
+        client.start();
+
+        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
+            session.addPasswordIdentity(getCurrentTestName());
+            session.auth().verify(5L, TimeUnit.SECONDS);
+
+            try(ClientChannel channel = session.createChannel(ClientChannel.CHANNEL_SHELL);
+                InputStream inp = new ByteArrayInputStream(GenericUtils.EMPTY_BYTE_ARRAY);
+                OutputStream out = new ByteArrayOutputStream();
+                OutputStream err = new ByteArrayOutputStream()) { 
+
+                channel.setIn(inp);
+                channel.setOut(out);
+                channel.setErr(err);
+
+                OpenFuture openFuture = channel.open();
+                CloseFuture closeFuture = session.close(true);
+                channelLatch.countDown();
+                openFuture.await();
+                closeFuture.await();
+                assertNotNull("No open exception", openFuture.getException());
+                assertTrue("Not closed", closeFuture.isClosed());
+            }
+        } finally {
+            client.stop();
+        }
+    }
+
+    @Test
+    public void testPublicKeyAuth() throws Exception {
+        client.start();
+        
+        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
+            KeyPair pair = Utils.createTestHostKeyProvider().loadKey(KeyPairProvider.SSH_RSA);
+            session.addPublicKeyIdentity(pair);
+            session.auth().verify(5L, TimeUnit.SECONDS);
+        } finally {
+            client.stop();
+        }
+    }
+
+    @Test
+    public void testPublicKeyAuthNew() throws Exception {
+        client.setUserAuthFactories(Arrays.<NamedFactory<UserAuth>>asList(UserAuthPublicKey.UserAuthPublicKeyFactory.INSTANCE));
+        client.start();
+        
+        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
+            session.addPublicKeyIdentity(Utils.createTestHostKeyProvider().loadKey(KeyPairProvider.SSH_RSA));
+            session.auth().verify(5L, TimeUnit.SECONDS);
+        } finally {
+            client.stop();
+        }
+    }
+
+    @Test
+    public void testPublicKeyAuthNewWithFailureOnFirstIdentity() throws Exception {
+        final KeyPair pair = Utils.createTestHostKeyProvider().loadKey(KeyPairProvider.SSH_RSA);
+        sshd.setPublickeyAuthenticator(new PublickeyAuthenticator() {
+            @Override
+            public boolean authenticate(String username, PublicKey key, ServerSession session) {
+                return key.equals(pair.getPublic());
+            }
+        });
+        client.setUserAuthFactories(Arrays.<NamedFactory<UserAuth>>asList(UserAuthPublicKey.UserAuthPublicKeyFactory.INSTANCE));
+        client.start();
+
+        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
+            session.addPublicKeyIdentity(new SimpleGeneratorHostKeyProvider(null, "RSA").loadKey(KeyPairProvider.SSH_RSA));
+            session.addPublicKeyIdentity(pair);
+            session.auth().verify(5L, TimeUnit.SECONDS);
+        } finally {
+            client.stop();
+        }
+    }
+
+    @Test
+    public void testPasswordAuthNew() throws Exception {
+        client.setUserAuthFactories(Arrays.<NamedFactory<UserAuth>>asList(new UserAuthPassword.UserAuthPasswordFactory()));
+        client.start();
+        
+        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
+            session.addPasswordIdentity(getCurrentTestName());
+            session.auth().verify(5L, TimeUnit.SECONDS);
+        } finally {
+            client.stop();
+        }
+    }
+
+    @Test
+    public void testPasswordAuthNewWithFailureOnFirstIdentity() throws Exception {
+        client.setUserAuthFactories(Arrays.<NamedFactory<UserAuth>>asList(new UserAuthPassword.UserAuthPasswordFactory()));
+        client.start();
+        
+        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
+            session.addPasswordIdentity(getClass().getSimpleName());
+            session.addPasswordIdentity(getCurrentTestName());
+            session.auth().verify(5L, TimeUnit.SECONDS);
+        } finally {
+            client.stop();
+        }
+    }
+
+    @Test
+    public void testKeyboardInteractiveAuthNew() throws Exception {
+        client.setUserAuthFactories(Arrays.<NamedFactory<UserAuth>>asList(UserAuthKeyboardInteractive.UserAuthKeyboardInteractiveFactory.INSTANCE));
+        client.start();
+        
+        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
+            session.addPasswordIdentity(getCurrentTestName());
+            session.auth().verify(5L, TimeUnit.SECONDS);
+        } finally {
+            client.stop();
+        }
+    }
+
+    @Test
+    public void testKeyboardInteractiveAuthNewWithFailureOnFirstIdentity() throws Exception {
+        client.setUserAuthFactories(Arrays.<NamedFactory<UserAuth>>asList(UserAuthKeyboardInteractive.UserAuthKeyboardInteractiveFactory.INSTANCE));
+        client.start();
+        
+        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
+            session.addPasswordIdentity(getClass().getSimpleName());
+            session.addPasswordIdentity(getCurrentTestName());
+            session.auth().verify(5L, TimeUnit.SECONDS);
+        } finally {
+            client.stop();
+        }
+    }
+
+    @Test
+    public void testKeyboardInteractiveWithFailures() throws Exception {
+        final AtomicInteger count = new AtomicInteger();
+        final int MAX_PROMPTS = 3;
+        FactoryManagerUtils.updateProperty(client, ClientFactoryManager.PASSWORD_PROMPTS, MAX_PROMPTS);
+
+        client.setUserAuthFactories(Arrays.<NamedFactory<UserAuth>>asList(new UserAuthKeyboardInteractive.UserAuthKeyboardInteractiveFactory()));
+        client.setUserInteraction(new UserInteraction() {
+            @Override
+            public void welcome(String banner) {
+                // ignored
+            }
+            @Override
+            public String[] interactive(String destination, String name, String instruction, String[] prompt, boolean[] echo) {
+                count.incrementAndGet();
+                return new String[] { "bad" };
+            }
+        });
+        client.start();
+        
+        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
+            AuthFuture future = session.auth();
+            future.await();
+            assertTrue("Unexpected authentication success", future.isFailure());
+            assertEquals("Mismatched authentication retry count", MAX_PROMPTS, count.get());
+        } finally {
+            client.stop();
+        }
+    }
+
+    @Test
+    public void testKeyboardInteractiveInSessionUserInteractive() throws Exception {
+        final AtomicInteger count = new AtomicInteger();
+        final int MAX_PROMPTS = 3;
+        FactoryManagerUtils.updateProperty(client, ClientFactoryManager.PASSWORD_PROMPTS, MAX_PROMPTS);
+
+        client.setUserAuthFactories(Arrays
+                        .<NamedFactory<UserAuth>> asList(UserAuthKeyboardInteractive.UserAuthKeyboardInteractiveFactory.INSTANCE));
+        client.start();
+
+        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
+            session.setUserInteraction(new UserInteraction() {
+                    @Override
+                    public void welcome(String banner) {
+                        // ignored
+                    }
+        
+                    @Override
+                    public String[] interactive(String destination, String name, String instruction,
+                                                String[] prompt, boolean[] echo) {
+                        count.incrementAndGet();
+                        return new String[] { getCurrentTestName() };
+                    }
+                });
+            AuthFuture future = session.auth();
+            future.await();
+            assertTrue("Authentication not marked as success", future.isSuccess());
+            assertFalse("Authentication marked as failure", future.isFailure());
+            assertEquals("Mismatched authentication attempts count", 1, count.get());
+        } finally {
+            client.stop();
+        }
+    }
+
+    @Test
+    public void testKeyboardInteractiveInSessionUserInteractiveFailure() throws Exception {
+        final AtomicInteger count = new AtomicInteger();
+        final int MAX_PROMPTS = 3;
+        FactoryManagerUtils.updateProperty(client, ClientFactoryManager.PASSWORD_PROMPTS, MAX_PROMPTS);
+        client.setUserAuthFactories(Arrays
+                        .<NamedFactory<UserAuth>> asList(new UserAuthKeyboardInteractive.UserAuthKeyboardInteractiveFactory()));
+        client.start();
+        
+        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
+            session.setUserInteraction(new UserInteraction() {
+                @Override
+                public void welcome(String banner) {
+                    // ignored
+                }
+    
+                @Override
+                public String[] interactive(String destination, String name, String instruction,
+                                            String[] prompt, boolean[] echo) {
+                    count.incrementAndGet();
+                    return new String[] { "bad" };
+                }
+            });
+            AuthFuture future = session.auth();
+            future.await();
+            assertTrue("Authentication not, marked as failure", future.isFailure());
+            assertEquals("Mismatched authentication retry count", MAX_PROMPTS, count.get());
+        } finally {
+            client.stop();
+        }
+    }
+
+    @Test
+    public void testClientDisconnect() throws Exception {
+        TestEchoShellFactory.TestEchoShell.latch = new CountDownLatch(1);
+        try {
+            client.start();
+            
+            try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
+                session.addPasswordIdentity(getCurrentTestName());
+                session.auth().verify(5L, TimeUnit.SECONDS);
+                
+                try(ClientChannel channel = session.createChannel(ClientChannel.CHANNEL_SHELL);
+                    PipedOutputStream pipedIn = new PipedOutputStream();
+                    InputStream inPipe = new PipedInputStream(pipedIn); 
+                    ByteArrayOutputStream out = new ByteArrayOutputStream();
+                    ByteArrayOutputStream err = new ByteArrayOutputStream()) {
+
+                    channel.setIn(inPipe);
+                    channel.setOut(out);
+                    channel.setErr(err);
+                    channel.open().await();
+        
+        //            ((AbstractSession) session).disconnect(SshConstants.SSH2_DISCONNECT_BY_APPLICATION, "Cancel");
+                    AbstractSession cs = (AbstractSession) session;
+                    Buffer buffer = cs.createBuffer(SshConstants.SSH_MSG_DISCONNECT);
+                    buffer.putInt(SshConstants.SSH2_DISCONNECT_BY_APPLICATION);
+                    buffer.putString("Cancel");
+                    buffer.putString("");
+                    IoWriteFuture f = cs.writePacket(buffer);
+                    f.await();
+                    suspend(cs.getIoSession());
+    
+                    TestEchoShellFactory.TestEchoShell.latch.await();
+                }
+            } finally {
+                client.stop();
+            }
+        } finally {
+            TestEchoShellFactory.TestEchoShell.latch = null;
+        }
+    }
+
+    @Test
+    public void testWaitAuth() throws Exception {
+        final AtomicBoolean ok = new AtomicBoolean();
+        client.setServerKeyVerifier(
+                new ServerKeyVerifier() {
+                    @Override
+                    public boolean verifyServerKey(
+                            ClientSession sshClientSession,
+                            SocketAddress remoteAddress,
+                            PublicKey serverKey
+                    ) {
+                        System.out.println(serverKey);
+                        ok.set(true);
+                        return true;
+                    }
+                }
+        );
+        client.start();
+        
+        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
+            session.waitFor(ClientSession.WAIT_AUTH, TimeUnit.SECONDS.toMillis(10L));
+            assertTrue(ok.get());
+        } finally {
+            client.stop();
+        }
+    }
+
+    @Test
+    public void testSwitchToNoneCipher() throws Exception {
+        sshd.getCipherFactories().add(BuiltinCiphers.none);
+        client.getCipherFactories().add(BuiltinCiphers.none);
+        client.start();
+        
+        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
+            session.addPasswordIdentity(getCurrentTestName());
+            session.auth().verify(5L, TimeUnit.SECONDS);
+            session.switchToNoneCipher().await();
+    
+            try(ClientChannel channel = session.createSubsystemChannel(SftpConstants.SFTP_SUBSYSTEM_NAME)) {
+                channel.open().verify(5L, TimeUnit.SECONDS);
+            }
+        } finally {
+            client.stop();
+        }
+    }
+
+    private void suspend(IoSession ioSession) {
+        if (ioSession instanceof MinaSession) {
+            ((MinaSession) ioSession).suspend();
+        } else {
+            ((Nio2Session) ioSession).suspend();
+        }
+    }
+
+    public static class TestEchoShellFactory extends EchoShellFactory {
+        @Override
+        public Command create() {
+            return new TestEchoShell();
+        }
+        public static class TestEchoShell extends EchoShell {
+
+            public static CountDownLatch latch = new CountDownLatch(1);
+
+            @Override
+            public void destroy() {
+                if (latch != null) {
+                    latch.countDown();
+                }
+                super.destroy();
+            }
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        SshClient.main(args);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/test/java/org/apache/sshd/client/session/ClientSessionImplTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/session/ClientSessionImplTest.java b/sshd-core/src/test/java/org/apache/sshd/client/session/ClientSessionImplTest.java
new file mode 100644
index 0000000..c17e711
--- /dev/null
+++ b/sshd-core/src/test/java/org/apache/sshd/client/session/ClientSessionImplTest.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.client.session;
+
+import java.io.IOException;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.sshd.client.ClientFactoryManager;
+import org.apache.sshd.common.Factory;
+import org.apache.sshd.common.ServiceFactory;
+import org.apache.sshd.common.forward.DefaultTcpipForwarderFactory;
+import org.apache.sshd.common.io.IoSession;
+import org.apache.sshd.common.random.JceRandom;
+import org.apache.sshd.common.random.Random;
+import org.apache.sshd.common.random.SingletonRandomFactory;
+import org.apache.sshd.util.BaseTestSupport;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+import org.mockito.Mockito;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class ClientSessionImplTest extends BaseTestSupport {
+    public ClientSessionImplTest() {
+        super();
+    }
+
+    @Test
+    public void testAddRemoveIdentities() throws Exception {
+        ClientFactoryManager client = Mockito.mock(ClientFactoryManager.class);
+        Mockito.when(client.getTcpipForwarderFactory()).thenReturn(DefaultTcpipForwarderFactory.INSTANCE);
+        
+        Factory<Random> randomFactory = new SingletonRandomFactory(JceRandom.JceRandomFactory.INSTANCE);
+        Mockito.when(client.getRandomFactory()).thenReturn(randomFactory);
+        
+        List<ServiceFactory> serviceFactories = Arrays.asList(
+                    new ClientUserAuthService.Factory(),
+                    new ClientConnectionService.Factory()
+                );
+        Mockito.when(client.getServiceFactories()).thenReturn(serviceFactories);
+
+        try(ClientSession session = new ClientSessionImpl(client, Mockito.mock(IoSession.class)) {
+            @Override
+            protected void sendClientIdentification() {
+                // ignored
+            }
+            
+            @Override
+            protected void sendKexInit() throws IOException {
+                // ignored
+            }
+
+            @Override
+            public void close() throws IOException {
+                // ignored
+            }
+        }) {
+            {
+                String expected = getCurrentTestName();
+                assertNull("Unexpected initial password identity", session.removePasswordIdentity(expected));
+                session.addPasswordIdentity(expected);
+    
+                String actual = session.removePasswordIdentity(expected);
+                assertSame("Mismatched removed password identity", expected, actual);
+                assertNull("Password identity not removed", session.removePasswordIdentity(expected));
+            }
+            
+            {
+                KeyPair expected = new KeyPair(Mockito.mock(PublicKey.class), Mockito.mock(PrivateKey.class));
+                assertNull("Unexpected initial pubket identity", session.removePublicKeyIdentity(expected));
+                session.addPublicKeyIdentity(expected);
+                
+                KeyPair actual = session.removePublicKeyIdentity(expected);
+                assertSame("Mismatched removed pubkey identity", expected, actual);
+                assertNull("Pubkey identity not removed", session.removePublicKeyIdentity(expected));
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/test/java/org/apache/sshd/client/sftp/SftpFileSystemTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/sftp/SftpFileSystemTest.java b/sshd-core/src/test/java/org/apache/sshd/client/sftp/SftpFileSystemTest.java
index 1d79f0f..71cd5b0 100644
--- a/sshd-core/src/test/java/org/apache/sshd/client/sftp/SftpFileSystemTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/client/sftp/SftpFileSystemTest.java
@@ -47,8 +47,8 @@ import org.apache.sshd.common.file.FileSystemFactory;
 import org.apache.sshd.common.file.root.RootedFileSystemProvider;
 import org.apache.sshd.common.session.Session;
 import org.apache.sshd.common.sftp.SftpConstants;
-import org.apache.sshd.common.util.IoUtils;
 import org.apache.sshd.common.util.OsUtils;
+import org.apache.sshd.common.util.io.IoUtils;
 import org.apache.sshd.server.Command;
 import org.apache.sshd.server.SshServer;
 import org.apache.sshd.server.command.ScpCommandFactory;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/test/java/org/apache/sshd/client/sftp/SftpTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/sftp/SftpTest.java b/sshd-core/src/test/java/org/apache/sshd/client/sftp/SftpTest.java
index 7aadf8c..7dc2694 100644
--- a/sshd-core/src/test/java/org/apache/sshd/client/sftp/SftpTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/client/sftp/SftpTest.java
@@ -47,9 +47,9 @@ import org.apache.sshd.common.file.FileSystemFactory;
 import org.apache.sshd.common.file.root.RootedFileSystemProvider;
 import org.apache.sshd.common.session.Session;
 import org.apache.sshd.common.sftp.SftpConstants;
-import org.apache.sshd.common.util.IoUtils;
 import org.apache.sshd.common.util.OsUtils;
 import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
+import org.apache.sshd.common.util.io.IoUtils;
 import org.apache.sshd.server.Command;
 import org.apache.sshd.server.SshServer;
 import org.apache.sshd.server.command.ScpCommandFactory;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/test/java/org/apache/sshd/common/SshBuilderTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/SshBuilderTest.java b/sshd-core/src/test/java/org/apache/sshd/common/SshBuilderTest.java
new file mode 100644
index 0000000..f801f44
--- /dev/null
+++ b/sshd-core/src/test/java/org/apache/sshd/common/SshBuilderTest.java
@@ -0,0 +1,136 @@
+/*
+ * 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.Collection;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.sshd.common.BaseBuilder;
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.cipher.BuiltinCiphers;
+import org.apache.sshd.common.cipher.Cipher;
+import org.apache.sshd.common.kex.BuiltinDHFactories;
+import org.apache.sshd.common.mac.BuiltinMacs;
+import org.apache.sshd.common.signature.BuiltinSignatures;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.util.BaseTestSupport;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class SshBuilderTest extends BaseTestSupport {
+    public SshBuilderTest() {
+        super();
+    }
+
+    /**
+     * Make sure that all values in {@link BuiltinCiphers} are
+     * listed in {@link BaseBuilder#DEFAULT_CIPHERS_PREFERENCE}
+     */
+    @Test
+    public void testAllBuiltinCiphersListed() {
+        Set<BuiltinCiphers> all=EnumSet.allOf(BuiltinCiphers.class);
+        // The 'none' cipher is not listed as preferred - it is implied
+        assertTrue("Missing " + BuiltinCiphers.Constants.NONE + " cipher in all values", all.remove(BuiltinCiphers.none));
+        testAllInstancesListed(all, BaseBuilder.DEFAULT_CIPHERS_PREFERENCE);
+    }
+
+    /**
+     * Make sure that all values in {@link BuiltinMacs} are
+     * listed in {@link BaseBuilder#DEFAULT_MAC_PREFERENCE}
+     */
+    @Test
+    public void testAllBuiltinMacsListed() {
+        testAllInstancesListed(BuiltinMacs.VALUES, BaseBuilder.DEFAULT_MAC_PREFERENCE);
+    }
+
+    /**
+     * Make sure that all values in {@link BuiltinSignatures} are
+     * listed in {@link BaseBuilder#DEFAULT_SIGNATURE_PREFERENCE}
+     */
+    @Test
+    public void testAllBuiltinSignaturesListed() {
+        testAllInstancesListed(BuiltinSignatures.VALUES, BaseBuilder.DEFAULT_SIGNATURE_PREFERENCE);
+    }
+
+    /**
+     * Make sure that all values in {@link BuiltinDHFactories} are
+     * listed in {@link BaseBuilder#DEFAULT_KEX_PREFERENCE}
+     */
+    @Test
+    public void testAllBuiltinDHFactoriesListed() {
+        testAllInstancesListed(BuiltinDHFactories.VALUES, BaseBuilder.DEFAULT_KEX_PREFERENCE);
+    }
+
+    private static <E extends Enum<E>> void testAllInstancesListed(Set<? extends E> expValues, Collection<? extends E> actValues) {
+        assertEquals("Mismatched actual values size", expValues.size(), actValues.size());
+        for (E expected : expValues) {
+            assertTrue(expected.name() + " not found in actual values", actValues.contains(expected));
+        }
+    }
+
+    /**
+     * Make sure that {@link BaseBuilder#setUpDefaultCiphers(boolean)} returns
+     * the correct result - i.e., according to the {@code ingoreUnsupported}
+     * parameter and in the defined preference order
+     */
+    @Test
+    public void testSetUpDefaultCiphers() {
+        for (boolean ignoreUnsupported : new boolean[] { true, false }) {
+            List<NamedFactory<Cipher>>  ciphers=BaseBuilder.setUpDefaultCiphers(ignoreUnsupported);
+            int                         numCiphers=GenericUtils.size(ciphers);
+            // make sure returned list size matches expected count
+            if (ignoreUnsupported) {
+                assertEquals("Incomplete full ciphers size", BaseBuilder.DEFAULT_CIPHERS_PREFERENCE.size(), numCiphers);
+            } else {
+                int expectedCount=0;
+                for (BuiltinCiphers c : BaseBuilder.DEFAULT_CIPHERS_PREFERENCE) {
+                    if (c.isSupported()) {
+                        expectedCount++;
+                    }
+                }
+                assertEquals("Incomplete supported ciphers size", expectedCount, numCiphers);
+            }
+            
+            // make sure order is according to the default preference list
+            List<String>    cipherNames=NamedResource.Utils.getNameList(ciphers);
+            int             nameIndex=0;
+            for (BuiltinCiphers c : BaseBuilder.DEFAULT_CIPHERS_PREFERENCE) {
+                if ((!c.isSupported()) && (!ignoreUnsupported)) {
+                    continue;
+                }
+                
+                String  expectedName=c.getName();
+                assertTrue("Out of actual ciphers for expected=" + expectedName, nameIndex < numCiphers);
+                
+                String  actualName=cipherNames.get(nameIndex);
+                assertEquals("Mismatched cipher at position " + nameIndex + " for ignoreUnsupported=" + ignoreUnsupported, expectedName, actualName);
+                nameIndex++;
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/test/java/org/apache/sshd/common/cipher/CipherTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/cipher/CipherTest.java b/sshd-core/src/test/java/org/apache/sshd/common/cipher/CipherTest.java
new file mode 100644
index 0000000..bc2d2ff
--- /dev/null
+++ b/sshd-core/src/test/java/org/apache/sshd/common/cipher/CipherTest.java
@@ -0,0 +1,181 @@
+/*
+ * 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.cipher;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Arrays;
+
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.cipher.BuiltinCiphers;
+import org.apache.sshd.common.cipher.Cipher;
+import org.apache.sshd.common.random.BouncyCastleRandom;
+import org.apache.sshd.common.random.Random;
+import org.apache.sshd.server.SshServer;
+import org.apache.sshd.util.BaseTestSupport;
+import org.apache.sshd.util.BogusPasswordAuthenticator;
+import org.apache.sshd.util.EchoShellFactory;
+import org.apache.sshd.util.JSchLogger;
+import org.apache.sshd.util.SimpleUserInfo;
+import org.apache.sshd.util.Utils;
+import org.junit.After;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+import com.jcraft.jsch.JSch;
+
+/**
+ * Test Cipher algorithms.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class CipherTest extends BaseTestSupport {
+
+    private SshServer sshd;
+    private int port;
+
+    @Test
+    public void testAES128CBC() throws Exception {
+        if (BuiltinCiphers.aes128cbc.isSupported()
+                && checkCipher(com.jcraft.jsch.jce.AES128CBC.class.getName())) {
+            setUp(BuiltinCiphers.aes128cbc);
+            runTest();
+        }
+    }
+
+    @Test
+    public void testAES192CBC() throws Exception {
+        if (BuiltinCiphers.aes192cbc.isSupported()
+                && checkCipher(com.jcraft.jsch.jce.AES192CBC.class.getName())) {
+            setUp(BuiltinCiphers.aes192cbc);
+            runTest();
+        }
+    }
+
+    @Test
+    public void testAES256CBC() throws Exception {
+        if (BuiltinCiphers.aes256cbc.isSupported()
+                && checkCipher(com.jcraft.jsch.jce.AES256CBC.class.getName())) {
+            setUp(BuiltinCiphers.aes256cbc);
+            runTest();
+        }
+    }
+
+    @Test
+    public void testBlowfishCBC() throws Exception {
+        if (BuiltinCiphers.blowfishcbc.isSupported()
+                && checkCipher(com.jcraft.jsch.jce.BlowfishCBC.class.getName())) {
+            setUp(BuiltinCiphers.blowfishcbc);
+            runTest();
+        }
+    }
+
+    @Test
+    public void testTripleDESCBC() throws Exception {
+        if (BuiltinCiphers.tripledescbc.isSupported()
+                && checkCipher(com.jcraft.jsch.jce.TripleDESCBC.class.getName())) {
+            setUp(BuiltinCiphers.tripledescbc);
+            runTest();
+        }
+    }
+
+    @Test
+    public void loadTest() throws Exception {
+        Random random = new BouncyCastleRandom();
+        loadTest(BuiltinCiphers.aes128cbc, random);
+        loadTest(BuiltinCiphers.blowfishcbc, random);
+        loadTest(BuiltinCiphers.tripledescbc, random);
+    }
+
+    protected void loadTest(NamedFactory<Cipher> factory, Random random) throws Exception {
+        Cipher cipher = factory.create();
+        byte[] key = new byte[cipher.getBlockSize()];
+        byte[] iv = new byte[cipher.getIVSize()];
+        random.fill(key, 0, key.length);
+        random.fill(iv, 0, iv.length);
+        cipher.init(Cipher.Mode.Encrypt, key, iv);
+
+        byte[] input = new byte[cipher.getBlockSize()];
+        random.fill(input, 0, input.length);
+        long t0 = System.currentTimeMillis();
+        for (int i = 0; i < 100000; i++) {
+            cipher.update(input, 0, input.length);
+        }
+        long t1 = System.currentTimeMillis();
+        System.err.println(factory.getName() + ": " + (t1 - t0) + " ms");
+    }
+
+
+    protected void setUp(NamedFactory<org.apache.sshd.common.cipher.Cipher> cipher) throws Exception {
+        sshd = SshServer.setUpDefaultServer();
+        sshd.setKeyPairProvider(Utils.createTestHostKeyProvider());
+        sshd.setCipherFactories(Arrays.<NamedFactory<org.apache.sshd.common.cipher.Cipher>>asList(cipher));
+        sshd.setShellFactory(new EchoShellFactory());
+        sshd.setPasswordAuthenticator(BogusPasswordAuthenticator.INSTANCE);
+        sshd.start();
+        port = sshd.getPort();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        if (sshd != null) {
+            sshd.stop(true);
+        }
+    }
+
+    protected void runTest() throws Exception {
+        JSchLogger.init();
+        JSch sch = new JSch();
+        JSch.setConfig("cipher.s2c", "aes128-cbc,3des-cbc,blowfish-cbc,aes192-cbc,aes256-cbc,none");
+        JSch.setConfig("cipher.c2s", "aes128-cbc,3des-cbc,blowfish-cbc,aes192-cbc,aes256-cbc,none");
+        com.jcraft.jsch.Session s = sch.getSession(getCurrentTestName(), "localhost", port);
+        s.setUserInfo(new SimpleUserInfo(getCurrentTestName()));
+        s.connect();
+        com.jcraft.jsch.Channel c = s.openChannel("shell");
+        c.connect();
+        OutputStream os = c.getOutputStream();
+        InputStream is = c.getInputStream();
+        for (int i = 0; i < 10; i++) {
+            os.write("this is my command\n".getBytes());
+            os.flush();
+            byte[] data = new byte[512];
+            int len = is.read(data);
+            String str = new String(data, 0, len);
+            assertEquals("this is my command\n", str);
+        }
+        c.disconnect();
+        s.disconnect();
+    }
+
+    static boolean checkCipher(String cipher){
+        try{
+            Class<?> c=Class.forName(cipher);
+            com.jcraft.jsch.Cipher _c = (com.jcraft.jsch.Cipher)(c.newInstance());
+            _c.init(com.jcraft.jsch.Cipher.ENCRYPT_MODE,
+                    new byte[_c.getBlockSize()],
+                    new byte[_c.getIVSize()]);
+            return true;
+        }
+        catch(Exception e){
+            return false;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/test/java/org/apache/sshd/common/config/SshConfigFileReaderTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/config/SshConfigFileReaderTest.java b/sshd-core/src/test/java/org/apache/sshd/common/config/SshConfigFileReaderTest.java
index 5a9aa61..41b4296 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/config/SshConfigFileReaderTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/config/SshConfigFileReaderTest.java
@@ -32,7 +32,6 @@ import org.apache.sshd.common.Closeable;
 import org.apache.sshd.common.FactoryManager;
 import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.NamedResource;
-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.compression.BuiltinCompressions;
@@ -44,6 +43,7 @@ import org.apache.sshd.common.mac.Mac;
 import org.apache.sshd.common.signature.BuiltinSignatures;
 import org.apache.sshd.common.signature.Signature;
 import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.Transformer;
 import org.apache.sshd.util.BaseTestSupport;
 import org.junit.FixMethodOrder;
 import org.junit.Test;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsTest.java b/sshd-core/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsTest.java
new file mode 100644
index 0000000..680eec8
--- /dev/null
+++ b/sshd-core/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsTest.java
@@ -0,0 +1,138 @@
+/*
+ * 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.config.keys;
+
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+
+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.util.BaseTestSupport;
+import org.junit.Assume;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class KeyUtilsTest extends BaseTestSupport {
+    private static int[] DSS_SIZES = { 512, 768, 1024 };
+    private static int[] RSA_SIZES = { 1024, 2048, 3072, 4096 };
+
+    public KeyUtilsTest() {
+        super();
+    }
+
+    @Test
+    public void testGenerateRSAKeyPairs() throws GeneralSecurityException {
+        GeneralSecurityException err = null;
+        for (int keySize : RSA_SIZES) {
+            try {
+                KeyPair kp = generateKeyPair(KeyPairProvider.SSH_RSA, keySize);
+                testKeyPairCloning(KeyPairProvider.SSH_RSA, keySize, kp);
+            } catch(GeneralSecurityException e) {
+                err = GenericUtils.accumulateException(err, e);
+            }
+        }
+        
+        if (err != null) {
+            throw err;
+        }
+    }
+
+    @Test
+    public void testGenerateDSSKeyPairs() throws GeneralSecurityException {
+        GeneralSecurityException err = null;
+        for (int keySize : DSS_SIZES) {
+            try {
+                KeyPair kp = generateKeyPair(KeyPairProvider.SSH_DSS, keySize);
+                testKeyPairCloning(KeyPairProvider.SSH_DSS, keySize, kp);
+            } catch(GeneralSecurityException e) {
+                err = GenericUtils.accumulateException(err, e);
+            }
+        }
+        
+        if (err != null) {
+            throw err;
+        }
+    }
+
+    @Test
+    public void testGenerateECDSAKeyPairs() throws GeneralSecurityException {
+        Assume.assumeTrue("No ECC support", SecurityUtils.hasEcc());
+
+        GeneralSecurityException err = null;
+        for (String curveName : ECCurves.NAMES) {
+            Integer keySize = ECCurves.getCurveSize(curveName);
+            try {
+                String keyType = ECCurves.ECDSA_SHA2_PREFIX + curveName;
+                KeyPair kp = generateKeyPair(keyType, keySize.intValue());
+                testKeyPairCloning(keyType, keySize.intValue(), kp);
+            } catch(GeneralSecurityException e) {
+                err = GenericUtils.accumulateException(err, e);
+            }
+        }
+        
+        if (err != null) {
+            throw err;
+        }
+    }
+
+    private static KeyPair generateKeyPair(String keyType, int keySize) throws GeneralSecurityException {
+        try {
+            System.out.println("generateKeyPair(" + keyType + ")[" + keySize + "]");
+            return KeyUtils.generateKeyPair(keyType, keySize);
+        } catch(GeneralSecurityException e) {
+            System.err.println("Failed (" + e.getClass().getSimpleName() + ") to generate key-pair for " + keyType + "/" + keySize + ": " + e.getMessage());
+            throw e;
+        }
+    }
+    
+    private static void testKeyPairCloning(String keyType, int keySize, KeyPair kp) throws GeneralSecurityException {
+        String prefix = keyType + "[" + keySize + "]";
+        System.out.println("testKeyPairCloning(" + prefix + ")");
+
+        KeyPair cloned = KeyUtils.cloneKeyPair(keyType, kp);
+        assertNotSame(prefix + ": Key pair not cloned", kp, cloned);
+        assertTrue(prefix + ": Cloned pair not equals", KeyUtils.compareKeyPairs(kp, cloned));
+        
+        {
+            PublicKey k1 = kp.getPublic(), k2 = cloned.getPublic();
+            assertNotSame(prefix + ": Public key not cloned", k1, k2);
+            assertTrue(prefix + ": Cloned public key not equals", KeyUtils.compareKeys(k1, k2));
+            
+            String f1 = KeyUtils.getFingerPrint(k1), f2 = KeyUtils.getFingerPrint(k2);
+            assertEquals(prefix + ": Mismatched fingerprints", f1, f2);
+        }
+        
+        {
+            PrivateKey k1 = kp.getPrivate(), k2 = cloned.getPrivate();
+            assertNotSame(prefix + ": Private key not cloned", k1, k2);
+            assertTrue(prefix + ": Cloned private key not equals", KeyUtils.compareKeys(k1, k2));
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/test/java/org/apache/sshd/common/mac/MacTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/mac/MacTest.java b/sshd-core/src/test/java/org/apache/sshd/common/mac/MacTest.java
new file mode 100644
index 0000000..ea9c555
--- /dev/null
+++ b/sshd-core/src/test/java/org/apache/sshd/common/mac/MacTest.java
@@ -0,0 +1,188 @@
+/*
+ * 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.mac;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Arrays;
+
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.cipher.BuiltinCiphers;
+import org.apache.sshd.common.cipher.Cipher;
+import org.apache.sshd.common.mac.BuiltinMacs;
+import org.apache.sshd.common.mac.Mac;
+import org.apache.sshd.common.random.BouncyCastleRandom;
+import org.apache.sshd.common.random.Random;
+import org.apache.sshd.server.SshServer;
+import org.apache.sshd.util.BaseTestSupport;
+import org.apache.sshd.util.BogusPasswordAuthenticator;
+import org.apache.sshd.util.EchoShellFactory;
+import org.apache.sshd.util.JSchLogger;
+import org.apache.sshd.util.SimpleUserInfo;
+import org.apache.sshd.util.Utils;
+import org.junit.After;
+import org.junit.FixMethodOrder;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+import com.jcraft.jsch.JSch;
+
+/**
+ * Test Cipher algorithms.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class MacTest extends BaseTestSupport {
+
+    private SshServer sshd;
+    private int port;
+
+    @Test
+    public void testHMACMD5() throws Exception {
+        setUp(BuiltinMacs.hmacmd5);
+        runTest();
+    }
+
+    @Test
+    public void testHMACMD596() throws Exception {
+        setUp(BuiltinMacs.hmacmd596);
+        runTest();
+    }
+
+    @Test
+    public void testHMACSHA1() throws Exception {
+        setUp(BuiltinMacs.hmacsha1);
+        runTest();
+    }
+
+    @Test
+    public void testHMACSHA196() throws Exception {
+        setUp(BuiltinMacs.hmacsha196);
+        runTest();
+    }
+
+    @Test
+    public void testHMACSHA256() throws Exception {
+        setUp(BuiltinMacs.hmacsha256);
+        runTest();
+    }
+
+    @Test
+    @Ignore("Lead to ArrayIndexOutOfBoundsException in JSch")
+    public void testHMACSHA512() throws Exception {
+        setUp(BuiltinMacs.hmacsha512);
+        runTest();
+    }
+
+    @Test
+    public void loadTest() throws Exception {
+        Random random = new BouncyCastleRandom();
+        loadTest(BuiltinCiphers.aes128cbc, random);
+        loadTest(BuiltinCiphers.blowfishcbc, random);
+        loadTest(BuiltinCiphers.tripledescbc, random);
+    }
+
+    protected void loadTest(NamedFactory<Cipher> factory, Random random) throws Exception {
+        Cipher cipher = factory.create();
+        byte[] key = new byte[cipher.getBlockSize()];
+        byte[] iv = new byte[cipher.getIVSize()];
+        random.fill(key, 0, key.length);
+        random.fill(iv, 0, iv.length);
+        cipher.init(Cipher.Mode.Encrypt, key, iv);
+
+        byte[] input = new byte[cipher.getBlockSize()];
+        random.fill(input, 0, input.length);
+        long t0 = System.currentTimeMillis();
+        for (int i = 0; i < 100000; i++) {
+            cipher.update(input, 0, input.length);
+        }
+        long t1 = System.currentTimeMillis();
+        System.err.println(factory.getName() + ": " + (t1 - t0) + " ms");
+    }
+
+
+    protected void setUp(NamedFactory<Mac> mac) throws Exception {
+        sshd = SshServer.setUpDefaultServer();
+        sshd.setKeyPairProvider(Utils.createTestHostKeyProvider());
+        sshd.setMacFactories(Arrays.<NamedFactory<Mac>>asList(mac));
+        sshd.setShellFactory(new EchoShellFactory());
+        sshd.setPasswordAuthenticator(BogusPasswordAuthenticator.INSTANCE);
+        sshd.start();
+        port  = sshd.getPort();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        if (sshd != null) {
+            sshd.stop(true);
+        }
+    }
+
+    protected void runTest() throws Exception {
+        JSchLogger.init();
+        JSch sch = new JSch();
+        JSch.setConfig("cipher.s2c", "aes128-cbc,3des-cbc,blowfish-cbc,aes192-cbc,aes256-cbc,none");
+        JSch.setConfig("cipher.c2s", "aes128-cbc,3des-cbc,blowfish-cbc,aes192-cbc,aes256-cbc,none");
+        JSch.setConfig("mac.s2c", "hmac-md5,hmac-sha1,hmac-sha2-256,hmac-sha1-96,hmac-md5-96,hmac-sha2-512");
+        JSch.setConfig("mac.c2s", "hmac-md5,hmac-sha1,hmac-sha2-256,hmac-sha1-96,hmac-md5-96,hmac-sha2-512");
+        JSch.setConfig("hmac-sha2-512",  "com.jcraft.jsch.jce.HMACSHA512");
+        com.jcraft.jsch.Session s = sch.getSession(getCurrentTestName(), "localhost", port);
+        try {
+            s.setUserInfo(new SimpleUserInfo(getCurrentTestName()));
+            s.connect();
+            com.jcraft.jsch.Channel c = s.openChannel("shell");
+            c.connect();
+
+            try(OutputStream os = c.getOutputStream();
+                InputStream is = c.getInputStream()) {
+
+                String  expected = "this is my command\n";
+                byte[] bytes = expected.getBytes();
+                byte[] data = new byte[bytes.length + Long.SIZE];
+                for (int i = 0; i < 10; i++) {
+                    os.write(bytes);
+                    os.flush();
+                    int len = is.read(data);
+                    String str = new String(data, 0, len);
+                    assertEquals("Mismatched data at iteration " + i, expected, str);
+                }
+            } finally {
+                c.disconnect();
+            }
+        } finally {
+            s.disconnect();
+        }
+    }
+
+    static boolean checkCipher(String cipher){
+        try{
+            Class<?> c=Class.forName(cipher);
+            com.jcraft.jsch.Cipher _c = (com.jcraft.jsch.Cipher)(c.newInstance());
+            _c.init(com.jcraft.jsch.Cipher.ENCRYPT_MODE,
+                    new byte[_c.getBlockSize()],
+                    new byte[_c.getIVSize()]);
+            return true;
+        }
+        catch(Exception e){
+            return false;
+        }
+    }
+}


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

Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/test/java/org/apache/sshd/ClientTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/ClientTest.java b/sshd-core/src/test/java/org/apache/sshd/ClientTest.java
deleted file mode 100644
index 5f956b6..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/ClientTest.java
+++ /dev/null
@@ -1,958 +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;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.PipedInputStream;
-import java.io.PipedOutputStream;
-import java.net.SocketAddress;
-import java.security.KeyPair;
-import java.security.PublicKey;
-import java.util.Arrays;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import org.apache.sshd.client.ClientFactoryManager;
-import org.apache.sshd.client.ServerKeyVerifier;
-import org.apache.sshd.client.SshClient;
-import org.apache.sshd.client.UserAuth;
-import org.apache.sshd.client.UserInteraction;
-import org.apache.sshd.client.auth.UserAuthKeyboardInteractive;
-import org.apache.sshd.client.auth.UserAuthPassword;
-import org.apache.sshd.client.auth.UserAuthPublicKey;
-import org.apache.sshd.client.channel.ChannelExec;
-import org.apache.sshd.client.channel.ChannelShell;
-import org.apache.sshd.client.channel.ClientChannel;
-import org.apache.sshd.client.future.AuthFuture;
-import org.apache.sshd.client.future.OpenFuture;
-import org.apache.sshd.client.session.ClientSession;
-import org.apache.sshd.common.FactoryManager;
-import org.apache.sshd.common.FactoryManagerUtils;
-import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.RuntimeSshException;
-import org.apache.sshd.common.Service;
-import org.apache.sshd.common.SshConstants;
-import org.apache.sshd.common.SshException;
-import org.apache.sshd.common.channel.Channel;
-import org.apache.sshd.common.cipher.BuiltinCiphers;
-import org.apache.sshd.common.future.CloseFuture;
-import org.apache.sshd.common.future.SshFutureListener;
-import org.apache.sshd.common.io.IoReadFuture;
-import org.apache.sshd.common.io.IoSession;
-import org.apache.sshd.common.io.IoWriteFuture;
-import org.apache.sshd.common.io.mina.MinaSession;
-import org.apache.sshd.common.io.nio2.Nio2Session;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.session.AbstractSession;
-import org.apache.sshd.common.session.ConnectionService;
-import org.apache.sshd.common.session.Session;
-import org.apache.sshd.common.sftp.SftpConstants;
-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.io.NoCloseOutputStream;
-import org.apache.sshd.server.Command;
-import org.apache.sshd.server.CommandFactory;
-import org.apache.sshd.server.PublickeyAuthenticator;
-import org.apache.sshd.server.SshServer;
-import org.apache.sshd.server.PublickeyAuthenticator.AcceptAllPublickeyAuthenticator;
-import org.apache.sshd.server.channel.ChannelSession;
-import org.apache.sshd.server.command.UnknownCommand;
-import org.apache.sshd.server.forward.TcpipServerChannel;
-import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
-import org.apache.sshd.server.session.ServerConnectionService;
-import org.apache.sshd.server.session.ServerSession;
-import org.apache.sshd.server.session.ServerUserAuthService;
-import org.apache.sshd.util.AsyncEchoShellFactory;
-import org.apache.sshd.util.BaseTestSupport;
-import org.apache.sshd.util.BogusPasswordAuthenticator;
-import org.apache.sshd.util.EchoShellFactory;
-import org.apache.sshd.util.TeeOutputStream;
-import org.apache.sshd.util.Utils;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.runners.MethodSorters;
-
-/**
- * TODO Add javadoc
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-public class ClientTest extends BaseTestSupport {
-
-    private SshServer sshd;
-    private SshClient client;
-    private int port;
-    private CountDownLatch authLatch;
-    private CountDownLatch channelLatch;
-
-    public ClientTest() {
-        super();
-    }
-
-    @Before
-    public void setUp() throws Exception {
-        authLatch = new CountDownLatch(0);
-        channelLatch = new CountDownLatch(0);
-
-        sshd = SshServer.setUpDefaultServer();
-        sshd.setKeyPairProvider(Utils.createTestHostKeyProvider());
-        sshd.setShellFactory(new TestEchoShellFactory());
-        sshd.setCommandFactory(new CommandFactory() {
-            @Override
-            public Command createCommand(String command) {
-                return new UnknownCommand(command);
-            }
-        });
-        sshd.setPasswordAuthenticator(BogusPasswordAuthenticator.INSTANCE);
-        sshd.setPublickeyAuthenticator(AcceptAllPublickeyAuthenticator.INSTANCE);
-        sshd.setServiceFactories(Arrays.asList(
-                new ServerUserAuthService.Factory() {
-                    @Override
-                    public Service create(Session session) throws IOException {
-                        return new ServerUserAuthService(session) {
-                            @SuppressWarnings("synthetic-access")
-                            @Override
-                            public void process(byte cmd, Buffer buffer) throws Exception {
-                                authLatch.await();
-                                super.process(cmd, buffer);
-                            }
-                        };
-                    }
-                },
-                new ServerConnectionService.Factory()
-        ));
-        sshd.setChannelFactories(Arrays.<NamedFactory<Channel>>asList(
-                new ChannelSession.ChannelSessionFactory() {
-                    @Override
-                    public Channel create() {
-                        return new ChannelSession() {
-                            @SuppressWarnings("synthetic-access")
-                            @Override
-                            public OpenFuture open(int recipient, int rwsize, int rmpsize, Buffer buffer) {
-                                try {
-                                    channelLatch.await();
-                                } catch (InterruptedException e) {
-                                    throw new RuntimeSshException(e);
-                                }
-                                return super.open(recipient, rwsize, rmpsize, buffer);
-                            }
-
-                            @Override
-                            public String toString() {
-                                return "ChannelSession" + "[id=" + id + ", recipient=" + recipient + "]";
-                            }
-                        };
-                    }
-                },
-                TcpipServerChannel.DirectTcpipFactory.INSTANCE));
-        sshd.start();
-        port = sshd.getPort();
-
-        client = SshClient.setUpDefaultClient();
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        if (sshd != null) {
-            sshd.stop(true);
-        }
-        if (client != null) {
-            client.stop();
-        }
-    }
-
-    @Test
-    public void testAsyncClient() throws Exception {
-        FactoryManagerUtils.updateProperty(sshd, FactoryManager.WINDOW_SIZE, 1024);
-        sshd.setShellFactory(new AsyncEchoShellFactory());
-
-        FactoryManagerUtils.updateProperty(client, FactoryManager.WINDOW_SIZE, 1024);
-        client.start();
-
-        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
-            session.addPasswordIdentity(getCurrentTestName());
-            session.auth().verify(5L, TimeUnit.SECONDS);
-
-            try(final ChannelShell channel = session.createShellChannel()) {
-                channel.setStreaming(ClientChannel.Streaming.Async);
-                channel.open().verify(5L, TimeUnit.SECONDS);
-        
-                final byte[] message = "0123456789\n".getBytes();
-                final int nbMessages = 1000;
-        
-                try(final ByteArrayOutputStream baosOut = new ByteArrayOutputStream();
-                    final ByteArrayOutputStream baosErr = new ByteArrayOutputStream()) {
-                    final AtomicInteger writes = new AtomicInteger(nbMessages);
-            
-                    channel.getAsyncIn().write(new ByteArrayBuffer(message))
-                            .addListener(new SshFutureListener<IoWriteFuture>() {
-                                @Override
-                                public void operationComplete(IoWriteFuture future) {
-                                    try {
-                                        if (future.isWritten()) {
-                                            if (writes.decrementAndGet() > 0) {
-                                                channel.getAsyncIn().write(new ByteArrayBuffer(message)).addListener(this);
-                                            } else {
-                                                channel.getAsyncIn().close(false);
-                                            }
-                                        } else {
-                                            throw new SshException("Error writing", future.getException());
-                                        }
-                                    } catch (IOException e) {
-                                        if (!channel.isClosing()) {
-                                            e.printStackTrace();
-                                            channel.close(true);
-                                        }
-                                    }
-                                }
-                            });
-                    channel.getAsyncOut().read(new ByteArrayBuffer())
-                            .addListener(new SshFutureListener<IoReadFuture>() {
-                                @Override
-                                public void operationComplete(IoReadFuture future) {
-                                    try {
-                                        future.verify(5L, TimeUnit.SECONDS);
-                                        Buffer buffer = future.getBuffer();
-                                        baosOut.write(buffer.array(), buffer.rpos(), buffer.available());
-                                        buffer.rpos(buffer.rpos() + buffer.available());
-                                        buffer.compact();
-                                        channel.getAsyncOut().read(buffer).addListener(this);
-                                    } catch (IOException e) {
-                                        if (!channel.isClosing()) {
-                                            e.printStackTrace();
-                                            channel.close(true);
-                                        }
-                                    }
-                                }
-                            });
-                    channel.getAsyncErr().read(new ByteArrayBuffer())
-                            .addListener(new SshFutureListener<IoReadFuture>() {
-                                @Override
-                                public void operationComplete(IoReadFuture future) {
-                                    try {
-                                        future.verify(5L, TimeUnit.SECONDS);
-                                        Buffer buffer = future.getBuffer();
-                                        baosErr.write(buffer.array(), buffer.rpos(), buffer.available());
-                                        buffer.rpos(buffer.rpos() + buffer.available());
-                                        buffer.compact();
-                                        channel.getAsyncErr().read(buffer).addListener(this);
-                                    } catch (IOException e) {
-                                        if (!channel.isClosing()) {
-                                            e.printStackTrace();
-                                            channel.close(true);
-                                        }
-                                    }
-                                }
-                            });
-        
-                    channel.waitFor(ClientChannel.CLOSED, 0);
-        
-                    assertEquals(nbMessages * message.length, baosOut.size());
-                }
-            }    
-
-            client.close(true);
-        } finally {
-            client.stop();
-        }
-    }
-
-    @Test
-    public void testCommandDeadlock() throws Exception {
-        client.start();
-
-        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
-            session.addPasswordIdentity(getCurrentTestName());
-            session.auth().verify(5L, TimeUnit.SECONDS);
-            
-            try(ChannelExec channel = session.createExecChannel("test");
-                OutputStream stdout = new NoCloseOutputStream(System.out);
-                OutputStream stderr = new NoCloseOutputStream(System.err)) {
-
-                channel.setOut(stdout);
-                channel.setErr(stderr);
-                channel.open().await();
-                Thread.sleep(100);
-                try {
-                    for (int i = 0; i < 100; i++) {
-                        channel.getInvertedIn().write("a".getBytes());
-                        channel.getInvertedIn().flush();
-                    }
-                } catch (SshException e) {
-                    // That's ok, the channel is being closed by the other side
-                }
-                assertEquals(ClientChannel.CLOSED, channel.waitFor(ClientChannel.CLOSED, 0) & ClientChannel.CLOSED);
-                session.close(false).await();
-            }
-        } finally {
-            client.stop();
-        }
-    }
-
-    @Test
-    public void testClient() throws Exception {
-        client.start();
-
-        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
-            session.addPasswordIdentity(getCurrentTestName());
-            session.auth().verify(5L, TimeUnit.SECONDS);
-            
-            try(ClientChannel channel = session.createChannel(ClientChannel.CHANNEL_SHELL);
-                ByteArrayOutputStream sent = new ByteArrayOutputStream();
-                PipedOutputStream pipedIn = new PipedOutputStream();
-                PipedInputStream pipedOut = new PipedInputStream(pipedIn)) {
-
-                channel.setIn(pipedOut);
-
-                try(OutputStream teeOut = new TeeOutputStream(sent, pipedIn);
-                    ByteArrayOutputStream out = new ByteArrayOutputStream();
-                    ByteArrayOutputStream err = new ByteArrayOutputStream()) {
-
-                    channel.setOut(out);
-                    channel.setErr(err);
-                    channel.open();
-            
-                    teeOut.write("this is my command\n".getBytes());
-                    teeOut.flush();
-            
-                    StringBuilder sb = new StringBuilder();
-                    for (int i = 0; i < 1000; i++) {
-                        sb.append("0123456789");
-                    }
-                    sb.append("\n");
-                    teeOut.write(sb.toString().getBytes());
-            
-                    teeOut.write("exit\n".getBytes());
-                    teeOut.flush();
-            
-                    channel.waitFor(ClientChannel.CLOSED, 0);
-            
-                    channel.close(false);
-                    client.stop();
-            
-                    assertArrayEquals(sent.toByteArray(), out.toByteArray());
-                }
-            }
-        } finally {
-            client.stop();
-        }
-    }
-
-    @Test
-    public void testClientInverted() throws Exception {
-        client.start();
-        
-        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
-            session.addPasswordIdentity(getCurrentTestName());
-            session.auth().verify(5L, TimeUnit.SECONDS);
-            
-            try(ClientChannel channel = session.createChannel(ClientChannel.CHANNEL_SHELL);
-                ByteArrayOutputStream sent = new ByteArrayOutputStream();
-                ByteArrayOutputStream out = new ByteArrayOutputStream();
-                ByteArrayOutputStream err = new ByteArrayOutputStream()) {
-
-                channel.setOut(out);
-                channel.setErr(err);
-                channel.open().await();
-        
-                try(OutputStream pipedIn = new TeeOutputStream(sent, channel.getInvertedIn())) {
-                    pipedIn.write("this is my command\n".getBytes());
-                    pipedIn.flush();
-            
-                    StringBuilder sb = new StringBuilder();
-                    for (int i = 0; i < 1000; i++) {
-                        sb.append("0123456789");
-                    }
-                    sb.append("\n");
-                    pipedIn.write(sb.toString().getBytes());
-            
-                    pipedIn.write("exit\n".getBytes());
-                    pipedIn.flush();
-                }
-        
-                channel.waitFor(ClientChannel.CLOSED, 0);
-        
-                channel.close(false);
-                client.stop();
-        
-                assertArrayEquals(sent.toByteArray(), out.toByteArray());
-            }
-        } finally {
-            client.stop();
-        }
-    }
-
-    @Test
-    public void testClientWithCustomChannel() throws Exception {
-        client.start();
-        
-        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
-            session.addPasswordIdentity(getCurrentTestName());
-            session.auth().verify(5L, TimeUnit.SECONDS);
-    
-            try(ChannelShell channel = new ChannelShell();
-                ByteArrayOutputStream sent = new ByteArrayOutputStream();
-                ByteArrayOutputStream out = new ByteArrayOutputStream();
-                ByteArrayOutputStream err = new ByteArrayOutputStream()) {
-
-                session.getService(ConnectionService.class).registerChannel(channel);
-                channel.setOut(out);
-                channel.setErr(err);
-                channel.open().verify(5L, TimeUnit.SECONDS);
-                channel.close(false).await();
-            }
-        } finally {
-            client.stop();
-        }
-    }
-
-    @Test
-    public void testClientClosingStream() throws Exception {
-        client.start();
-        
-        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
-            session.addPasswordIdentity(getCurrentTestName());
-            session.auth().verify(5L, TimeUnit.SECONDS);
-    
-            try(ClientChannel channel = session.createChannel(ClientChannel.CHANNEL_SHELL);
-                ByteArrayOutputStream sent = new ByteArrayOutputStream();
-                PipedOutputStream pipedIn = new PipedOutputStream();
-                InputStream inPipe = new PipedInputStream(pipedIn);
-                ByteArrayOutputStream out = new ByteArrayOutputStream();
-                ByteArrayOutputStream err = new ByteArrayOutputStream()) {
-
-                channel.setIn(inPipe);
-                channel.setOut(out);
-                channel.setErr(err);
-                channel.open();
-
-                try(OutputStream teeOut = new TeeOutputStream(sent, pipedIn)) {
-                    teeOut.write("this is my command\n".getBytes());
-                    teeOut.flush();
-        
-                    StringBuilder sb = new StringBuilder();
-                    for (int i = 0; i < 1000; i++) {
-                        sb.append("0123456789");
-                    }
-                    sb.append("\n");
-                    teeOut.write(sb.toString().getBytes());
-                }    
-    
-                channel.waitFor(ClientChannel.CLOSED, 0);
-        
-                channel.close(false);
-                client.stop();
-        
-                assertArrayEquals(sent.toByteArray(), out.toByteArray());
-            }
-        } finally {
-            client.stop();
-        }
-    }
-
-    @Test
-    public void testClientWithLengthyDialog() throws Exception {
-        // Reduce window size and packet size
-//        FactoryManagerUtils.updateProperty(client, SshClient.WINDOW_SIZE, 0x20000);
-//        FactoryManagerUtils.updateProperty(client, SshClient.MAX_PACKET_SIZE, 0x1000);
-//        FactoryManagerUtils.updateProperty(sshd, SshServer.WINDOW_SIZE, 0x20000);
-//        FactoryManagerUtils.updateProperty(sshd, SshServer.MAX_PACKET_SIZE, 0x1000);
-        client.start();
-        
-        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
-            session.addPasswordIdentity(getCurrentTestName());
-            session.auth().verify(5L, TimeUnit.SECONDS);
-
-            try(ClientChannel channel = session.createChannel(ClientChannel.CHANNEL_SHELL);
-                ByteArrayOutputStream sent = new ByteArrayOutputStream();
-                PipedOutputStream pipedIn = new PipedOutputStream();
-                InputStream inPipe = new PipedInputStream(pipedIn); 
-                ByteArrayOutputStream out = new ByteArrayOutputStream();
-                ByteArrayOutputStream err = new ByteArrayOutputStream()) {
-
-                channel.setIn(inPipe);
-                channel.setOut(out);
-                channel.setErr(err);
-                channel.open().await();
-        
-        
-                int bytes = 0;
-                byte[] data = "01234567890123456789012345678901234567890123456789\n".getBytes();
-                long t0 = System.currentTimeMillis();
-                try(OutputStream teeOut = new TeeOutputStream(sent, pipedIn)) {
-                    for (int i = 0; i < 10000; i++) {
-                        teeOut.write(data);
-                        teeOut.flush();
-                        bytes += data.length;
-                        if ((bytes & 0xFFF00000) != ((bytes - data.length) & 0xFFF00000)) {
-                            System.out.println("Bytes written: " + bytes);
-                        }
-                    }
-                    teeOut.write("exit\n".getBytes());
-                    teeOut.flush();
-                }        
-                long t1 = System.currentTimeMillis();
-        
-                System.out.println("Sent " + (bytes / 1024) + " Kb in " + (t1 - t0) + " ms");
-        
-                System.out.println("Waiting for channel to be closed");
-        
-                channel.waitFor(ClientChannel.CLOSED, 0);
-        
-                channel.close(false);
-                client.stop();
-        
-                assertArrayEquals(sent.toByteArray(), out.toByteArray());
-                //assertArrayEquals(sent.toByteArray(), out.toByteArray());
-            }
-        } finally {
-            client.stop();
-        }
-    }
-
-    @Test(expected = SshException.class)
-    public void testOpenChannelOnClosedSession() throws Exception {
-        client.start();
-        
-        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
-            session.addPasswordIdentity(getCurrentTestName());
-            session.auth().verify(5L, TimeUnit.SECONDS);
-            
-            try(ClientChannel channel = session.createChannel(ClientChannel.CHANNEL_SHELL)) {
-                session.close(false);
-        
-                try(PipedOutputStream pipedIn = new PipedOutputStream();
-                    InputStream inPipe = new PipedInputStream(pipedIn);
-                    ByteArrayOutputStream out = new ByteArrayOutputStream();
-                    ByteArrayOutputStream err = new ByteArrayOutputStream()) {
-
-                    channel.setIn(inPipe);
-                    channel.setOut(out);
-                    channel.setErr(err);
-                    channel.open();
-                }
-            }
-        }
-    }
-
-    @Test
-    public void testCloseBeforeAuthSucceed() throws Exception {
-        authLatch = new CountDownLatch(1);
-        client.start();
-        
-        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
-            session.addPasswordIdentity(getCurrentTestName());
-
-            AuthFuture authFuture = session.auth();
-            CloseFuture closeFuture = session.close(false);
-            authLatch.countDown();
-            authFuture.await();
-            closeFuture.await();
-            assertNotNull("No authentication exception", authFuture.getException());
-            assertTrue("Future not closed", closeFuture.isClosed());
-        } finally {
-            client.stop();
-        }
-    }
-
-    @Test
-    public void testCloseCleanBeforeChannelOpened() throws Exception {
-        client.start();
-        
-        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
-            session.addPasswordIdentity(getCurrentTestName());
-            session.auth().verify(5L, TimeUnit.SECONDS);
-
-            try(ClientChannel channel = session.createChannel(ClientChannel.CHANNEL_SHELL);
-                InputStream inp = new ByteArrayInputStream(GenericUtils.EMPTY_BYTE_ARRAY);
-                OutputStream out = new ByteArrayOutputStream();
-                OutputStream err = new ByteArrayOutputStream()) { 
-
-                channel.setIn(inp);
-                channel.setOut(out);
-                channel.setErr(err);
-
-                OpenFuture openFuture = channel.open();
-                CloseFuture closeFuture = session.close(false);
-                openFuture.await();
-                closeFuture.await();
-                assertTrue("Not open", openFuture.isOpened());
-                assertTrue("Not closed", closeFuture.isClosed());
-            }
-        } finally {
-            client.stop();
-        }
-    }
-
-    @Test
-    public void testCloseImmediateBeforeChannelOpened() throws Exception {
-        channelLatch = new CountDownLatch(1);
-        client.start();
-
-        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
-            session.addPasswordIdentity(getCurrentTestName());
-            session.auth().verify(5L, TimeUnit.SECONDS);
-
-            try(ClientChannel channel = session.createChannel(ClientChannel.CHANNEL_SHELL);
-                InputStream inp = new ByteArrayInputStream(GenericUtils.EMPTY_BYTE_ARRAY);
-                OutputStream out = new ByteArrayOutputStream();
-                OutputStream err = new ByteArrayOutputStream()) { 
-
-                channel.setIn(inp);
-                channel.setOut(out);
-                channel.setErr(err);
-
-                OpenFuture openFuture = channel.open();
-                CloseFuture closeFuture = session.close(true);
-                channelLatch.countDown();
-                openFuture.await();
-                closeFuture.await();
-                assertNotNull("No open exception", openFuture.getException());
-                assertTrue("Not closed", closeFuture.isClosed());
-            }
-        } finally {
-            client.stop();
-        }
-    }
-
-    @Test
-    public void testPublicKeyAuth() throws Exception {
-        client.start();
-        
-        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
-            KeyPair pair = Utils.createTestHostKeyProvider().loadKey(KeyPairProvider.SSH_RSA);
-            session.addPublicKeyIdentity(pair);
-            session.auth().verify(5L, TimeUnit.SECONDS);
-        } finally {
-            client.stop();
-        }
-    }
-
-    @Test
-    public void testPublicKeyAuthNew() throws Exception {
-        client.setUserAuthFactories(Arrays.<NamedFactory<UserAuth>>asList(UserAuthPublicKey.UserAuthPublicKeyFactory.INSTANCE));
-        client.start();
-        
-        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
-            session.addPublicKeyIdentity(Utils.createTestHostKeyProvider().loadKey(KeyPairProvider.SSH_RSA));
-            session.auth().verify(5L, TimeUnit.SECONDS);
-        } finally {
-            client.stop();
-        }
-    }
-
-    @Test
-    public void testPublicKeyAuthNewWithFailureOnFirstIdentity() throws Exception {
-        final KeyPair pair = Utils.createTestHostKeyProvider().loadKey(KeyPairProvider.SSH_RSA);
-        sshd.setPublickeyAuthenticator(new PublickeyAuthenticator() {
-            @Override
-            public boolean authenticate(String username, PublicKey key, ServerSession session) {
-                return key.equals(pair.getPublic());
-            }
-        });
-        client.setUserAuthFactories(Arrays.<NamedFactory<UserAuth>>asList(UserAuthPublicKey.UserAuthPublicKeyFactory.INSTANCE));
-        client.start();
-
-        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
-            session.addPublicKeyIdentity(new SimpleGeneratorHostKeyProvider(null, "RSA").loadKey(KeyPairProvider.SSH_RSA));
-            session.addPublicKeyIdentity(pair);
-            session.auth().verify(5L, TimeUnit.SECONDS);
-        } finally {
-            client.stop();
-        }
-    }
-
-    @Test
-    public void testPasswordAuthNew() throws Exception {
-        client.setUserAuthFactories(Arrays.<NamedFactory<UserAuth>>asList(new UserAuthPassword.UserAuthPasswordFactory()));
-        client.start();
-        
-        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
-            session.addPasswordIdentity(getCurrentTestName());
-            session.auth().verify(5L, TimeUnit.SECONDS);
-        } finally {
-            client.stop();
-        }
-    }
-
-    @Test
-    public void testPasswordAuthNewWithFailureOnFirstIdentity() throws Exception {
-        client.setUserAuthFactories(Arrays.<NamedFactory<UserAuth>>asList(new UserAuthPassword.UserAuthPasswordFactory()));
-        client.start();
-        
-        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
-            session.addPasswordIdentity(getClass().getSimpleName());
-            session.addPasswordIdentity(getCurrentTestName());
-            session.auth().verify(5L, TimeUnit.SECONDS);
-        } finally {
-            client.stop();
-        }
-    }
-
-    @Test
-    public void testKeyboardInteractiveAuthNew() throws Exception {
-        client.setUserAuthFactories(Arrays.<NamedFactory<UserAuth>>asList(UserAuthKeyboardInteractive.UserAuthKeyboardInteractiveFactory.INSTANCE));
-        client.start();
-        
-        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
-            session.addPasswordIdentity(getCurrentTestName());
-            session.auth().verify(5L, TimeUnit.SECONDS);
-        } finally {
-            client.stop();
-        }
-    }
-
-    @Test
-    public void testKeyboardInteractiveAuthNewWithFailureOnFirstIdentity() throws Exception {
-        client.setUserAuthFactories(Arrays.<NamedFactory<UserAuth>>asList(UserAuthKeyboardInteractive.UserAuthKeyboardInteractiveFactory.INSTANCE));
-        client.start();
-        
-        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
-            session.addPasswordIdentity(getClass().getSimpleName());
-            session.addPasswordIdentity(getCurrentTestName());
-            session.auth().verify(5L, TimeUnit.SECONDS);
-        } finally {
-            client.stop();
-        }
-    }
-
-    @Test
-    public void testKeyboardInteractiveWithFailures() throws Exception {
-        final AtomicInteger count = new AtomicInteger();
-        final int MAX_PROMPTS = 3;
-        FactoryManagerUtils.updateProperty(client, ClientFactoryManager.PASSWORD_PROMPTS, MAX_PROMPTS);
-
-        client.setUserAuthFactories(Arrays.<NamedFactory<UserAuth>>asList(new UserAuthKeyboardInteractive.UserAuthKeyboardInteractiveFactory()));
-        client.setUserInteraction(new UserInteraction() {
-            @Override
-            public void welcome(String banner) {
-                // ignored
-            }
-            @Override
-            public String[] interactive(String destination, String name, String instruction, String[] prompt, boolean[] echo) {
-                count.incrementAndGet();
-                return new String[] { "bad" };
-            }
-        });
-        client.start();
-        
-        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
-            AuthFuture future = session.auth();
-            future.await();
-            assertTrue("Unexpected authentication success", future.isFailure());
-            assertEquals("Mismatched authentication retry count", MAX_PROMPTS, count.get());
-        } finally {
-            client.stop();
-        }
-    }
-
-    @Test
-    public void testKeyboardInteractiveInSessionUserInteractive() throws Exception {
-        final AtomicInteger count = new AtomicInteger();
-        final int MAX_PROMPTS = 3;
-        FactoryManagerUtils.updateProperty(client, ClientFactoryManager.PASSWORD_PROMPTS, MAX_PROMPTS);
-
-        client.setUserAuthFactories(Arrays
-                        .<NamedFactory<UserAuth>> asList(UserAuthKeyboardInteractive.UserAuthKeyboardInteractiveFactory.INSTANCE));
-        client.start();
-
-        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
-            session.setUserInteraction(new UserInteraction() {
-                    @Override
-                    public void welcome(String banner) {
-                        // ignored
-                    }
-        
-                    @Override
-                    public String[] interactive(String destination, String name, String instruction,
-                                                String[] prompt, boolean[] echo) {
-                        count.incrementAndGet();
-                        return new String[] { getCurrentTestName() };
-                    }
-                });
-            AuthFuture future = session.auth();
-            future.await();
-            assertTrue("Authentication not marked as success", future.isSuccess());
-            assertFalse("Authentication marked as failure", future.isFailure());
-            assertEquals("Mismatched authentication attempts count", 1, count.get());
-        } finally {
-            client.stop();
-        }
-    }
-
-    @Test
-    public void testKeyboardInteractiveInSessionUserInteractiveFailure() throws Exception {
-        final AtomicInteger count = new AtomicInteger();
-        final int MAX_PROMPTS = 3;
-        FactoryManagerUtils.updateProperty(client, ClientFactoryManager.PASSWORD_PROMPTS, MAX_PROMPTS);
-        client.setUserAuthFactories(Arrays
-                        .<NamedFactory<UserAuth>> asList(new UserAuthKeyboardInteractive.UserAuthKeyboardInteractiveFactory()));
-        client.start();
-        
-        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
-            session.setUserInteraction(new UserInteraction() {
-                @Override
-                public void welcome(String banner) {
-                    // ignored
-                }
-    
-                @Override
-                public String[] interactive(String destination, String name, String instruction,
-                                            String[] prompt, boolean[] echo) {
-                    count.incrementAndGet();
-                    return new String[] { "bad" };
-                }
-            });
-            AuthFuture future = session.auth();
-            future.await();
-            assertTrue("Authentication not, marked as failure", future.isFailure());
-            assertEquals("Mismatched authentication retry count", MAX_PROMPTS, count.get());
-        } finally {
-            client.stop();
-        }
-    }
-
-    @Test
-    public void testClientDisconnect() throws Exception {
-        TestEchoShellFactory.TestEchoShell.latch = new CountDownLatch(1);
-        try {
-            client.start();
-            
-            try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
-                session.addPasswordIdentity(getCurrentTestName());
-                session.auth().verify(5L, TimeUnit.SECONDS);
-                
-                try(ClientChannel channel = session.createChannel(ClientChannel.CHANNEL_SHELL);
-                    PipedOutputStream pipedIn = new PipedOutputStream();
-                    InputStream inPipe = new PipedInputStream(pipedIn); 
-                    ByteArrayOutputStream out = new ByteArrayOutputStream();
-                    ByteArrayOutputStream err = new ByteArrayOutputStream()) {
-
-                    channel.setIn(inPipe);
-                    channel.setOut(out);
-                    channel.setErr(err);
-                    channel.open().await();
-        
-        //            ((AbstractSession) session).disconnect(SshConstants.SSH2_DISCONNECT_BY_APPLICATION, "Cancel");
-                    AbstractSession cs = (AbstractSession) session;
-                    Buffer buffer = cs.createBuffer(SshConstants.SSH_MSG_DISCONNECT);
-                    buffer.putInt(SshConstants.SSH2_DISCONNECT_BY_APPLICATION);
-                    buffer.putString("Cancel");
-                    buffer.putString("");
-                    IoWriteFuture f = cs.writePacket(buffer);
-                    f.await();
-                    suspend(cs.getIoSession());
-    
-                    TestEchoShellFactory.TestEchoShell.latch.await();
-                }
-            } finally {
-                client.stop();
-            }
-        } finally {
-            TestEchoShellFactory.TestEchoShell.latch = null;
-        }
-    }
-
-    @Test
-    public void testWaitAuth() throws Exception {
-        final AtomicBoolean ok = new AtomicBoolean();
-        client.setServerKeyVerifier(
-                new ServerKeyVerifier() {
-                    @Override
-                    public boolean verifyServerKey(
-                            ClientSession sshClientSession,
-                            SocketAddress remoteAddress,
-                            PublicKey serverKey
-                    ) {
-                        System.out.println(serverKey);
-                        ok.set(true);
-                        return true;
-                    }
-                }
-        );
-        client.start();
-        
-        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
-            session.waitFor(ClientSession.WAIT_AUTH, TimeUnit.SECONDS.toMillis(10L));
-            assertTrue(ok.get());
-        } finally {
-            client.stop();
-        }
-    }
-
-    @Test
-    public void testSwitchToNoneCipher() throws Exception {
-        sshd.getCipherFactories().add(BuiltinCiphers.none);
-        client.getCipherFactories().add(BuiltinCiphers.none);
-        client.start();
-        
-        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
-            session.addPasswordIdentity(getCurrentTestName());
-            session.auth().verify(5L, TimeUnit.SECONDS);
-            session.switchToNoneCipher().await();
-    
-            try(ClientChannel channel = session.createSubsystemChannel(SftpConstants.SFTP_SUBSYSTEM_NAME)) {
-                channel.open().verify(5L, TimeUnit.SECONDS);
-            }
-        } finally {
-            client.stop();
-        }
-    }
-
-    private void suspend(IoSession ioSession) {
-        if (ioSession instanceof MinaSession) {
-            ((MinaSession) ioSession).suspend();
-        } else {
-            ((Nio2Session) ioSession).suspend();
-        }
-    }
-
-    public static class TestEchoShellFactory extends EchoShellFactory {
-        @Override
-        public Command create() {
-            return new TestEchoShell();
-        }
-        public static class TestEchoShell extends EchoShell {
-
-            public static CountDownLatch latch = new CountDownLatch(1);
-
-            @Override
-            public void destroy() {
-                if (latch != null) {
-                    latch.countDown();
-                }
-                super.destroy();
-            }
-        }
-    }
-
-    public static void main(String[] args) throws Exception {
-        SshClient.main(args);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/test/java/org/apache/sshd/MacTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/MacTest.java b/sshd-core/src/test/java/org/apache/sshd/MacTest.java
deleted file mode 100644
index 6f3ea60..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/MacTest.java
+++ /dev/null
@@ -1,188 +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;
-
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.Arrays;
-
-import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.cipher.BuiltinCiphers;
-import org.apache.sshd.common.cipher.Cipher;
-import org.apache.sshd.common.mac.BuiltinMacs;
-import org.apache.sshd.common.mac.Mac;
-import org.apache.sshd.common.random.BouncyCastleRandom;
-import org.apache.sshd.common.random.Random;
-import org.apache.sshd.server.SshServer;
-import org.apache.sshd.util.BaseTestSupport;
-import org.apache.sshd.util.BogusPasswordAuthenticator;
-import org.apache.sshd.util.EchoShellFactory;
-import org.apache.sshd.util.JSchLogger;
-import org.apache.sshd.util.SimpleUserInfo;
-import org.apache.sshd.util.Utils;
-import org.junit.After;
-import org.junit.FixMethodOrder;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runners.MethodSorters;
-
-import com.jcraft.jsch.JSch;
-
-/**
- * Test Cipher algorithms.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-public class MacTest extends BaseTestSupport {
-
-    private SshServer sshd;
-    private int port;
-
-    @Test
-    public void testHMACMD5() throws Exception {
-        setUp(BuiltinMacs.hmacmd5);
-        runTest();
-    }
-
-    @Test
-    public void testHMACMD596() throws Exception {
-        setUp(BuiltinMacs.hmacmd596);
-        runTest();
-    }
-
-    @Test
-    public void testHMACSHA1() throws Exception {
-        setUp(BuiltinMacs.hmacsha1);
-        runTest();
-    }
-
-    @Test
-    public void testHMACSHA196() throws Exception {
-        setUp(BuiltinMacs.hmacsha196);
-        runTest();
-    }
-
-    @Test
-    public void testHMACSHA256() throws Exception {
-        setUp(BuiltinMacs.hmacsha256);
-        runTest();
-    }
-
-    @Test
-    @Ignore("Lead to ArrayIndexOutOfBoundsException in JSch")
-    public void testHMACSHA512() throws Exception {
-        setUp(BuiltinMacs.hmacsha512);
-        runTest();
-    }
-
-    @Test
-    public void loadTest() throws Exception {
-        Random random = new BouncyCastleRandom();
-        loadTest(BuiltinCiphers.aes128cbc, random);
-        loadTest(BuiltinCiphers.blowfishcbc, random);
-        loadTest(BuiltinCiphers.tripledescbc, random);
-    }
-
-    protected void loadTest(NamedFactory<Cipher> factory, Random random) throws Exception {
-        Cipher cipher = factory.create();
-        byte[] key = new byte[cipher.getBlockSize()];
-        byte[] iv = new byte[cipher.getIVSize()];
-        random.fill(key, 0, key.length);
-        random.fill(iv, 0, iv.length);
-        cipher.init(Cipher.Mode.Encrypt, key, iv);
-
-        byte[] input = new byte[cipher.getBlockSize()];
-        random.fill(input, 0, input.length);
-        long t0 = System.currentTimeMillis();
-        for (int i = 0; i < 100000; i++) {
-            cipher.update(input, 0, input.length);
-        }
-        long t1 = System.currentTimeMillis();
-        System.err.println(factory.getName() + ": " + (t1 - t0) + " ms");
-    }
-
-
-    protected void setUp(NamedFactory<Mac> mac) throws Exception {
-        sshd = SshServer.setUpDefaultServer();
-        sshd.setKeyPairProvider(Utils.createTestHostKeyProvider());
-        sshd.setMacFactories(Arrays.<NamedFactory<Mac>>asList(mac));
-        sshd.setShellFactory(new EchoShellFactory());
-        sshd.setPasswordAuthenticator(BogusPasswordAuthenticator.INSTANCE);
-        sshd.start();
-        port  = sshd.getPort();
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        if (sshd != null) {
-            sshd.stop(true);
-        }
-    }
-
-    protected void runTest() throws Exception {
-        JSchLogger.init();
-        JSch sch = new JSch();
-        JSch.setConfig("cipher.s2c", "aes128-cbc,3des-cbc,blowfish-cbc,aes192-cbc,aes256-cbc,none");
-        JSch.setConfig("cipher.c2s", "aes128-cbc,3des-cbc,blowfish-cbc,aes192-cbc,aes256-cbc,none");
-        JSch.setConfig("mac.s2c", "hmac-md5,hmac-sha1,hmac-sha2-256,hmac-sha1-96,hmac-md5-96,hmac-sha2-512");
-        JSch.setConfig("mac.c2s", "hmac-md5,hmac-sha1,hmac-sha2-256,hmac-sha1-96,hmac-md5-96,hmac-sha2-512");
-        JSch.setConfig("hmac-sha2-512",  "com.jcraft.jsch.jce.HMACSHA512");
-        com.jcraft.jsch.Session s = sch.getSession(getCurrentTestName(), "localhost", port);
-        try {
-            s.setUserInfo(new SimpleUserInfo(getCurrentTestName()));
-            s.connect();
-            com.jcraft.jsch.Channel c = s.openChannel("shell");
-            c.connect();
-
-            try(OutputStream os = c.getOutputStream();
-                InputStream is = c.getInputStream()) {
-
-                String  expected = "this is my command\n";
-                byte[] bytes = expected.getBytes();
-                byte[] data = new byte[bytes.length + Long.SIZE];
-                for (int i = 0; i < 10; i++) {
-                    os.write(bytes);
-                    os.flush();
-                    int len = is.read(data);
-                    String str = new String(data, 0, len);
-                    assertEquals("Mismatched data at iteration " + i, expected, str);
-                }
-            } finally {
-                c.disconnect();
-            }
-        } finally {
-            s.disconnect();
-        }
-    }
-
-    static boolean checkCipher(String cipher){
-        try{
-            Class<?> c=Class.forName(cipher);
-            com.jcraft.jsch.Cipher _c = (com.jcraft.jsch.Cipher)(c.newInstance());
-            _c.init(com.jcraft.jsch.Cipher.ENCRYPT_MODE,
-                    new byte[_c.getBlockSize()],
-                    new byte[_c.getIVSize()]);
-            return true;
-        }
-        catch(Exception e){
-            return false;
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/test/java/org/apache/sshd/RandomTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/RandomTest.java b/sshd-core/src/test/java/org/apache/sshd/RandomTest.java
deleted file mode 100644
index f35a265..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/RandomTest.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;
-
-import org.apache.sshd.common.random.BouncyCastleRandom;
-import org.apache.sshd.common.random.JceRandom;
-import org.apache.sshd.common.random.Random;
-import org.apache.sshd.util.BaseTestSupport;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.runners.MethodSorters;
-
-/**
- * TODO Add javadoc
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-public class RandomTest extends BaseTestSupport {
-
-    @Test
-    public void testJce() {
-        long t = test(new JceRandom());
-        System.out.println("JCE: " + t + " micro");
-    }
-
-    @Test
-    public void testBc() {
-        long t = test(new BouncyCastleRandom());
-        System.out.println("BC:  " + t + " micro");
-    }
-
-    protected long test(Random random) {
-        byte[] bytes = new byte[32];
-        long l0 = System.nanoTime();
-        for (int i = 0; i < 1000; i++) {
-            random.fill(bytes, 8, 16);
-        }
-        long l1 = System.nanoTime();
-        return (l1 - l0) / 1000;
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/test/java/org/apache/sshd/ServerMain.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/ServerMain.java b/sshd-core/src/test/java/org/apache/sshd/ServerMain.java
deleted file mode 100644
index 2d9195f..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/ServerMain.java
+++ /dev/null
@@ -1,34 +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;
-
-import org.apache.sshd.server.SshServer;
-
-
-/**
- * An activator for the {@link SshServer#main(String[])} - the reason it is
- * here is because the logging configuration is available only for test scope
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class ServerMain {
-    public static void main(String[] args) throws Throwable {
-        SshServer.main(args);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/test/java/org/apache/sshd/ServerTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/ServerTest.java b/sshd-core/src/test/java/org/apache/sshd/ServerTest.java
deleted file mode 100644
index 160b033..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/ServerTest.java
+++ /dev/null
@@ -1,561 +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;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.PipedInputStream;
-import java.io.PipedOutputStream;
-import java.net.SocketAddress;
-import java.util.Arrays;
-import java.util.Map;
-import java.util.TreeMap;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import org.apache.sshd.client.SessionFactory;
-import org.apache.sshd.client.SshClient;
-import org.apache.sshd.client.channel.ChannelExec;
-import org.apache.sshd.client.channel.ChannelShell;
-import org.apache.sshd.client.future.AuthFuture;
-import org.apache.sshd.client.session.ClientConnectionService;
-import org.apache.sshd.client.session.ClientSession;
-import org.apache.sshd.client.session.ClientSessionImpl;
-import org.apache.sshd.common.FactoryManager;
-import org.apache.sshd.common.FactoryManagerUtils;
-import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.SshConstants;
-import org.apache.sshd.common.channel.Channel;
-import org.apache.sshd.common.channel.WindowClosedException;
-import org.apache.sshd.common.io.IoSession;
-import org.apache.sshd.common.session.AbstractConnectionService;
-import org.apache.sshd.common.session.AbstractSession;
-import org.apache.sshd.common.session.Session;
-import org.apache.sshd.common.session.SessionListener;
-import org.apache.sshd.deprecated.ClientUserAuthServiceOld;
-import org.apache.sshd.deprecated.UserAuthPassword;
-import org.apache.sshd.server.Command;
-import org.apache.sshd.server.CommandFactory;
-import org.apache.sshd.server.Environment;
-import org.apache.sshd.server.ExitCallback;
-import org.apache.sshd.server.ServerFactoryManager;
-import org.apache.sshd.server.SshServer;
-import org.apache.sshd.server.command.ScpCommandFactory;
-import org.apache.sshd.server.sftp.SftpSubsystemFactory;
-import org.apache.sshd.util.BaseTestSupport;
-import org.apache.sshd.util.BogusPasswordAuthenticator;
-import org.apache.sshd.util.EchoShellFactory;
-import org.apache.sshd.util.Utils;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.runners.MethodSorters;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * TODO Add javadoc
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-public class ServerTest extends BaseTestSupport {
-
-    private SshServer sshd;
-    private SshClient client;
-    private int port;
-
-    @Before
-    public void setUp() throws Exception {
-        sshd = SshServer.setUpDefaultServer();
-        sshd.setKeyPairProvider(Utils.createTestHostKeyProvider());
-        sshd.setShellFactory(new TestEchoShellFactory());
-        sshd.setPasswordAuthenticator(BogusPasswordAuthenticator.INSTANCE);
-        sshd.setSessionFactory(new org.apache.sshd.server.session.SessionFactory());
-        sshd.start();
-        port = sshd.getPort();
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        if (sshd != null) {
-            sshd.stop(true);
-        }
-        if (client != null) {
-            client.stop();
-        }
-    }
-
-    /**
-     * Send bad password.  The server should disconnect after a few attempts
-     * @throws Exception
-     */
-    @Test
-    public void testFailAuthenticationWithWaitFor() throws Exception {
-        final int   MAX_AUTH_REQUESTS=10;
-        FactoryManagerUtils.updateProperty(sshd, ServerFactoryManager.MAX_AUTH_REQUESTS, MAX_AUTH_REQUESTS);
-
-        client = SshClient.setUpDefaultClient();
-        client.setServiceFactories(Arrays.asList(
-                new ClientUserAuthServiceOld.Factory(),
-                new ClientConnectionService.Factory()
-        ));
-        client.start();
-        
-        try(ClientSession s = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
-            int nbTrials = 0;
-            int res = 0;
-            while ((res & ClientSession.CLOSED) == 0) {
-                nbTrials ++;
-                s.getService(ClientUserAuthServiceOld.class)
-                        .auth(new UserAuthPassword((ClientSessionImpl) s, "ssh-connection", "buggy"));
-                res = s.waitFor(ClientSession.CLOSED | ClientSession.WAIT_AUTH, 5000);
-                if (res == ClientSession.TIMEOUT) {
-                    throw new TimeoutException();
-                }
-            }
-            assertTrue("Number trials (" + nbTrials + ") below min.=" + MAX_AUTH_REQUESTS, nbTrials > MAX_AUTH_REQUESTS);
-        } finally {
-            client.stop();
-        }
-    }
-
-    @Test
-    public void testFailAuthenticationWithFuture() throws Exception {
-        final int   MAX_AUTH_REQUESTS=10;
-        FactoryManagerUtils.updateProperty(sshd, ServerFactoryManager.MAX_AUTH_REQUESTS, MAX_AUTH_REQUESTS);
-
-        client = SshClient.setUpDefaultClient();
-        client.setServiceFactories(Arrays.asList(
-                new ClientUserAuthServiceOld.Factory(),
-                new ClientConnectionService.Factory()
-        ));
-        client.start();
-        try(ClientSession s = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
-            int nbTrials = 0;
-            AuthFuture authFuture;
-            do {
-                nbTrials++;
-                assertTrue(nbTrials < 100);
-                authFuture = s.getService(ClientUserAuthServiceOld.class)
-                        .auth(new UserAuthPassword((ClientSessionImpl) s, "ssh-connection", "buggy"));
-                assertTrue("Authentication wait failed", authFuture.await(5000));
-                assertTrue("Authentication not done", authFuture.isDone());
-                assertFalse("Authentication unexpectedly successful", authFuture.isSuccess());
-            }
-            while (authFuture.isFailure());
-            assertNotNull("Missing auth future exception", authFuture.getException());
-            assertTrue("Number trials (" + nbTrials + ") below min.=" + MAX_AUTH_REQUESTS, nbTrials > MAX_AUTH_REQUESTS);
-        } finally {
-            client.stop();
-        }
-    }
-
-    @Test
-    public void testAuthenticationTimeout() throws Exception {
-        final int   AUTH_TIMEOUT=5000;
-        FactoryManagerUtils.updateProperty(sshd, FactoryManager.AUTH_TIMEOUT, AUTH_TIMEOUT);
-
-        client = SshClient.setUpDefaultClient();
-        client.start();
-        try(ClientSession s = client.connect("test", "localhost", port).await().getSession()) {
-            int res = s.waitFor(ClientSession.CLOSED, 2 * AUTH_TIMEOUT);
-            assertEquals("Session should be closed", ClientSession.CLOSED | ClientSession.WAIT_AUTH, res);
-        } finally {
-            client.stop();
-        }
-    }
-
-    @Test
-    public void testIdleTimeout() throws Exception {
-        final CountDownLatch latch = new CountDownLatch(1);
-        TestEchoShellFactory.TestEchoShell.latch = new CountDownLatch(1);
-        final int   IDLE_TIMEOUT=2500;
-        FactoryManagerUtils.updateProperty(sshd, FactoryManager.IDLE_TIMEOUT, IDLE_TIMEOUT);
-
-        sshd.getSessionFactory().addListener(new SessionListener() {
-            @Override
-            public void sessionCreated(Session session) {
-                System.out.println("Session created");
-            }
-            @Override
-            public void sessionEvent(Session session, Event event) {
-                System.out.println("Session event: " + event);
-            }
-            @Override
-            public void sessionClosed(Session session) {
-                System.out.println("Session closed");
-                latch.countDown();
-            }
-        });
-
-        client = SshClient.setUpDefaultClient();
-        client.start();
-        try(ClientSession s = client.connect("test", "localhost", port).await().getSession()) {
-            s.addPasswordIdentity("test");
-            s.auth().verify(5L, TimeUnit.SECONDS);
-
-            try(ChannelShell shell = s.createShellChannel();
-                ByteArrayOutputStream out = new ByteArrayOutputStream();
-                ByteArrayOutputStream err = new ByteArrayOutputStream()) {
-                shell.setOut(out);
-                shell.setErr(err);
-                shell.open().await();
-                int res = s.waitFor(ClientSession.CLOSED, 2 * IDLE_TIMEOUT);
-                assertEquals("Session should be closed", ClientSession.CLOSED | ClientSession.AUTHED, res);
-            }
-        } finally {
-            client.stop();
-        }
-
-        assertTrue(latch.await(1, TimeUnit.SECONDS));
-        assertTrue(TestEchoShellFactory.TestEchoShell.latch.await(1, TimeUnit.SECONDS));
-    }
-
-    /**
-     * The scenario is the following:
-     *  - create a command that sends continuous data to the client
-     *  - the client does not read the data, filling the ssh window and the tcp socket
-     *  - the server session becomes idle, but the ssh disconnect message can't be written
-     *  - the server session is forcibly closed
-     */
-    @Test
-    public void testServerIdleTimeoutWithForce() throws Exception {
-        final CountDownLatch latch = new CountDownLatch(1);
-
-        sshd.setCommandFactory(new StreamCommand.Factory());
-        
-        FactoryManagerUtils.updateProperty(sshd, FactoryManager.IDLE_TIMEOUT, 5000);
-        FactoryManagerUtils.updateProperty(sshd, FactoryManager.DISCONNECT_TIMEOUT, 2000);
-        sshd.getSessionFactory().addListener(new SessionListener() {
-            @Override
-            public void sessionCreated(Session session) {
-                System.out.println("Session created");
-            }
-
-            @Override
-            public void sessionEvent(Session session, Event event) {
-                System.out.println("Session event: " + event);
-            }
-
-            @Override
-            public void sessionClosed(Session session) {
-                System.out.println("Session closed");
-                latch.countDown();
-            }
-        });
-
-        client = SshClient.setUpDefaultClient();
-        client.start();
-
-        try(ClientSession s = client.connect("test", "localhost", port).await().getSession()) {
-            s.addPasswordIdentity("test");
-            s.auth().verify(5L, TimeUnit.SECONDS);
-
-            try(ChannelExec shell = s.createExecChannel("normal");
-                // Create a pipe that will block reading when the buffer is full
-                PipedInputStream pis = new PipedInputStream();
-                PipedOutputStream pos = new PipedOutputStream(pis)) {
-
-                shell.setOut(pos);
-                shell.open().verify(5L, TimeUnit.SECONDS);
-        
-                try(AbstractSession serverSession = sshd.getActiveSessions().iterator().next();
-                    Channel channel = serverSession.getService(AbstractConnectionService.class).getChannels().iterator().next()) {
-
-                    while (channel.getRemoteWindow().getSize() > 0) {
-                        Thread.sleep(1);
-                    }
-        
-                    LoggerFactory.getLogger(getClass()).info("Waiting for session idle timeouts");
-            
-                    long t0 = System.currentTimeMillis();
-                    latch.await(1, TimeUnit.MINUTES);
-                    long t1 = System.currentTimeMillis(), diff = t1 - t0;
-                    assertTrue("Wait time too low: " + diff, diff > 7000);
-                    assertTrue("Wait time too high: " + diff, diff < 10000);
-                }
-            }
-        } finally {
-            client.stop();
-        }
-    }
-
-    @Test
-    public void testLanguage() throws Exception {
-        client = SshClient.setUpDefaultClient();
-        client.setSessionFactory(new SessionFactory() {
-            @Override
-            protected AbstractSession createSession(IoSession ioSession) throws Exception {
-                return new ClientSessionImpl(client, ioSession) {
-                    @Override
-                    protected String[] createProposal(String hostKeyTypes) {
-                        String[] proposal = super.createProposal(hostKeyTypes);
-                        proposal[SshConstants.PROPOSAL_LANG_CTOS] = "en-US";
-                        proposal[SshConstants.PROPOSAL_LANG_STOC] = "en-US";
-                        return proposal;
-                    }
-                };
-            }
-        });
-        client.start();
-        try(ClientSession s = client.connect("test", "localhost", port).await().getSession()) {
-            s.close(false);
-        } finally {
-            client.stop();
-        }
-    }
-
-    @Test
-    public void testKexCompletedEvent() throws Exception {
-    	final AtomicInteger	serverEventCount=new AtomicInteger(0);
-        sshd.getSessionFactory().addListener(new SessionListener() {
-	            @Override
-                public void sessionCreated(Session session) {
-	            	// ignored
-	            }
-	
-	            @Override
-                public void sessionEvent(Session session, Event event) {
-	            	if (event == Event.KexCompleted) {
-	            		serverEventCount.incrementAndGet();
-	            	}
-	            }
-	
-	            @Override
-                public void sessionClosed(Session session) {
-	            	// ignored
-	            }
-	        });
-
-        client = SshClient.setUpDefaultClient();
-        client.start();
-    	final AtomicInteger	clientEventCount=new AtomicInteger(0);
-        client.getSessionFactory().addListener(new SessionListener() {
-	            @Override
-                public void sessionCreated(Session session) {
-	            	// ignored
-	            }
-	
-	            @Override
-                public void sessionEvent(Session session, Event event) {
-	            	if (event == Event.KexCompleted) {
-	            		clientEventCount.incrementAndGet();
-	            	}
-	            }
-	
-	            @Override
-                public void sessionClosed(Session session) {
-	            	// ignored
-	            }
-	        });
-
-        try(ClientSession s = client.connect("test", "localhost", port).await().getSession()) {
-            s.addPasswordIdentity("test");
-            s.auth().verify(5L, TimeUnit.SECONDS);
-            assertEquals("Mismatched client events count", 1, clientEventCount.get());
-            assertEquals("Mismatched server events count", 1, serverEventCount.get());
-            s.close(false);
-        } finally {
-            client.stop();
-        }
-    }
-
-    @Test   // see https://issues.apache.org/jira/browse/SSHD-456
-    public void testServerStillListensIfSessionListenerThrowsException() throws InterruptedException {
-        final Map<String,SocketAddress> eventsMap = new TreeMap<String, SocketAddress>(String.CASE_INSENSITIVE_ORDER);
-        sshd.getSessionFactory().addListener(new SessionListener() {
-            private final Logger log=LoggerFactory.getLogger(getClass());
-            @Override
-            public void sessionCreated(Session session) {
-                throwException("SessionCreated", session);
-            }
-
-            @Override
-            public void sessionEvent(Session session, Event event) {
-                throwException("SessionEvent", session);
-            }
-
-            @Override
-            public void sessionClosed(Session session) {
-                throwException("SessionClosed", session);
-            }
-            
-            private void throwException(String phase, Session session) {
-                IoSession       ioSession = session.getIoSession();
-                SocketAddress   addr = ioSession.getRemoteAddress();
-                synchronized (eventsMap) {
-                    if (eventsMap.put(phase, addr) != null) {
-                        return; // already generated an event for this phase
-                    }
-                }
-                
-                RuntimeException e = new RuntimeException("Synthetic exception at phase=" + phase + ": " + addr);
-                log.info(e.getMessage());
-                throw e;
-            }
-        });
-        
-        client = SshClient.setUpDefaultClient();
-        client.start();
-        
-        int curCount=0;
-        for (int retryCount=0; retryCount < Byte.SIZE; retryCount++){
-            synchronized(eventsMap) {
-                if ((curCount=eventsMap.size()) >= 3) {
-                    return;
-                }
-            }
-            
-            try {
-                try(ClientSession s = client.connect("test", "localhost", port).await().getSession()) {
-                    s.addPasswordIdentity("test");
-                    s.auth().verify(5L, TimeUnit.SECONDS);
-                }
-                
-                synchronized(eventsMap) {
-                    assertTrue("Unexpected premature success: " + eventsMap, eventsMap.size() >= 3);
-                }
-            } catch(IOException e) {
-                // expected - ignored
-                synchronized(eventsMap) {
-                    int nextCount=eventsMap.size();
-                    assertTrue("No session event generated", nextCount > curCount);
-                }
-            }
-        }
-        
-        fail("No success to authenticate");
-    }
-
-    public static class TestEchoShellFactory extends EchoShellFactory {
-        @Override
-        public Command create() {
-            return new TestEchoShell();
-        }
-        public static class TestEchoShell extends EchoShell {
-
-            public static CountDownLatch latch = new CountDownLatch(1);
-
-            @Override
-            public void destroy() {
-                if (latch != null) {
-                    latch.countDown();
-                }
-                super.destroy();
-            }
-        }
-    }
-
-    public static class StreamCommand implements Command, Runnable {
-
-        public static class Factory implements CommandFactory {
-            @Override
-            public Command createCommand(String name) {
-                return new StreamCommand(name);
-            }
-        }
-
-        public static CountDownLatch latch;
-
-        private final String name;
-        private OutputStream out;
-
-        public StreamCommand(String name) {
-            this.name = name;
-        }
-
-        @Override
-        public void setInputStream(InputStream in) {
-            // ignored
-        }
-
-        @Override
-        public void setOutputStream(OutputStream out) {
-            this.out = out;
-        }
-
-        @Override
-        public void setErrorStream(OutputStream err) {
-            // ignored
-        }
-
-        @Override
-        public void setExitCallback(ExitCallback callback) {
-            // ignored
-        }
-
-        @Override
-        public void start(Environment env) throws IOException {
-            new Thread(this).start();
-        }
-
-        @Override
-        public void destroy() {
-            synchronized (name) {
-                if ("block".equals(name)) {
-                    try {
-                        name.wait();
-                    } catch (InterruptedException e) {
-                        e.printStackTrace();
-                    }
-                }
-            }
-        }
-
-        @Override
-        public void run() {
-            try {
-                Thread.sleep(5000);
-                while (true) {
-                    for (int i = 0; i < 100; i++) {
-                        out.write("0123456789\n".getBytes());
-                    }
-                    out.flush();
-                }
-            } catch (WindowClosedException e) {
-                // ok, do nothing
-            } catch (Throwable e) {
-                e.printStackTrace();
-            } finally {
-                if (latch != null) {
-                    latch.countDown();
-                }
-            }
-        }
-    }
-
-    public static void main(String[] args) throws Exception {
-        SshServer sshd = SshServer.setUpDefaultServer();
-        FactoryManagerUtils.updateProperty(sshd, FactoryManager.IDLE_TIMEOUT, TimeUnit.SECONDS.toMillis(10L));
-        sshd.setPort(8001);
-        sshd.setKeyPairProvider(Utils.createTestHostKeyProvider());
-        sshd.setSubsystemFactories(Arrays.<NamedFactory<Command>>asList(new SftpSubsystemFactory()));
-        sshd.setShellFactory(new EchoShellFactory());
-        sshd.setCommandFactory(new ScpCommandFactory());
-        sshd.setPasswordAuthenticator(BogusPasswordAuthenticator.INSTANCE);
-        sshd.start();
-        Thread.sleep(100000);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/test/java/org/apache/sshd/SshBuilderTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/SshBuilderTest.java b/sshd-core/src/test/java/org/apache/sshd/SshBuilderTest.java
deleted file mode 100644
index 7f06812..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/SshBuilderTest.java
+++ /dev/null
@@ -1,136 +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;
-
-import java.util.Collection;
-import java.util.EnumSet;
-import java.util.List;
-import java.util.Set;
-
-import org.apache.sshd.common.BaseBuilder;
-import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.NamedResource;
-import org.apache.sshd.common.cipher.BuiltinCiphers;
-import org.apache.sshd.common.cipher.Cipher;
-import org.apache.sshd.common.kex.BuiltinDHFactories;
-import org.apache.sshd.common.mac.BuiltinMacs;
-import org.apache.sshd.common.signature.BuiltinSignatures;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.util.BaseTestSupport;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-public class SshBuilderTest extends BaseTestSupport {
-    public SshBuilderTest() {
-        super();
-    }
-
-    /**
-     * Make sure that all values in {@link BuiltinCiphers} are
-     * listed in {@link BaseBuilder#DEFAULT_CIPHERS_PREFERENCE}
-     */
-    @Test
-    public void testAllBuiltinCiphersListed() {
-        Set<BuiltinCiphers> all=EnumSet.allOf(BuiltinCiphers.class);
-        // The 'none' cipher is not listed as preferred - it is implied
-        assertTrue("Missing " + BuiltinCiphers.Constants.NONE + " cipher in all values", all.remove(BuiltinCiphers.none));
-        testAllInstancesListed(all, BaseBuilder.DEFAULT_CIPHERS_PREFERENCE);
-    }
-
-    /**
-     * Make sure that all values in {@link BuiltinMacs} are
-     * listed in {@link BaseBuilder#DEFAULT_MAC_PREFERENCE}
-     */
-    @Test
-    public void testAllBuiltinMacsListed() {
-        testAllInstancesListed(BuiltinMacs.VALUES, BaseBuilder.DEFAULT_MAC_PREFERENCE);
-    }
-
-    /**
-     * Make sure that all values in {@link BuiltinSignatures} are
-     * listed in {@link BaseBuilder#DEFAULT_SIGNATURE_PREFERENCE}
-     */
-    @Test
-    public void testAllBuiltinSignaturesListed() {
-        testAllInstancesListed(BuiltinSignatures.VALUES, BaseBuilder.DEFAULT_SIGNATURE_PREFERENCE);
-    }
-
-    /**
-     * Make sure that all values in {@link BuiltinDHFactories} are
-     * listed in {@link BaseBuilder#DEFAULT_KEX_PREFERENCE}
-     */
-    @Test
-    public void testAllBuiltinDHFactoriesListed() {
-        testAllInstancesListed(BuiltinDHFactories.VALUES, BaseBuilder.DEFAULT_KEX_PREFERENCE);
-    }
-
-    private static <E extends Enum<E>> void testAllInstancesListed(Set<? extends E> expValues, Collection<? extends E> actValues) {
-        assertEquals("Mismatched actual values size", expValues.size(), actValues.size());
-        for (E expected : expValues) {
-            assertTrue(expected.name() + " not found in actual values", actValues.contains(expected));
-        }
-    }
-
-    /**
-     * Make sure that {@link BaseBuilder#setUpDefaultCiphers(boolean)} returns
-     * the correct result - i.e., according to the {@code ingoreUnsupported}
-     * parameter and in the defined preference order
-     */
-    @Test
-    public void testSetUpDefaultCiphers() {
-        for (boolean ignoreUnsupported : new boolean[] { true, false }) {
-            List<NamedFactory<Cipher>>  ciphers=BaseBuilder.setUpDefaultCiphers(ignoreUnsupported);
-            int                         numCiphers=GenericUtils.size(ciphers);
-            // make sure returned list size matches expected count
-            if (ignoreUnsupported) {
-                assertEquals("Incomplete full ciphers size", BaseBuilder.DEFAULT_CIPHERS_PREFERENCE.size(), numCiphers);
-            } else {
-                int expectedCount=0;
-                for (BuiltinCiphers c : BaseBuilder.DEFAULT_CIPHERS_PREFERENCE) {
-                    if (c.isSupported()) {
-                        expectedCount++;
-                    }
-                }
-                assertEquals("Incomplete supported ciphers size", expectedCount, numCiphers);
-            }
-            
-            // make sure order is according to the default preference list
-            List<String>    cipherNames=NamedResource.Utils.getNameList(ciphers);
-            int             nameIndex=0;
-            for (BuiltinCiphers c : BaseBuilder.DEFAULT_CIPHERS_PREFERENCE) {
-                if ((!c.isSupported()) && (!ignoreUnsupported)) {
-                    continue;
-                }
-                
-                String  expectedName=c.getName();
-                assertTrue("Out of actual ciphers for expected=" + expectedName, nameIndex < numCiphers);
-                
-                String  actualName=cipherNames.get(nameIndex);
-                assertEquals("Mismatched cipher at position " + nameIndex + " for ignoreUnsupported=" + ignoreUnsupported, expectedName, actualName);
-                nameIndex++;
-            }
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/test/java/org/apache/sshd/SshServerMain.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/SshServerMain.java b/sshd-core/src/test/java/org/apache/sshd/SshServerMain.java
deleted file mode 100644
index 72d662f..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/SshServerMain.java
+++ /dev/null
@@ -1,35 +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;
-
-import org.apache.sshd.server.SshServer;
-
-/**
- * Just a test class used to invoke {@link SshServer#main(String[])} in
- * order to have logging - which is in {@code test} scope
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class SshServerMain {
-
-    public static void main(String[] args) throws Exception {
-        SshServer.main(args);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/test/java/org/apache/sshd/SshServerTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/SshServerTest.java b/sshd-core/src/test/java/org/apache/sshd/SshServerTest.java
deleted file mode 100644
index 674aba1..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/SshServerTest.java
+++ /dev/null
@@ -1,99 +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;
-
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-
-import org.apache.sshd.server.SshServer;
-import org.apache.sshd.util.BaseTestSupport;
-import org.apache.sshd.util.BogusPasswordAuthenticator;
-import org.apache.sshd.util.EchoShellFactory;
-import org.apache.sshd.util.Utils;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author Kohsuke Kawaguchi
- * @author Michael Heemskerk
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-public class SshServerTest extends BaseTestSupport {
-
-    @Test
-    public void stopMethodShouldBeIdempotent() throws Exception {
-        try(SshServer sshd = new SshServer()) {
-            sshd.stop();
-            sshd.stop();
-            sshd.stop();
-        }
-    }
-
-    @Test
-    public void testExecutorShutdownFalse() throws Exception {
-        ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
-
-        try(SshServer sshd = createTestServer()) {
-            sshd.setScheduledExecutorService(executorService);
-    
-            sshd.start();
-            sshd.stop();
-    
-            assertFalse(executorService.isShutdown());
-            executorService.shutdownNow();
-        }
-    }
-
-    @Test
-    public void testExecutorShutdownTrue() throws Exception {
-        ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
-
-        try(SshServer sshd = createTestServer()) {
-            sshd.setScheduledExecutorService(executorService, true);
-    
-            sshd.start();
-            sshd.stop();
-    
-            assertTrue(executorService.isShutdown());
-        }
-    }
-
-    @Test
-    public void testDynamicPort() throws Exception {
-        try(SshServer sshd = createTestServer()) {
-            sshd.setHost("localhost");
-            sshd.start();
-    
-            assertNotEquals(0, sshd.getPort());
-    
-            sshd.stop();
-        }
-    }
-
-
-    private SshServer createTestServer() {
-        SshServer sshd = SshServer.setUpDefaultServer();
-        sshd.setKeyPairProvider(Utils.createTestHostKeyProvider());
-        sshd.setShellFactory(new EchoShellFactory());
-        sshd.setPasswordAuthenticator(BogusPasswordAuthenticator.INSTANCE);
-
-        return sshd;
-    }
-}


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

Posted by lg...@apache.org.
[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;


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

Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/common/config/keys/KeyUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/KeyUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/KeyUtils.java
index c4f808d..e8207ed 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/KeyUtils.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/KeyUtils.java
@@ -18,17 +18,24 @@
  */
 package org.apache.sshd.common.config.keys;
 
+import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
 import java.security.Key;
 import java.security.KeyPair;
+import java.security.PrivateKey;
 import java.security.PublicKey;
 import java.security.interfaces.DSAKey;
 import java.security.interfaces.DSAParams;
+import java.security.interfaces.DSAPrivateKey;
 import java.security.interfaces.DSAPublicKey;
 import java.security.interfaces.ECKey;
+import java.security.interfaces.ECPrivateKey;
 import java.security.interfaces.ECPublicKey;
 import java.security.interfaces.RSAKey;
+import java.security.interfaces.RSAPrivateKey;
 import java.security.interfaces.RSAPublicKey;
 import java.security.spec.ECParameterSpec;
+import java.security.spec.InvalidKeySpecException;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashMap;
@@ -51,11 +58,11 @@ import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
  *
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
-public class KeyUtils {
-    private static final Map<String,PublicKeyEntryDecoder<? extends PublicKey>> byKeyTypeDecodersMap =
-            new TreeMap<String, PublicKeyEntryDecoder<? extends PublicKey>>(String.CASE_INSENSITIVE_ORDER);
-    private static final Map<Class<?>,PublicKeyEntryDecoder<? extends PublicKey>> byKeyClassDecodersMap =
-            new HashMap<Class<?>, PublicKeyEntryDecoder<? extends PublicKey>>();
+public final class KeyUtils {
+    private static final Map<String,PublicKeyEntryDecoder<?,?>> byKeyTypeDecodersMap =
+            new TreeMap<String, PublicKeyEntryDecoder<?,?>>(String.CASE_INSENSITIVE_ORDER);
+    private static final Map<Class<?>,PublicKeyEntryDecoder<?,?>> byKeyClassDecodersMap =
+            new HashMap<Class<?>, PublicKeyEntryDecoder<?,?>>();
 
     static {
         registerPublicKeyEntryDecoder(RSAPublicKeyDecoder.INSTANCE);
@@ -67,18 +74,61 @@ public class KeyUtils {
         throw new UnsupportedOperationException("No instance");
     }
 
-    public static void registerPublicKeyEntryDecoder(PublicKeyEntryDecoder<? extends PublicKey> decoder) {
+    /**
+     * @param keyType The key type - {@code OpenSSH} name - e.g., {@code ssh-rsa, ssh-dss}
+     * @param keySize The key size (in bits)
+     * @return A {@link KeyPair} of the specified type and size
+     * @throws GeneralSecurityException If failed to generate the key pair
+     * @see #getPublicKeyEntryDecoder(String)
+     * @see PublicKeyEntryDecoder#generateKeyPair(int)
+     */
+    public static KeyPair generateKeyPair(String keyType, int keySize) throws GeneralSecurityException {
+        PublicKeyEntryDecoder<?,?> decoder = getPublicKeyEntryDecoder(keyType);
+        if (decoder == null) {
+            throw new InvalidKeySpecException("No decoder for key type=" + keyType);
+        }
+        
+        return decoder.generateKeyPair(keySize);
+    }
+
+    /**
+     * Performs a deep-clone of the original {@link KeyPair} - i.e., creates
+     * <U>new</U> public/private keys that are clones of the original one
+     * @param keyType The key type - {@code OpenSSH} name - e.g., {@code ssh-rsa, ssh-dss}
+     * @param kp The {@link KeyPair} to clone - ignored if {@code null}
+     * @return The cloned instance
+     * @throws GeneralSecurityException If failed to clone the pair
+     */
+    public static KeyPair cloneKeyPair(String keyType, KeyPair kp) throws GeneralSecurityException {
+        PublicKeyEntryDecoder<?,?> decoder = getPublicKeyEntryDecoder(keyType);
+        if (decoder == null) {
+            throw new InvalidKeySpecException("No decoder for key type=" + keyType);
+        }
+
+        return decoder.cloneKeyPair(kp);
+    }
+
+    /**
+     * @param decoder The decoder to register
+     * @throws IllegalArgumentException if no decoder or not key type or no
+     * supported names for the decoder
+     * @see PublicKeyEntryDecoder#getPublicKeyType()
+     * @see PublicKeyEntryDecoder#getSupportedTypeNames()
+     */
+    public static void registerPublicKeyEntryDecoder(PublicKeyEntryDecoder<?,?> decoder) {
         ValidateUtils.checkNotNull(decoder, "No decoder specified", GenericUtils.EMPTY_OBJECT_ARRAY);
 
-        Class<?> keyType = ValidateUtils.checkNotNull(decoder.getKeyType(), "No key type declared", GenericUtils.EMPTY_OBJECT_ARRAY);
+        Class<?> pubType = ValidateUtils.checkNotNull(decoder.getPublicKeyType(), "No public key type declared", GenericUtils.EMPTY_OBJECT_ARRAY);
+        Class<?> prvType = ValidateUtils.checkNotNull(decoder.getPrivateKeyType(), "No private key type declared", GenericUtils.EMPTY_OBJECT_ARRAY);
         synchronized(byKeyClassDecodersMap) {
-            byKeyClassDecodersMap.put(keyType, decoder);
+            byKeyClassDecodersMap.put(pubType, decoder);
+            byKeyClassDecodersMap.put(prvType, decoder);
         }
 
         Collection<String> names = ValidateUtils.checkNotNullAndNotEmpty(decoder.getSupportedTypeNames(), "No supported key type", GenericUtils.EMPTY_OBJECT_ARRAY);
         synchronized(byKeyTypeDecodersMap) {
             for (String n : names) {
-                PublicKeyEntryDecoder<? extends PublicKey>  prev = byKeyTypeDecodersMap.put(n, decoder);
+                PublicKeyEntryDecoder<?,?>  prev = byKeyTypeDecodersMap.put(n, decoder);
                 if (prev != null) {
                     continue;   // debug breakpoint
                 }
@@ -87,10 +137,11 @@ public class KeyUtils {
     }
 
     /**
-     * @param keyType The {@code OpenSSH} key type string - ignored if {@code null}/empty
+     * @param keyType The {@code OpenSSH} key type string -  e.g., {@code ssh-rsa, ssh-dss}
+     * - ignored if {@code null}/empty
      * @return The registered {@link PublicKeyEntryDecoder} or {code null} if not found
      */
-    public static PublicKeyEntryDecoder<? extends PublicKey> getPublicKeyEntryDecoder(String keyType) {
+    public static PublicKeyEntryDecoder<?,?> getPublicKeyEntryDecoder(String keyType) {
         if (GenericUtils.isEmpty(keyType)) {
             return null;
         }
@@ -101,11 +152,11 @@ public class KeyUtils {
     }
     
     /**
-     * @param key The {@link PublicKey} - ignored if {@code null}
+     * @param key The {@link Key} (public or private) - ignored if {@code null}
      * @return The registered {@link PublicKeyEntryDecoder} for this key or {code null} if no match found
      * @see #getPublicKeyEntryDecoder(Class)
      */
-    public static PublicKeyEntryDecoder<? extends PublicKey> getPublicKeyEntryDecoder(PublicKey key) {
+    public static PublicKeyEntryDecoder<?,?> getPublicKeyEntryDecoder(Key key) {
         if (key == null) {
             return null;
         } else {
@@ -114,27 +165,27 @@ public class KeyUtils {
     }
 
     /**
-     * @param keyType The key {@link Class} - ignored if {@code null} or not a {@link PublicKey}
+     * @param keyType The key {@link Class} - ignored if {@code null} or not a {@link Key}
      * compatible type
      * @return The registered {@link PublicKeyEntryDecoder} or {code null} if no match found
      */
-    public static PublicKeyEntryDecoder<? extends PublicKey> getPublicKeyEntryDecoder(Class<?> keyType) {
-        if ((keyType == null) || (!PublicKey.class.isAssignableFrom(keyType))) {
+    public static PublicKeyEntryDecoder<?,?> getPublicKeyEntryDecoder(Class<?> keyType) {
+        if ((keyType == null) || (!Key.class.isAssignableFrom(keyType))) {
             return null;
         }
         
         synchronized(byKeyTypeDecodersMap) {
             {
-                PublicKeyEntryDecoder<? extends PublicKey>  decoder=byKeyClassDecodersMap.get(keyType);
+                PublicKeyEntryDecoder<?,?>  decoder=byKeyClassDecodersMap.get(keyType);
                 if (decoder != null) {
                     return decoder;
                 }
             }
             
             // in case it is a derived class
-            for (PublicKeyEntryDecoder<? extends PublicKey> decoder : byKeyClassDecodersMap.values()) {
-                Class<?> t = decoder.getKeyType();
-                if (t.isAssignableFrom(keyType)) {
+            for (PublicKeyEntryDecoder<?,?> decoder : byKeyClassDecodersMap.values()) {
+                Class<?> pubType = decoder.getPublicKeyType(), prvType = decoder.getPrivateKeyType();
+                if (pubType.isAssignableFrom(keyType) || prvType.isAssignableFrom(keyType)) {
                     return decoder;
                 }
             }
@@ -157,14 +208,39 @@ public class KeyUtils {
         try {
             Buffer buffer = new ByteArrayBuffer();
             buffer.putRawPublicKey(key);
-            Digest md5 = BuiltinDigests.md5.create();
-            md5.init();
-            md5.update(buffer.array(), 0, buffer.wpos());
-            byte[] data = md5.digest();
-            return BufferUtils.printHex(data, 0, data.length, ':');
+            return getFingerPrint(buffer.array(), 0, buffer.wpos());
+        } catch(Exception e) {
+            return e.getClass().getSimpleName();
+        }
+    }
+
+    public static String getFingerPrint(String password) {
+        if (GenericUtils.isEmpty(password)) {
+            return null;
+        }
+        
+        try {
+            return getFingerPrint(password.getBytes(StandardCharsets.UTF_8));
         } catch(Exception e) {
-            return "Unable to compute fingerprint";
+            return e.getClass().getSimpleName();
+        }
+    }
+    
+    public static String getFingerPrint(byte ... buf) throws Exception {
+        return getFingerPrint(buf, 0, GenericUtils.length(buf));
+    }
+    
+    public static String getFingerPrint(byte[] buf, int offset, int len) throws Exception {
+        if (len <= 0) {
+            return null;
         }
+
+        Digest md5 = BuiltinDigests.md5.create();
+        md5.init();
+        md5.update(buf, offset, len);
+
+        byte[] data = md5.digest();
+        return BufferUtils.printHex(data, 0, data.length, ':');
     }
 
     /**
@@ -232,6 +308,72 @@ public class KeyUtils {
         return null;
     }
 
+    public static boolean compareKeyPairs(KeyPair k1, KeyPair k2) {
+        if (Objects.equals(k1, k2)) {
+            return true;
+        } else if ((k1 == null) || (k2 == null)) {
+            return false;   // both null is covered by Objects#equals
+        }
+        
+        if (compareKeys(k1.getPublic(), k2.getPublic())
+         && compareKeys(k1.getPrivate(), k2.getPrivate())) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    public static boolean compareKeys(PrivateKey k1, PrivateKey k2) {
+        if ((k1 instanceof RSAPrivateKey) && (k2 instanceof RSAPrivateKey)) {
+            return compareRSAKeys(RSAPrivateKey.class.cast(k1), RSAPrivateKey.class.cast(k2));
+        } else if ((k1 instanceof DSAPrivateKey) && (k2 instanceof DSAPrivateKey)) {
+            return compareDSAKeys(DSAPrivateKey.class.cast(k1), DSAPrivateKey.class.cast(k2));
+        } else if ((k1 instanceof ECPrivateKey) && (k2 instanceof ECPrivateKey)) {
+            return compareECKeys(ECPrivateKey.class.cast(k1), ECPrivateKey.class.cast(k2));
+        } else {
+            return false;   // either key is null or not of same class
+        }
+    }
+
+    public static boolean compareRSAKeys(RSAPrivateKey k1, RSAPrivateKey k2) {
+        if (Objects.equals(k1, k2)) {
+            return true;
+        } else if ((k1 == null) || (k2 == null)) {
+            return false;   // both null is covered by Objects#equals
+        } else if (Objects.equals(k1.getModulus(), k2.getModulus())
+                && Objects.equals(k1.getPrivateExponent(), k2.getPrivateExponent())) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    public static boolean compareDSAKeys(DSAPrivateKey k1, DSAPrivateKey k2) {
+        if (Objects.equals(k1, k2)) {
+            return true;
+        } else if ((k1 == null) || (k2 == null)) {
+            return false;   // both null is covered by Objects#equals
+        } else if (Objects.equals(k1.getX(), k2.getAlgorithm())
+                && compareDSAParams(k1.getParams(), k2.getParams())) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    public static boolean compareECKeys(ECPrivateKey k1, ECPrivateKey k2) {
+        if (Objects.equals(k1, k2)) {
+            return true;
+        } else if ((k1 == null) || (k2 == null)) {
+            return false;   // both null is covered by Objects#equals
+        } else if (Objects.equals(k1.getS(), k2.getS())
+                && compareECParams(k1.getParams(), k2.getParams())) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
     public static boolean compareKeys(PublicKey k1, PublicKey k2) {
         if ((k1 instanceof RSAPublicKey) && (k2 instanceof RSAPublicKey)) {
             return compareRSAKeys(RSAPublicKey.class.cast(k1), RSAPublicKey.class.cast(k2));

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntry.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntry.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntry.java
index 28708fb..cd3ec94 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntry.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntry.java
@@ -83,7 +83,7 @@ public class PublicKeyEntry implements Serializable {
      */
     public PublicKey resolvePublicKey() throws IOException, GeneralSecurityException {
         String kt = getKeyType();
-        PublicKeyEntryDecoder<? extends PublicKey> decoder = KeyUtils.getPublicKeyEntryDecoder(kt);
+        PublicKeyEntryDecoder<?,?> decoder = KeyUtils.getPublicKeyEntryDecoder(kt);
         if (decoder == null) {
             throw new InvalidKeySpecException("No decoder registered for key type=" + kt);
         }
@@ -228,7 +228,7 @@ public class PublicKeyEntry implements Serializable {
      */
     public static final <A extends Appendable> A appendPublicKeyEntry(A sb, PublicKey key) throws IOException {
         @SuppressWarnings("unchecked")
-        PublicKeyEntryDecoder<PublicKey> decoder = (PublicKeyEntryDecoder<PublicKey>) KeyUtils.getPublicKeyEntryDecoder(key);
+        PublicKeyEntryDecoder<PublicKey,?> decoder = (PublicKeyEntryDecoder<PublicKey,?>) KeyUtils.getPublicKeyEntryDecoder(key);
         if (decoder == null) {
             throw new StreamCorruptedException("Cannot retrived decoder for key=" + key.getAlgorithm());
         }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntryDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntryDecoder.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntryDecoder.java
index fc43bb3..dbff853 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntryDecoder.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntryDecoder.java
@@ -23,6 +23,10 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.PrivateKey;
 import java.security.PublicKey;
 import java.util.Collection;
 
@@ -30,12 +34,18 @@ import java.util.Collection;
  * Represents a decoder of an {@code OpenSSH} encoded key data
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
-public interface PublicKeyEntryDecoder<K extends PublicKey> {
+public interface PublicKeyEntryDecoder<PUB extends PublicKey,PRV extends PrivateKey> {
     /**
      * @return The {@link Class} of the {@link PublicKey} that is the result
      * of decoding
      */
-    Class<K> getKeyType();
+    Class<PUB> getPublicKeyType();
+    
+    /**
+     * @return The {@link Class} of the {@link PrivateKey} that matches the
+     * public one
+     */
+    Class<PRV> getPrivateKeyType();
 
     /**
      * @return The {@link Collection} of {@code OpenSSH} key type names that
@@ -43,7 +53,44 @@ public interface PublicKeyEntryDecoder<K extends PublicKey> {
      * <B>Caveat:</B> this collection may be un-modifiable...
      */
     Collection<String> getSupportedTypeNames();
-    
+
+    /**
+     * @param keySize Key size in bits
+     * @return A {@link KeyPair} with the specified key size
+     * @throws GeneralSecurityException if unable to generate the pair
+     */
+    KeyPair generateKeyPair(int keySize) throws GeneralSecurityException;
+
+    /**
+     * @param kp The {@link KeyPair} to be cloned - ignored if {@code null}
+     * @return A cloned pair (or {@code null} if no original pair)
+     * @throws GeneralSecurityException If failed to clone - e.g., provided key
+     * pair does not contain keys of the expected type
+     * @see #getPublicKeyType()
+     * @see #getPrivateKeyType() 
+     */
+    KeyPair cloneKeyPair(KeyPair kp) throws GeneralSecurityException;
+
+    /**
+     * @param key The {@link PublicKey} to clone - ignored if {@code null}
+     * @return The cloned key (or {@code null} if no original key)
+     * @throws GeneralSecurityException If failed to clone the key
+     */
+    PUB clonePublicKey(PUB key) throws GeneralSecurityException;
+
+    /**
+     * @param key The {@link PrivateKey} to clone - ignored if {@code null}
+     * @return The cloned key (or {@code null} if no original key)
+     * @throws GeneralSecurityException If failed to clone the key
+     */
+    PRV clonePrivateKey(PRV key) throws GeneralSecurityException;
+
+    /**
+     * @return A {@link KeyPairGenerator} suitable for this decoder
+     * @throws GeneralSecurityException If failed to create the generator
+     */
+    KeyPairGenerator getKeyPairGenerator() throws GeneralSecurityException;
+
     /**
      * @param keyData The key data bytes in {@code OpenSSH} format (after
      * BASE64 decoding) - ignored if {@code null}/empty
@@ -51,9 +98,9 @@ public interface PublicKeyEntryDecoder<K extends PublicKey> {
      * @throws IOException If failed to decode the key
      * @throws GeneralSecurityException If failed to generate the key
      */
-    K decodePublicKey(byte ... keyData) throws IOException, GeneralSecurityException;
-    K decodePublicKey(byte[] keyData, int offset, int length) throws IOException, GeneralSecurityException;
-    K decodePublicKey(InputStream keyData) throws IOException, GeneralSecurityException;
+    PUB decodePublicKey(byte ... keyData) throws IOException, GeneralSecurityException;
+    PUB decodePublicKey(byte[] keyData, int offset, int length) throws IOException, GeneralSecurityException;
+    PUB decodePublicKey(InputStream keyData) throws IOException, GeneralSecurityException;
     
     /**
      * Encodes the {@link PublicKey} using the {@code OpenSSH} format - same
@@ -63,5 +110,11 @@ public interface PublicKeyEntryDecoder<K extends PublicKey> {
      * @return The key type value - one of the {@link #getSupportedTypeNames()}
      * @throws IOException If failed to generate the encoding
      */
-    String encodePublicKey(OutputStream s, K key) throws IOException;
+    String encodePublicKey(OutputStream s, PUB key) throws IOException;
+
+    /**
+     * @return A {@link KeyFactory} suitable for the specific decoder type
+     * @throws GeneralSecurityException If failed to create one
+     */
+    KeyFactory getKeyFactoryInstance() throws GeneralSecurityException;
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/common/config/keys/RSAPublicKeyDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/RSAPublicKeyDecoder.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/RSAPublicKeyDecoder.java
index f1b5dcb..5927568 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/RSAPublicKeyDecoder.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/RSAPublicKeyDecoder.java
@@ -24,9 +24,14 @@ 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.RSAPrivateCrtKey;
+import java.security.interfaces.RSAPrivateKey;
 import java.security.interfaces.RSAPublicKey;
 import java.security.spec.InvalidKeySpecException;
+import java.security.spec.RSAPrivateCrtKeySpec;
 import java.security.spec.RSAPublicKeySpec;
 import java.util.Collections;
 
@@ -38,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 RSAPublicKeyDecoder extends AbstractPublicKeyEntryDecoder<RSAPublicKey> {
+public class RSAPublicKeyDecoder extends AbstractPublicKeyEntryDecoder<RSAPublicKey,RSAPrivateKey> {
     public static final RSAPublicKeyDecoder INSTANCE = new RSAPublicKeyDecoder();
 
     public RSAPublicKeyDecoder() {
-        super(RSAPublicKey.class, Collections.unmodifiableList(Collections.singletonList(KeyPairProvider.SSH_RSA)));
+        super(RSAPublicKey.class, RSAPrivateKey.class, Collections.unmodifiableList(Collections.singletonList(KeyPairProvider.SSH_RSA)));
     }
 
     @Override
@@ -68,6 +73,43 @@ public class RSAPublicKeyDecoder extends AbstractPublicKeyEntryDecoder<RSAPublic
     }
 
     @Override
+    public RSAPublicKey clonePublicKey(RSAPublicKey key) throws GeneralSecurityException {
+        if (key == null) {
+            return null;
+        } else {
+            return generatePublicKey(new RSAPublicKeySpec(key.getModulus(), key.getPublicExponent()));
+        }
+    }
+
+    @Override
+    public RSAPrivateKey clonePrivateKey(RSAPrivateKey key) throws GeneralSecurityException {
+        if (key == null) {
+            return null;
+        }
+        
+        if (!(key instanceof RSAPrivateCrtKey)) {
+            throw new InvalidKeyException("Cannot clone a non-RSAPrivateCrtKey: " + key.getClass().getSimpleName());
+        }
+
+        RSAPrivateCrtKey rsaPrv = (RSAPrivateCrtKey) key;
+        return generatePrivateKey(
+                new RSAPrivateCrtKeySpec(
+                        rsaPrv.getModulus(),
+                        rsaPrv.getPublicExponent(),
+                        rsaPrv.getPrivateExponent(),
+                        rsaPrv.getPrimeP(),
+                        rsaPrv.getPrimeQ(),
+                        rsaPrv.getPrimeExponentP(),
+                        rsaPrv.getPrimeExponentQ(),
+                        rsaPrv.getCrtCoefficient()));
+    }
+
+    @Override
+    public KeyPairGenerator getKeyPairGenerator() throws GeneralSecurityException {
+        return SecurityUtils.getKeyPairGenerator("RSA");
+    }
+
+    @Override
     public KeyFactory getKeyFactoryInstance() throws GeneralSecurityException {
         return SecurityUtils.getKeyFactory("RSA");
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NativeFileSystemFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NativeFileSystemFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NativeFileSystemFactory.java
index 8f35ba7..61cfff0 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NativeFileSystemFactory.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NativeFileSystemFactory.java
@@ -25,7 +25,7 @@ import java.nio.file.FileSystems;
 
 import org.apache.sshd.common.file.FileSystemFactory;
 import org.apache.sshd.common.session.Session;
-import org.apache.sshd.common.util.AbstractLoggingBean;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
 
 /**
  * Native file system factory. It uses the OS file system.

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/common/file/root/RootedFileSystemProvider.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/file/root/RootedFileSystemProvider.java b/sshd-core/src/main/java/org/apache/sshd/common/file/root/RootedFileSystemProvider.java
index c1bb4e3..bad235f 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/file/root/RootedFileSystemProvider.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/file/root/RootedFileSystemProvider.java
@@ -49,8 +49,8 @@ import java.util.Set;
 import java.util.concurrent.ExecutorService;
 
 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;
 
 /**
  * File system provider which provides a rooted file system.

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/common/future/DefaultSshFuture.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/future/DefaultSshFuture.java b/sshd-core/src/main/java/org/apache/sshd/common/future/DefaultSshFuture.java
index 8d263e8..0ee8afe 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/future/DefaultSshFuture.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/future/DefaultSshFuture.java
@@ -21,7 +21,7 @@ package org.apache.sshd.common.future;
 import java.lang.reflect.Array;
 import java.util.concurrent.TimeUnit;
 
-import org.apache.sshd.common.util.AbstractLoggingBean;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
 
 /**
  * A default implementation of {@link SshFuture}.

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/common/io/AbstractIoServiceFactoryFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/io/AbstractIoServiceFactoryFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/io/AbstractIoServiceFactoryFactory.java
index 0cb85f6..9d9b312 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/io/AbstractIoServiceFactoryFactory.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/io/AbstractIoServiceFactoryFactory.java
@@ -21,7 +21,7 @@ package org.apache.sshd.common.io;
 
 import java.util.concurrent.ExecutorService;
 
-import org.apache.sshd.common.util.AbstractLoggingBean;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
 import org.apache.sshd.common.util.threads.ExecutorServiceConfigurer;
 
 /**

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/common/kex/dh/AbstractDHKeyExchange.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/kex/dh/AbstractDHKeyExchange.java b/sshd-core/src/main/java/org/apache/sshd/common/kex/dh/AbstractDHKeyExchange.java
index a3d57eb..9abbaa4 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/kex/dh/AbstractDHKeyExchange.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/kex/dh/AbstractDHKeyExchange.java
@@ -22,7 +22,7 @@ package org.apache.sshd.common.kex.dh;
 import org.apache.sshd.common.digest.Digest;
 import org.apache.sshd.common.kex.KeyExchange;
 import org.apache.sshd.common.session.AbstractSession;
-import org.apache.sshd.common.util.AbstractLoggingBean;
+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/common/keyprovider/AbstractKeyPairProvider.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/AbstractKeyPairProvider.java b/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/AbstractKeyPairProvider.java
index 03ba5f2..9a0aa73 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/AbstractKeyPairProvider.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/AbstractKeyPairProvider.java
@@ -23,9 +23,9 @@ import java.util.ArrayList;
 import java.util.List;
 
 import org.apache.sshd.common.config.keys.KeyUtils;
-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;
 
 /**
  * TODO Add javadoc

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/MappedKeyPairProvider.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/MappedKeyPairProvider.java b/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/MappedKeyPairProvider.java
index 8453d25..dcb9e36 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/MappedKeyPairProvider.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/MappedKeyPairProvider.java
@@ -22,8 +22,8 @@ package org.apache.sshd.common.keyprovider;
 import java.security.KeyPair;
 import java.util.Map;
 
-import org.apache.sshd.common.Transformer;
 import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.Transformer;
 import org.apache.sshd.common.util.ValidateUtils;
 
 /**

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/common/mac/MacFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/mac/MacFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/mac/MacFactory.java
index 6bc59f0..e84bbb4 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/mac/MacFactory.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/mac/MacFactory.java
@@ -21,7 +21,7 @@ package org.apache.sshd.common.mac;
 
 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/scp/LocalFileScpSourceStreamResolver.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/scp/LocalFileScpSourceStreamResolver.java b/sshd-core/src/main/java/org/apache/sshd/common/scp/LocalFileScpSourceStreamResolver.java
index 7c2311e..4db74c0 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/scp/LocalFileScpSourceStreamResolver.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/scp/LocalFileScpSourceStreamResolver.java
@@ -29,10 +29,10 @@ import java.nio.file.attribute.PosixFilePermission;
 import java.util.Collection;
 import java.util.Set;
 
-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/common/scp/LocalFileScpTargetStreamResolver.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/scp/LocalFileScpTargetStreamResolver.java b/sshd-core/src/main/java/org/apache/sshd/common/scp/LocalFileScpTargetStreamResolver.java
index efd7f46..1648b9e 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/scp/LocalFileScpTargetStreamResolver.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/scp/LocalFileScpTargetStreamResolver.java
@@ -33,8 +33,8 @@ import java.nio.file.attribute.PosixFilePermission;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 
-import org.apache.sshd.common.util.AbstractLoggingBean;
-import org.apache.sshd.common.util.IoUtils;
+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/common/scp/ScpHelper.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/scp/ScpHelper.java b/sshd-core/src/main/java/org/apache/sshd/common/scp/ScpHelper.java
index b7d244e..45afa6e 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/scp/ScpHelper.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/scp/ScpHelper.java
@@ -43,11 +43,11 @@ import java.util.concurrent.TimeUnit;
 import org.apache.sshd.common.SshException;
 import org.apache.sshd.common.file.util.MockPath;
 import org.apache.sshd.common.scp.ScpTransferEventListener.FileOperation;
-import org.apache.sshd.common.util.AbstractLoggingBean;
 import org.apache.sshd.common.util.DirectoryScanner;
 import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.IoUtils;
+import org.apache.sshd.common.util.io.IoUtils;
 import org.apache.sshd.common.util.io.LimitInputStream;
+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/common/session/AbstractSession.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java b/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java
index 1d25411..ce0287d 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java
@@ -416,7 +416,7 @@ public abstract class AbstractSession extends CloseableUtils.AbstractInnerClosea
                 sendEvent(SessionListener.Event.KeyEstablished);
                 synchronized (pendingPackets) {
                     if (!pendingPackets.isEmpty()) {
-                        log.info("Dequeing pending packets");
+                        log.debug("Dequeing pending packets");
                         synchronized (encodeLock) {
                             PendingWriteFuture future;
                             while ((future = pendingPackets.poll()) != null) {
@@ -530,7 +530,7 @@ public abstract class AbstractSession extends CloseableUtils.AbstractInnerClosea
                 synchronized (pendingPackets) {
                     if (kexState.get() != KEX_STATE_DONE) {
                         if (pendingPackets.isEmpty()) {
-                            log.info("Start flagging packets as pending until key exchange is done");
+                            log.debug("Start flagging packets as pending until key exchange is done");
                         }
                         PendingWriteFuture future = new PendingWriteFuture(buffer);
                         pendingPackets.add(future);
@@ -1159,7 +1159,7 @@ public abstract class AbstractSession extends CloseableUtils.AbstractInnerClosea
     protected void negotiate() {
         String[] guess = new String[SshConstants.PROPOSAL_MAX];
         for (int i = 0; i < SshConstants.PROPOSAL_MAX; i++) {
-        	String paramName = SshConstants.PROPOSAL_KEX_NAMES[i];
+        	String paramName = SshConstants.PROPOSAL_KEX_NAMES.get(i);
         	String clientParamValue = clientProposal[i];
         	String serverParamValue = serverProposal[i];
             String[] c = clientParamValue.split(",");

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/common/session/SessionTimeoutListener.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/SessionTimeoutListener.java b/sshd-core/src/main/java/org/apache/sshd/common/session/SessionTimeoutListener.java
index 26764a7..fcabbe1 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/session/SessionTimeoutListener.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/session/SessionTimeoutListener.java
@@ -21,7 +21,7 @@ package org.apache.sshd.common.session;
 import java.util.Set;
 import java.util.concurrent.CopyOnWriteArraySet;
 
-import org.apache.sshd.common.util.AbstractLoggingBean;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
 
 /**
  * Task that iterates over all currently open {@link AbstractSession}s and checks each of them for timeouts. If

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureFactory.java
index 26abe3c..e0880d4 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureFactory.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureFactory.java
@@ -21,7 +21,7 @@ package org.apache.sshd.common.signature;
 
 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/util/AbstractLoggingBean.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/AbstractLoggingBean.java b/sshd-core/src/main/java/org/apache/sshd/common/util/AbstractLoggingBean.java
deleted file mode 100644
index 39131c8..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/AbstractLoggingBean.java
+++ /dev/null
@@ -1,49 +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.util;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Serves as a common base class for the vast majority of classes that require
- * some kind of logging. Facilitates quick and easy replacement of the actual used
- * logger from one framework to another
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class AbstractLoggingBean {
-    protected final Logger log;
-    
-    /**
-     * Default constructor - creates a logger using the full class name
-     */
-    protected AbstractLoggingBean() {
-        log = LoggerFactory.getLogger(getClass());
-    }
-    
-    /**
-     * Create a logger for instances of the same class for which we might
-     * want to have a &quot;discriminator&quot; for them
-     * @param discriminator The discriminator value
-     */
-    protected AbstractLoggingBean(String discriminator) {
-        log = LoggerFactory.getLogger(getClass().getName() + "[" + discriminator + "]");
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/common/util/CloseableUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/CloseableUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/CloseableUtils.java
index 9710d57..a75f470 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/CloseableUtils.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/CloseableUtils.java
@@ -36,13 +36,17 @@ import org.apache.sshd.common.future.DefaultCloseFuture;
 import org.apache.sshd.common.future.DefaultSshFuture;
 import org.apache.sshd.common.future.SshFuture;
 import org.apache.sshd.common.future.SshFutureListener;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
 
 /**
  * Utility class to help with {@link Closeable}s.
  *
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
-public class CloseableUtils {
+public final class CloseableUtils {
+    private CloseableUtils() {
+        throw new UnsupportedOperationException("No instance");
+    }
 
     // TODO once JDK 8+ becomes the minimum for this project, make it a static method in the Closeable interface
     public static void close(Closeable closeable) throws IOException {
@@ -349,6 +353,7 @@ public class CloseableUtils {
                     if (grace != null) {
                         grace.addListener(new SshFutureListener<CloseFuture>() {
                             @Override
+                            @SuppressWarnings("synthetic-access")
                             public void operationComplete(CloseFuture future) {
                                 if (state.compareAndSet(State.Graceful, State.Immediate)) {
                                     doCloseImmediately();
@@ -438,8 +443,4 @@ public class CloseableUtils {
             });
         }
     }
-
-    private CloseableUtils() {
-        throw new UnsupportedOperationException("No instance allowed");
-    }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/common/util/EventListenerUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/EventListenerUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/EventListenerUtils.java
index b89774f..48df9f4 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/EventListenerUtils.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/EventListenerUtils.java
@@ -28,7 +28,11 @@ import java.util.EventListener;
 /**
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
-public class EventListenerUtils {
+public final class EventListenerUtils {
+    private EventListenerUtils() {
+        throw new UnsupportedOperationException("No instance");
+    }
+
     /**
      * Provides proxy wrapper around an {@link Iterable} container of listener
      * interface implementation. <b>Note:</b> a listener interface is one whose

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/common/util/GenericUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/GenericUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/GenericUtils.java
index 657a6ad..6d049e8 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/GenericUtils.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/GenericUtils.java
@@ -35,11 +35,15 @@ import java.util.TreeSet;
 /**
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
-public class GenericUtils {
+public final class GenericUtils {
     public static final byte[]      EMPTY_BYTE_ARRAY={ };
     public static final String[]    EMPTY_STRING_ARRAY={ };
     public static final Object[]    EMPTY_OBJECT_ARRAY={ };
 
+    private GenericUtils() {
+        throw new UnsupportedOperationException("No instance");
+    }
+
     public static final String trimToEmpty(String s) {
         if (s == null) {
             return "";
@@ -333,4 +337,27 @@ public class GenericUtils {
             return s.subSequence(1, lastPos);
         }
     }
+    
+    /**
+     * Used to &quot;accumulate&quot; exceptions of the <U>same type</U>. If the
+     * current exception is {@code null} then the new one becomes the current,
+     * otherwise the new one is added as a <U>suppressed</U> exception to the
+     * current one
+     * @param current The current exception
+     * @param extra The extra/new exception
+     * @return The resolved exception
+     * @see Throwable#addSuppressed(Throwable)
+     */
+    public static <T extends Throwable> T accumulateException(T current, T extra) {
+        if (current == null) {
+            return extra;
+        }
+        
+        if ((extra == null) || (extra == current)) {
+            return current;
+        }
+        
+        current.addSuppressed(extra);
+        return current;
+    }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/common/util/IoUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/IoUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/IoUtils.java
deleted file mode 100644
index da17c22..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/IoUtils.java
+++ /dev/null
@@ -1,291 +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.util;
-
-import java.io.Closeable;
-import java.io.EOFException;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.file.FileSystem;
-import java.nio.file.Files;
-import java.nio.file.LinkOption;
-import java.nio.file.Path;
-import java.nio.file.attribute.PosixFilePermission;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.List;
-import java.util.Set;
-
-/**
- * TODO Add javadoc
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class IoUtils {
-
-    public static final LinkOption[] EMPTY_OPTIONS = new LinkOption[0];
-    private static final LinkOption[] NO_FOLLOW_OPTIONS = new LinkOption[] { LinkOption.NOFOLLOW_LINKS };
-
-    public static LinkOption[] getLinkOptions(boolean followLinks) {
-        if (followLinks) {
-            return EMPTY_OPTIONS;
-        } else {    // return a clone that modifications to the array will not affect others
-            return NO_FOLLOW_OPTIONS.clone();
-        }
-    }
-
-    public static final int DEFAULT_COPY_SIZE=8192;
-
-    public static long copy(InputStream source, OutputStream sink) throws IOException {
-        return copy(source, sink, DEFAULT_COPY_SIZE);
-    }
-
-    public static long copy(InputStream source, OutputStream sink, int bufferSize) throws IOException {
-        long nread = 0L;
-        byte[] buf = new byte[bufferSize];
-        int n;
-        while ((n = source.read(buf)) > 0) {
-            sink.write(buf, 0, n);
-            nread += n;
-        }
-        return nread;
-    }
-
-    public static void closeQuietly(Closeable... closeables) {
-        for (Closeable c : closeables) {
-            try {
-                if (c != null) {
-                    c.close();
-                }
-            } catch (IOException e) {
-                // Ignore
-            }
-        }
-    }
-
-    public static final List<String> WINDOWS_EXECUTABLE_EXTENSIONS = Collections.unmodifiableList(Arrays.asList(".bat", ".exe", ".cmd"));
-
-    /**
-     * @param fileName The file name to be evaluated - ignored if {@code null}/empty
-     * @return {@code true} if the file ends in one of the {@link #WINDOWS_EXECUTABLE_EXTENSIONS}
-     */
-    public static boolean isWindowsExecutable(String fileName) {
-        if ((fileName == null) || (fileName.length() <= 0)) {
-            return false;
-        }
-        for (String suffix : WINDOWS_EXECUTABLE_EXTENSIONS) {
-            if (fileName.endsWith(suffix)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * If the &quot;posix&quot; view is supported, then it returns
-     * {@link Files#getPosixFilePermissions(Path, LinkOption...)}, otherwise
-     * uses the {@link #getPermissionsFromFile(File)} method
-     * @param path The {@link Path}
-     * @return A {@link Set} of {@link PosixFilePermission}
-     * @throws IOException If failed to access the file system in order to
-     * retrieve the permissions
-     */
-    public static Set<PosixFilePermission> getPermissions(Path path) throws IOException {
-        FileSystem          fs = path.getFileSystem();
-        Collection<String>  views = fs.supportedFileAttributeViews();
-        if (views.contains("posix")) {
-            return Files.getPosixFilePermissions(path, getLinkOptions(false));
-        } else {
-            return getPermissionsFromFile(path.toFile());
-        }
-    }
-
-    /**
-     * @param f The {@link File} to be checked
-     * @return A {@link Set} of {@link PosixFilePermission}s based on whether
-     * the file is readable/writable/executable. If so, then <U>all</U> the
-     * relevant permissions are set (i.e., owner, group and others)
-     */
-    public static Set<PosixFilePermission> getPermissionsFromFile(File f) {
-        Set<PosixFilePermission> perms = EnumSet.noneOf(PosixFilePermission.class);
-        if (f.canRead()) {
-            perms.add(PosixFilePermission.OWNER_READ);
-            perms.add(PosixFilePermission.GROUP_READ);
-            perms.add(PosixFilePermission.OTHERS_READ);
-        }
-
-        if (f.canWrite()) {
-            perms.add(PosixFilePermission.OWNER_WRITE);
-            perms.add(PosixFilePermission.GROUP_WRITE);
-            perms.add(PosixFilePermission.OTHERS_WRITE);
-        }
-
-        if (f.canExecute() || (OsUtils.isWin32() && isWindowsExecutable(f.getName()))) {
-            perms.add(PosixFilePermission.OWNER_EXECUTE);
-            perms.add(PosixFilePermission.GROUP_EXECUTE);
-            perms.add(PosixFilePermission.OTHERS_EXECUTE);
-        }
-
-        return perms;
-    }
-
-    /**
-     * If the &quot;posix&quot; view is supported, then it invokes
-     * {@link Files#setPosixFilePermissions(Path, Set)}, otherwise
-     * uses the {@link #setPermissionsToFile(File, Collection)} method
-     * @param path The {@link Path}
-     * @param perms The {@link Set} of {@link PosixFilePermission}s
-     * @throws IOException If failed to access the file system
-     */
-    public static void setPermissions(Path path, Set<PosixFilePermission> perms) throws IOException {
-        FileSystem          fs = path.getFileSystem();
-        Collection<String>  views = fs.supportedFileAttributeViews();
-        if (views.contains("posix")) {
-            Files.setPosixFilePermissions(path, perms);
-        } else {
-            setPermissionsToFile(path.toFile(), perms);
-        }
-    }
-
-    /**
-     * @param f The {@link File}
-     * @param perms A {@link Collection} of {@link PosixFilePermission}s to set on it.
-     * <B>Note:</B> the file is set to readable/writable/executable not only by the
-     * owner if <U>any</U> of relevant the owner/group/others permission is set
-     */
-    public static void setPermissionsToFile(File f, Collection<PosixFilePermission> perms) {
-        boolean readable = perms != null &&
-                  (perms.contains(PosixFilePermission.OWNER_READ)
-                || perms.contains(PosixFilePermission.GROUP_READ)
-                || perms.contains(PosixFilePermission.OTHERS_READ));
-        f.setReadable(readable, false);
-
-        boolean writable = perms != null &&
-                  (perms.contains(PosixFilePermission.OWNER_WRITE)
-                || perms.contains(PosixFilePermission.GROUP_WRITE)
-                || perms.contains(PosixFilePermission.OTHERS_WRITE));
-        f.setWritable(writable, false);
-
-        boolean executable = perms != null &&
-                  (perms.contains(PosixFilePermission.OWNER_EXECUTE)
-                || perms.contains(PosixFilePermission.GROUP_EXECUTE)
-                || perms.contains(PosixFilePermission.OTHERS_EXECUTE));
-        f.setExecutable(executable, false);
-    }
-
-    /**
-     * <P>Checks if a file exists - <B>Note:</B> according to the
-     * <A HREF="http://docs.oracle.com/javase/tutorial/essential/io/check.html">Java tutorial - Checking a File or Directory</A>:
-     * </P></BR>
-     * <PRE>
-     *      The methods in the Path class are syntactic, meaning that they operate
-     *      on the Path instance. But eventually you must access the file system
-     *      to verify that a particular Path exists, or does not exist. You can do
-     *      so with the exists(Path, LinkOption...) and the notExists(Path, LinkOption...)
-     *      methods. Note that !Files.exists(path) is not equivalent to Files.notExists(path).
-     *      When you are testing a file's existence, three results are possible:
-     *
-     *      - The file is verified to exist.
-     *      - The file is verified to not exist.
-     *      - The file's status is unknown.
-     *      
-     *      This result can occur when the program does not have access to the file.
-     *      If both exists and notExists return false, the existence of the file cannot
-     *      be verified.
-     * </PRE>
-     * @param path The {@link Path} to be tested
-     * @param options The {@link LinkOption}s to use
-     * @return {@link Boolean#TRUE}/{@link Boolean#FALSE} or {@code null}
-     * according to the file status as explained above
-     */
-    public static Boolean checkFileExists(Path path, LinkOption ... options) {
-        if (Files.exists(path, options)) {
-            return Boolean.TRUE;
-        } else if (Files.notExists(path, options)) {
-            return Boolean.FALSE;
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * Read the requested number of bytes or fail if there are not enough left.
-     * @param input where to read input from
-     * @param buffer destination
-     * @throws IOException if there is a problem reading the file
-     * @throws EOFException if the number of bytes read was incorrect
-     */
-    public static void readFully(InputStream input, byte[] buffer) throws IOException {
-        readFully(input, buffer, 0, buffer.length);
-    }
-
-    /**
-     * Read the requested number of bytes or fail if there are not enough left.
-     * @param input where to read input from
-     * @param buffer destination
-     * @param offset initial offset into buffer
-     * @param length length to read, must be >= 0
-     * @throws IOException if there is a problem reading the file
-     * @throws EOFException if the number of bytes read was incorrect
-     */
-    public static void readFully(InputStream input, byte[] buffer, int offset, int length) throws IOException {
-        int actual = read(input, buffer, offset, length);
-        if (actual != length) {
-            throw new EOFException("Premature EOF - expected=" + length + ", actual=" + actual);
-        }
-    }
-
-    /**
-     * Read as many bytes as possible until EOF or achieved required length
-     * @param input where to read input from
-     * @param buffer destination
-     * @return actual length read; may be less than requested if EOF was reached
-     * @throws IOException if a read error occurs
-     */
-    public static int read(InputStream input, byte[] buffer) throws IOException {
-        return read(input, buffer, 0, buffer.length);
-    }
-
-    /**
-     * Read as many bytes as possible until EOF or achieved required length
-     * @param input where to read input from
-     * @param buffer destination
-     * @param offset initial offset into buffer
-     * @param length length to read - ignored if non-positive
-     * @return actual length read; may be less than requested if EOF was reached
-     * @throws IOException if a read error occurs
-     */
-    public static int read(InputStream input, byte[] buffer, int offset, int length) throws IOException {
-        for (int remaining = length, curOffset = offset; remaining > 0; ) {
-            int count = input.read(buffer, curOffset, remaining);
-            if (count == (-1)) { // EOF before achieved required length
-                return curOffset - offset;
-            }
-
-            remaining -= count;
-            curOffset += count;
-        }
-
-        return length;
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/common/util/OsUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/OsUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/OsUtils.java
index f693a8e..08f1d1b 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/OsUtils.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/OsUtils.java
@@ -23,7 +23,7 @@ package org.apache.sshd.common.util;
  *
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
-public class OsUtils {
+public final class OsUtils {
     private static final boolean win32;
 
     static {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/common/util/SecurityUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/SecurityUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/SecurityUtils.java
index 1bb460f..f23029d 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/SecurityUtils.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/SecurityUtils.java
@@ -39,7 +39,6 @@ import org.slf4j.LoggerFactory;
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
 public class SecurityUtils {
-
     public static final String BOUNCY_CASTLE = "BC";
 
     private static final Logger LOG = LoggerFactory.getLogger(SecurityUtils.class);
@@ -49,6 +48,10 @@ public class SecurityUtils {
     private static boolean registrationDone;
     private static Boolean hasEcc;
 
+    private SecurityUtils() {
+        throw new UnsupportedOperationException("No instance");
+    }
+
     public static boolean hasEcc() {
         if (hasEcc == null) {
             try {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/common/util/Transformer.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/Transformer.java b/sshd-core/src/main/java/org/apache/sshd/common/util/Transformer.java
new file mode 100644
index 0000000..19ba9c4
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/Transformer.java
@@ -0,0 +1,58 @@
+/*
+ * 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.util;
+
+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/util/ValidateUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/ValidateUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/ValidateUtils.java
index 9e270ab..78e1fc1 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/ValidateUtils.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/ValidateUtils.java
@@ -25,7 +25,11 @@ import java.util.Map;
 /**
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
-public class ValidateUtils {
+public final class ValidateUtils {
+    private ValidateUtils() {
+        throw new UnsupportedOperationException("No instance");
+    }
+
     public static final <T> T checkNotNull(T t, String message, Object ... args) {
         checkTrue(t != null, message, args);
         return t;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/Buffer.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/Buffer.java b/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/Buffer.java
index 74ff9df..784daa7 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/Buffer.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/Buffer.java
@@ -149,10 +149,6 @@ public abstract class Buffer implements Readable {
 
     public abstract String getString(Charset charset);
 
-    public byte[] getStringAsBytes() {
-        return getBytes();
-    }
-
     public BigInteger getMPInt() {
         return new BigInteger(getMPIntAsBytes());
     }
@@ -189,42 +185,47 @@ public abstract class Buffer implements Readable {
 
     public PublicKey getRawPublicKey() throws SshException {
         try {
-            PublicKey key;
             String keyAlg = getString();
             if (KeyPairProvider.SSH_RSA.equals(keyAlg)) {
                 BigInteger e = getMPInt();
                 BigInteger n = getMPInt();
                 KeyFactory keyFactory = SecurityUtils.getKeyFactory("RSA");
-                key = keyFactory.generatePublic(new RSAPublicKeySpec(n, e));
+                return keyFactory.generatePublic(new RSAPublicKeySpec(n, e));
             } else if (KeyPairProvider.SSH_DSS.equals(keyAlg)) {
                 BigInteger p = getMPInt();
                 BigInteger q = getMPInt();
                 BigInteger g = getMPInt();
                 BigInteger y = getMPInt();
                 KeyFactory keyFactory = SecurityUtils.getKeyFactory("DSA");
-                key = keyFactory.generatePublic(new DSAPublicKeySpec(y, p, q, g));
-            } else if (KeyPairProvider.ECDSA_SHA2_NISTP256.equals(keyAlg)) {
-                key = getRawECKey("nistp256", ECCurves.EllipticCurves.nistp256);
-            } else if (KeyPairProvider.ECDSA_SHA2_NISTP384.equals(keyAlg)) {
-                key = getRawECKey("nistp384", ECCurves.EllipticCurves.nistp384);
-            } else if (KeyPairProvider.ECDSA_SHA2_NISTP521.equals(keyAlg)) {
-                key = getRawECKey("nistp521", ECCurves.EllipticCurves.nistp521);
+                return keyFactory.generatePublic(new DSAPublicKeySpec(y, p, q, g));
+            } else if (keyAlg.startsWith(ECCurves.ECDSA_SHA2_PREFIX)) {
+                String curveName = keyAlg.substring(ECCurves.ECDSA_SHA2_PREFIX.length());
+                ECParameterSpec params = ECCurves.getECParameterSpec(curveName);
+                return getRawECKey(curveName, params);
             } else {
                 throw new NoSuchAlgorithmException("Unsupported raw public algorithm: " + keyAlg);
             }
-            return key;
         } catch (GeneralSecurityException e) {
             throw new SshException(e);
         }
     }
 
-    protected PublicKey getRawECKey(String expectedCurve, ECParameterSpec spec) throws GeneralSecurityException, SshException {
+    protected PublicKey getRawECKey(String expectedCurve, ECParameterSpec spec) throws GeneralSecurityException {
         String curveName = getString();
         if (!expectedCurve.equals(curveName)) {
-            throw new InvalidKeySpecException("Curve name does not match expected: " + curveName + " vs "
-                    + expectedCurve);
+            throw new InvalidKeySpecException("getRawECKey(" + expectedCurve + ") curve name does not match expected: " + curveName);
+        }
+
+        if (spec == null) {
+            throw new InvalidKeySpecException("getRawECKey(" + expectedCurve + ") missing curve parameters");
+        }
+
+        byte[] octets = getBytes();
+        ECPoint w = ECCurves.decodeECPoint(octets, spec.getCurve());
+        if (w == null) {
+            throw new InvalidKeySpecException("getRawECKey(" + expectedCurve + ") cannot retrieve W value");
         }
-        ECPoint w = ECCurves.decodeECPoint(getStringAsBytes(), spec.getCurve());
+
         KeyFactory keyFactory = SecurityUtils.getKeyFactory("EC");
         return keyFactory.generatePublic(new ECPublicKeySpec(w, spec));
     }
@@ -255,12 +256,10 @@ public abstract class Buffer implements Readable {
                 KeyFactory keyFactory = SecurityUtils.getKeyFactory("DSA");
                 pub = keyFactory.generatePublic(new DSAPublicKeySpec(y, p, q, g));
                 prv = keyFactory.generatePrivate(new DSAPrivateKeySpec(x, p, q, g));
-            } else if (KeyPairProvider.ECDSA_SHA2_NISTP256.equals(keyAlg)) {
-                return extractEC("nistp256", ECCurves.EllipticCurves.nistp256);
-            } else if (KeyPairProvider.ECDSA_SHA2_NISTP384.equals(keyAlg)) {
-                return extractEC("nistp384", ECCurves.EllipticCurves.nistp384);
-            } else if (KeyPairProvider.ECDSA_SHA2_NISTP521.equals(keyAlg)) {
-                return extractEC("nistp521", ECCurves.EllipticCurves.nistp521);
+            } else if (keyAlg.startsWith(ECCurves.ECDSA_SHA2_PREFIX)) {
+                String curveName = keyAlg.substring(ECCurves.ECDSA_SHA2_PREFIX.length());
+                ECParameterSpec params = ECCurves.getECParameterSpec(curveName);
+                return extractEC(curveName, params);
             } else {
                 throw new NoSuchAlgorithmException("Unsupported key pair algorithm: " + keyAlg);
             }
@@ -270,18 +269,22 @@ public abstract class Buffer implements Readable {
         }
     }
 
-    protected KeyPair extractEC(String expectedCurveName, ECParameterSpec spec) throws GeneralSecurityException, SshException {
+    protected KeyPair extractEC(String expectedCurveName, ECParameterSpec spec) throws GeneralSecurityException {
         String curveName = getString();
-        byte[] groupBytes = getStringAsBytes();
+        if (!expectedCurveName.equals(curveName)) {
+            throw new InvalidKeySpecException("extractEC(" + expectedCurveName + ") mismatched curve name: " + curveName);
+        }
+
+        byte[] groupBytes = getBytes();
         BigInteger exponent = getMPInt();
 
-        if (!expectedCurveName.equals(curveName)) {
-            throw new SshException("Expected curve " + expectedCurveName + " but was " + curveName);
+        if (spec == null) {
+            throw new InvalidKeySpecException("extractEC(" + expectedCurveName + ") missing parameters for curve");
         }
 
         ECPoint group = ECCurves.decodeECPoint(groupBytes, spec.getCurve());
         if (group == null) {
-            throw new InvalidKeySpecException("Couldn't decode EC group");
+            throw new InvalidKeySpecException("extractEC(" + expectedCurveName + ") couldn't decode EC group for curve");
         }
 
         KeyFactory keyFactory = SecurityUtils.getKeyFactory("EC");

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/common/util/io/CloseableEmptyInputStream.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/io/CloseableEmptyInputStream.java b/sshd-core/src/main/java/org/apache/sshd/common/util/io/CloseableEmptyInputStream.java
new file mode 100644
index 0000000..ecb0144
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/io/CloseableEmptyInputStream.java
@@ -0,0 +1,94 @@
+/*
+ * 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.util.io;
+
+import java.io.IOException;
+import java.nio.channels.Channel;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * A {@code /dev/null} stream that can be closed - in which case it will throw
+ * {@link IOException}s if invoked after being closed
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class CloseableEmptyInputStream extends EmptyInputStream implements Channel {
+    private final AtomicBoolean open = new AtomicBoolean(true);
+    
+    public CloseableEmptyInputStream() {
+        super();
+    }
+
+    @Override
+    public boolean isOpen() {
+        return open.get();
+    }
+
+    @Override
+    public int available() throws IOException {
+        if (isOpen()) {
+            return super.available();
+        } else {
+            throw new IOException("available() stream is closed");
+        }
+    }
+
+    @Override
+    public int read() throws IOException {
+        if (isOpen()) {
+            return super.read();
+        } else {
+            throw new IOException("read() stream is closed");
+        }
+    }
+
+    @Override
+    public int read(byte[] b, int off, int len) throws IOException {
+        if (isOpen()) {
+            return super.read(b, off, len);
+        } else {
+            throw new IOException("read([])[" + off + "," + len + "] stream is closed");
+        }
+    }
+
+    @Override
+    public long skip(long n) throws IOException {
+        if (isOpen()) {
+            return super.skip(n);
+        } else {
+            throw new IOException("skip(" + n + ") stream is closed");
+        }
+    }
+
+    @Override
+    public synchronized void reset() throws IOException {
+        if (isOpen()) {
+            super.reset();
+        } else {
+            throw new IOException("reset() stream is closed");
+        }
+    }
+
+    @Override
+    public void close() throws IOException {
+        if (open.getAndSet(false)) {
+            return; // debug breakpoint
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6432a7af/sshd-core/src/main/java/org/apache/sshd/common/util/io/EmptyInputStream.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/io/EmptyInputStream.java b/sshd-core/src/main/java/org/apache/sshd/common/util/io/EmptyInputStream.java
new file mode 100644
index 0000000..1047a5f
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/io/EmptyInputStream.java
@@ -0,0 +1,65 @@
+/*
+ * 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.util.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * A {@code /dev/null} implementation - always open
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class EmptyInputStream extends InputStream {
+    public static final EmptyInputStream DEV_NULL = new EmptyInputStream();
+
+    public EmptyInputStream() {
+        super();
+    }
+
+    @Override
+    public int read() throws IOException {
+        return (-1);
+    }
+
+    @Override
+    public int read(byte[] b, int off, int len) throws IOException {
+        return (-1);
+    }
+
+    @Override
+    public long skip(long n) throws IOException {
+        return 0L;
+    }
+
+    @Override
+    public int available() throws IOException {
+        return 0;
+    }
+
+    @Override
+    public synchronized void mark(int readlimit) {
+        throw new UnsupportedOperationException("mark(" + readlimit + ") called despite the fact that markSupported=" + markSupported());
+    }
+
+    @Override
+    public synchronized void reset() throws IOException {
+        // ignored
+    }
+}