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

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

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
+    }
+}