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/12/01 08:05:49 UTC

[2/3] mina-sshd git commit: [SSHD-533] Add support for SHA-224 (builtin) digest

[SSHD-533] Add support for SHA-224 (builtin) digest


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

Branch: refs/heads/master
Commit: 7b18b090347ef95ace18679ed71d76dbd549a756
Parents: 5fd4fba
Author: Lyor Goldstein <lg...@vmware.com>
Authored: Tue Dec 1 09:04:11 2015 +0200
Committer: Lyor Goldstein <lg...@vmware.com>
Committed: Tue Dec 1 09:04:11 2015 +0200

----------------------------------------------------------------------
 .../org/apache/sshd/client/kex/DHGEXClient.java |   4 +-
 .../subsystem/sftp/SftpVersionSelector.java     |   3 +-
 .../impl/AbstractMD5HashExtension.java          |   3 +-
 .../extensions/impl/CopyDataExtensionImpl.java  |   6 +-
 .../openssh/impl/OpenSSHFsyncExtensionImpl.java |   4 +-
 .../org/apache/sshd/common/OptionalFeature.java |  84 +++-
 .../apache/sshd/common/cipher/BaseCipher.java   |   4 +-
 .../apache/sshd/common/cipher/CipherNone.java   |   4 +-
 .../org/apache/sshd/common/cipher/ECCurves.java |  35 +-
 .../keys/AbstractPublicKeyEntryDecoder.java     |   3 +-
 .../config/keys/ECDSAPublicKeyEntryDecoder.java |   6 +-
 .../sshd/common/config/keys/KeyUtils.java       |  15 +-
 .../sshd/common/config/keys/PublicKeyEntry.java |   5 +-
 .../apache/sshd/common/digest/BaseDigest.java   |  14 +-
 .../sshd/common/digest/BuiltinDigests.java      |  25 +-
 .../sshd/common/digest/DigestFactory.java       |  10 +-
 .../apache/sshd/common/digest/DigestUtils.java  |   5 +-
 .../org/apache/sshd/common/kex/AbstractDH.java  |   4 +-
 .../sshd/common/kex/BuiltinDHFactories.java     |  14 +-
 .../java/org/apache/sshd/common/kex/DHG.java    |   6 +-
 .../org/apache/sshd/common/mac/BuiltinMacs.java |   5 +-
 .../org/apache/sshd/common/scp/ScpHelper.java   |   2 +-
 .../sshd/common/session/AbstractSession.java    |   3 +-
 .../common/signature/AbstractSignature.java     |   6 +-
 .../common/signature/BuiltinSignatures.java     |   5 +-
 .../sshd/common/signature/SignatureDSA.java     |   6 +-
 .../sftp/extensions/AbstractParser.java         |   4 +-
 .../org/apache/sshd/common/util/Base64.java     |   4 +-
 .../sshd/common/util/DirectoryScanner.java      | 380 ------------------
 .../apache/sshd/common/util/GenericUtils.java   |  38 --
 .../apache/sshd/common/util/NumberUtils.java    | 149 +++++++-
 .../org/apache/sshd/common/util/OsUtils.java    |  90 ++++-
 .../apache/sshd/common/util/SecurityUtils.java  |  11 +
 .../apache/sshd/common/util/ValidateUtils.java  |   8 +-
 .../apache/sshd/common/util/VersionInfo.java    | 137 +++++++
 .../sshd/common/util/buffer/BufferUtils.java    |  25 +-
 .../apache/sshd/common/util/io/DERParser.java   |   4 +-
 .../apache/sshd/common/util/io/DERWriter.java   |   4 +-
 .../sshd/common/util/io/DirectoryScanner.java   | 382 +++++++++++++++++++
 .../sshd/server/auth/gss/UserAuthGSS.java       |   4 +-
 .../server/subsystem/sftp/SftpSubsystem.java    |  76 +++-
 .../sshd/client/subsystem/sftp/SftpTest.java    |  22 +-
 .../impl/AbstractCheckFileExtensionTest.java    |  15 +-
 .../impl/AbstractMD5HashExtensionTest.java      |   7 +
 .../keys/KeyUtilsFingerprintGenerationTest.java |  14 +-
 .../sshd/common/config/keys/KeyUtilsTest.java   |  34 +-
 .../sshd/common/digest/BuiltinDigestsTest.java  |  62 +++
 .../apache/sshd/common/util/OsUtilsTest.java    | 132 +++++++
 .../sshd/common/util/SecurityUtilsTest.java     |  31 ++
 .../sshd/common/util/VersionInfoTest.java       |  49 +++
 .../apache/sshd/common/util/io/IoUtilsTest.java |   4 +-
 51 files changed, 1386 insertions(+), 581 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7b18b090/sshd-core/src/main/java/org/apache/sshd/client/kex/DHGEXClient.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/kex/DHGEXClient.java b/sshd-core/src/main/java/org/apache/sshd/client/kex/DHGEXClient.java
index cff00d8..8548dff 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/kex/DHGEXClient.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/kex/DHGEXClient.java
@@ -84,7 +84,9 @@ public class DHGEXClient extends AbstractDHClientKeyExchange {
     @Override
     public void init(AbstractSession s, byte[] v_s, byte[] v_c, byte[] i_s, byte[] i_c) throws Exception {
         super.init(s, v_s, v_c, i_s, i_c);
-        log.debug("Send SSH_MSG_KEX_DH_GEX_REQUEST");
+        if (log.isDebugEnabled()) {
+            log.debug("init({}) Send SSH_MSG_KEX_DH_GEX_REQUEST", s);
+        }
         Buffer buffer = s.createBuffer(SshConstants.SSH_MSG_KEX_DH_GEX_REQUEST);
         buffer.putInt(min);
         buffer.putInt(prf);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7b18b090/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpVersionSelector.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpVersionSelector.java b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpVersionSelector.java
index 240950e..ccdf3ce 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpVersionSelector.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpVersionSelector.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.NumberUtils;
 import org.apache.sshd.common.util.ValidateUtils;
 
 /**
@@ -124,7 +125,7 @@ public interface SftpVersionSelector {
          * the most preferred version that is also listed as available.
          */
         public static SftpVersionSelector preferredVersionSelector(final int ... preferred) {
-            return preferredVersionSelector(GenericUtils.asList(preferred));
+            return preferredVersionSelector(NumberUtils.asList(preferred));
 
         }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7b18b090/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/impl/AbstractMD5HashExtension.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/impl/AbstractMD5HashExtension.java b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/impl/AbstractMD5HashExtension.java
index 61acb73..5548c65 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/impl/AbstractMD5HashExtension.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/impl/AbstractMD5HashExtension.java
@@ -26,6 +26,7 @@ import java.util.Collection;
 import org.apache.sshd.client.subsystem.sftp.RawSftpClient;
 import org.apache.sshd.client.subsystem.sftp.SftpClient;
 import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.NumberUtils;
 import org.apache.sshd.common.util.buffer.Buffer;
 import org.apache.sshd.common.util.buffer.BufferUtils;
 
@@ -38,7 +39,7 @@ public abstract class AbstractMD5HashExtension extends AbstractSftpClientExtensi
     }
 
     protected byte[] doGetHash(Object target, long offset, long length, byte[] quickHash) throws IOException {
-        Buffer buffer = getCommandBuffer(target, Long.SIZE + 2 * (Long.SIZE / Byte.SIZE) + (Integer.SIZE / Byte.SIZE) + GenericUtils.length(quickHash));
+        Buffer buffer = getCommandBuffer(target, Long.SIZE + 2 * (Long.SIZE / Byte.SIZE) + (Integer.SIZE / Byte.SIZE) + NumberUtils.length(quickHash));
         String opcode = getName();
         putTarget(buffer, target);
         buffer.putLong(offset);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7b18b090/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/impl/CopyDataExtensionImpl.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/impl/CopyDataExtensionImpl.java b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/impl/CopyDataExtensionImpl.java
index 0bb19d6..0d4ad89 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/impl/CopyDataExtensionImpl.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/impl/CopyDataExtensionImpl.java
@@ -27,7 +27,7 @@ import org.apache.sshd.client.subsystem.sftp.SftpClient;
 import org.apache.sshd.client.subsystem.sftp.SftpClient.Handle;
 import org.apache.sshd.client.subsystem.sftp.extensions.CopyDataExtension;
 import org.apache.sshd.common.subsystem.sftp.SftpConstants;
-import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.NumberUtils;
 import org.apache.sshd.common.util.buffer.Buffer;
 
 /**
@@ -42,8 +42,8 @@ public class CopyDataExtensionImpl extends AbstractSftpClientExtension implement
     public void copyData(Handle readHandle, long readOffset, long readLength, Handle writeHandle, long writeOffset) throws IOException {
         byte[] srcId = readHandle.getIdentifier();
         byte[] dstId = writeHandle.getIdentifier();
-        Buffer buffer = getCommandBuffer((Integer.SIZE / Byte.SIZE) + GenericUtils.length(srcId)
-                + (Integer.SIZE / Byte.SIZE) + GenericUtils.length(dstId)
+        Buffer buffer = getCommandBuffer((Integer.SIZE / Byte.SIZE) + NumberUtils.length(srcId)
+                + (Integer.SIZE / Byte.SIZE) + NumberUtils.length(dstId)
                 + (3 * (Long.SIZE + (Integer.SIZE / Byte.SIZE))));
         buffer.putBytes(srcId);
         buffer.putLong(readOffset);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7b18b090/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/openssh/impl/OpenSSHFsyncExtensionImpl.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/openssh/impl/OpenSSHFsyncExtensionImpl.java b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/openssh/impl/OpenSSHFsyncExtensionImpl.java
index 8c70880..c650c78 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/openssh/impl/OpenSSHFsyncExtensionImpl.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/openssh/impl/OpenSSHFsyncExtensionImpl.java
@@ -28,7 +28,7 @@ import org.apache.sshd.client.subsystem.sftp.SftpClient.Handle;
 import org.apache.sshd.client.subsystem.sftp.extensions.impl.AbstractSftpClientExtension;
 import org.apache.sshd.client.subsystem.sftp.extensions.openssh.OpenSSHFsyncExtension;
 import org.apache.sshd.common.subsystem.sftp.extensions.openssh.FsyncExtensionParser;
-import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.NumberUtils;
 import org.apache.sshd.common.util.buffer.Buffer;
 
 /**
@@ -42,7 +42,7 @@ public class OpenSSHFsyncExtensionImpl extends AbstractSftpClientExtension imple
     @Override
     public void fsync(Handle fileHandle) throws IOException {
         byte[] handle = fileHandle.getIdentifier();
-        Buffer buffer = getCommandBuffer((Integer.SIZE / Byte.SIZE) + GenericUtils.length(handle));
+        Buffer buffer = getCommandBuffer((Integer.SIZE / Byte.SIZE) + NumberUtils.length(handle));
         buffer.putBytes(handle);
         sendAndCheckExtendedCommandStatus(buffer);
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7b18b090/sshd-core/src/main/java/org/apache/sshd/common/OptionalFeature.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/OptionalFeature.java b/sshd-core/src/main/java/org/apache/sshd/common/OptionalFeature.java
index 9c5e396..43497eb 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/OptionalFeature.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/OptionalFeature.java
@@ -19,9 +19,91 @@
 
 package org.apache.sshd.common;
 
+import java.util.Collection;
+
+import org.apache.sshd.common.util.GenericUtils;
+
 /**
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
-public interface OptionalFeature {
+public interface OptionalFeature {  // TODO define this as a @FunctionalInterface in Java 8
+    OptionalFeature TRUE = new OptionalFeature() {
+        @Override
+        public boolean isSupported() {
+            return true;
+        }
+
+        @Override
+        public String toString() {
+            return "TRUE";
+        }
+    };
+
+    OptionalFeature FALSE = new OptionalFeature() {
+        @Override
+        public boolean isSupported() {
+            return false;
+        }
+
+        @Override
+        public String toString() {
+            return "FALSE";
+        }
+    };
+
     boolean isSupported();
+
+    /**
+     * Utility class to help using {@link OptionalFeature}s
+     */
+    // CHECKSTYLE:OFF
+    final class Utils {
+    // CHECKSTYLE:ON
+
+        private Utils() {
+            throw new UnsupportedOperationException("No instance allowed");
+        }
+
+        public static OptionalFeature of(boolean supported) {
+            return supported ? TRUE : FALSE;
+        }
+
+        public static OptionalFeature all(final Collection<? extends OptionalFeature> features) {
+            return new OptionalFeature() {
+                @Override
+                public boolean isSupported() {
+                    if (GenericUtils.isEmpty(features)) {
+                        return false;
+                    }
+
+                    for (OptionalFeature f : features) {
+                        if (!f.isSupported()) {
+                            return false;
+                        }
+                    }
+
+                    return true;
+                }
+            };
+        }
+
+        public static OptionalFeature any(final Collection<? extends OptionalFeature> features) {
+            return new OptionalFeature() {
+                @Override
+                public boolean isSupported() {
+                    if (GenericUtils.isEmpty(features)) {
+                        return false;
+                    }
+
+                    for (OptionalFeature f : features) {
+                        if (f.isSupported()) {
+                            return true;
+                        }
+                    }
+
+                    return false;
+                }
+            };
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7b18b090/sshd-core/src/main/java/org/apache/sshd/common/cipher/BaseCipher.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/cipher/BaseCipher.java b/sshd-core/src/main/java/org/apache/sshd/common/cipher/BaseCipher.java
index c6b534b..381825d 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/cipher/BaseCipher.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/cipher/BaseCipher.java
@@ -22,7 +22,7 @@ import javax.crypto.spec.IvParameterSpec;
 import javax.crypto.spec.SecretKeySpec;
 
 import org.apache.sshd.common.SshException;
-import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.NumberUtils;
 import org.apache.sshd.common.util.SecurityUtils;
 import org.apache.sshd.common.util.ValidateUtils;
 
@@ -83,7 +83,7 @@ public class BaseCipher implements Cipher {
 
     @Override
     public void update(byte[] input) throws Exception {
-        update(input, 0, GenericUtils.length(input));
+        update(input, 0, NumberUtils.length(input));
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7b18b090/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherNone.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherNone.java b/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherNone.java
index 7f2c863..616e305 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherNone.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherNone.java
@@ -18,7 +18,7 @@
  */
 package org.apache.sshd.common.cipher;
 
-import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.NumberUtils;
 
 
 /**
@@ -61,7 +61,7 @@ public class CipherNone implements Cipher {
 
     @Override
     public void update(byte[] input) throws Exception {
-        update(input, 0, GenericUtils.length(input));
+        update(input, 0, NumberUtils.length(input));
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7b18b090/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 6855ed9..34587b7 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
@@ -33,6 +33,7 @@ import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.OptionalFeature;
 import org.apache.sshd.common.digest.BuiltinDigests;
 import org.apache.sshd.common.digest.Digest;
+import org.apache.sshd.common.digest.DigestFactory;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.SecurityUtils;
 import org.apache.sshd.common.util.ValidateUtils;
@@ -54,12 +55,8 @@ public enum ECCurves implements NamedResource, OptionalFeature {
                             new BigInteger("4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5", 16)),
                     new BigInteger("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551", 16),
                     1),
-            32) {
-        @Override
-        public Digest getDigestForParams() {
-            return BuiltinDigests.sha256.create();
-        }
-    },
+            32,
+            BuiltinDigests.sha256),
     nistp384(Constants.NISTP384,
             new ECParameterSpec(
                     new EllipticCurve(
@@ -71,12 +68,8 @@ public enum ECCurves implements NamedResource, OptionalFeature {
                             new BigInteger("3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F", 16)),
                     new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973", 16),
                     1),
-            48) {
-        @Override
-        public Digest getDigestForParams() {
-            return BuiltinDigests.sha384.create();
-        }
-    },
+            48,
+            BuiltinDigests.sha384),
     nistp521(Constants.NISTP521,
             new ECParameterSpec(
                     new EllipticCurve(
@@ -94,12 +87,8 @@ public enum ECCurves implements NamedResource, OptionalFeature {
                     new BigInteger("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B"
                                     + "7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409", 16),
                     1),
-            66) {
-        @Override
-        public Digest getDigestForParams() {
-            return BuiltinDigests.sha512.create();
-        }
-    };
+            66,
+            BuiltinDigests.sha512);
 
     /**
      * A {@link Set} of all the known curves
@@ -140,13 +129,15 @@ public enum ECCurves implements NamedResource, OptionalFeature {
     private final ECParameterSpec params;
     private final int keySize;
     private final int numOctets;
+    private final DigestFactory digestFactory;
 
-    ECCurves(String name, ECParameterSpec params, int numOctets) {
+    ECCurves(String name, ECParameterSpec params, int numOctets, DigestFactory digestFactory) {
         this.name = ValidateUtils.checkNotNullAndNotEmpty(name, "No curve name");
         this.keyType = Constants.ECDSA_SHA2_PREFIX + name;
         this.params = ValidateUtils.checkNotNull(params, "No EC params for %s", name);
         this.keySize = getCurveSize(params);
         this.numOctets = numOctets;
+        this.digestFactory = ValidateUtils.checkNotNull(digestFactory, "No digestFactory");
     }
 
     @Override   // The curve name
@@ -163,7 +154,7 @@ public enum ECCurves implements NamedResource, OptionalFeature {
 
     @Override
     public final boolean isSupported() {
-        return SecurityUtils.hasEcc();
+        return SecurityUtils.hasEcc() && digestFactory.isSupported();
     }
 
     public final ECParameterSpec getParameters() {
@@ -187,7 +178,9 @@ public enum ECCurves implements NamedResource, OptionalFeature {
     /**
      * @return The {@link Digest} to use when hashing the curve's parameters
      */
-    public abstract Digest getDigestForParams();
+    public final Digest getDigestForParams() {
+        return digestFactory.create();
+    }
 
     /**
      * @param type The key type value - ignored if {@code null}/empty

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7b18b090/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 1fb988b..e89a944 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
@@ -39,6 +39,7 @@ import java.security.spec.KeySpec;
 import java.util.Collection;
 
 import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.NumberUtils;
 import org.apache.sshd.common.util.ValidateUtils;
 import org.apache.sshd.common.util.io.IoUtils;
 
@@ -123,7 +124,7 @@ public abstract class AbstractPublicKeyEntryDecoder<PUB extends PublicKey, PRV e
 
     @Override
     public PUB decodePublicKey(byte... keyData) throws IOException, GeneralSecurityException {
-        return decodePublicKey(keyData, 0, GenericUtils.length(keyData));
+        return decodePublicKey(keyData, 0, NumberUtils.length(keyData));
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7b18b090/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 e2a0318..e85d600 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
@@ -44,7 +44,7 @@ import java.util.EnumSet;
 import java.util.Set;
 
 import org.apache.sshd.common.cipher.ECCurves;
-import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.NumberUtils;
 import org.apache.sshd.common.util.SecurityUtils;
 import org.apache.sshd.common.util.ValidateUtils;
 import org.apache.sshd.common.util.buffer.BufferUtils;
@@ -184,7 +184,7 @@ public class ECDSAPublicKeyEntryDecoder extends AbstractPublicKeyEntryDecoder<EC
     }
 
     public static ECPoint octetStringToEcPoint(byte... octets) {
-        if (GenericUtils.isEmpty(octets)) {
+        if (NumberUtils.isEmpty(octets)) {
             return null;
         }
 
@@ -204,7 +204,7 @@ public class ECDSAPublicKeyEntryDecoder extends AbstractPublicKeyEntryDecoder<EC
     }
 
     private static int findFirstNonZeroIndex(byte... octets) {
-        if (GenericUtils.isEmpty(octets)) {
+        if (NumberUtils.isEmpty(octets)) {
             return -1;
         }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7b18b090/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 be9e0e0..bac5a3a 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
@@ -98,9 +98,9 @@ public final class KeyUtils {
      * overridden by {@link #OPENSSH_KEY_FINGERPRINT_FACTORY_PROP} or
      * {@link #setDefaultFingerPrintFactory(Factory)}
      */
-    public static final Factory<Digest> DEFAULT_FINGERPRINT_DIGEST_FACTORY = BuiltinDigests.sha256;
+    public static final DigestFactory DEFAULT_FINGERPRINT_DIGEST_FACTORY = BuiltinDigests.sha256;
 
-    private static final AtomicReference<Factory<? extends Digest>> DEFAULT_DIGEST_HOLDER = new AtomicReference<>();
+    private static final AtomicReference<DigestFactory> DEFAULT_DIGEST_HOLDER = new AtomicReference<>();
 
     private static final Map<String, PublicKeyEntryDecoder<?, ?>> BY_KEY_TYPE_DECODERS_MAP =
             new TreeMap<String, PublicKeyEntryDecoder<?, ?>>(String.CASE_INSENSITIVE_ORDER);
@@ -366,14 +366,14 @@ public final class KeyUtils {
     }
 
     /**
-     * @return The default {@link Factory} of {@link Digest}s used
+     * @return The default {@link DigestFactory}
      * by the {@link #getFingerPrint(PublicKey)} and {@link #getFingerPrint(String)}
      * methods
      * @see #KEY_FINGERPRINT_FACTORY_PROP
      * @see #setDefaultFingerPrintFactory(Factory)
      */
-    public static Factory<? extends Digest> getDefaultFingerPrintFactory() {
-        Factory<? extends Digest> factory = null;
+    public static DigestFactory getDefaultFingerPrintFactory() {
+        DigestFactory factory = null;
         synchronized (DEFAULT_DIGEST_HOLDER) {
             factory = DEFAULT_DIGEST_HOLDER.get();
             if (factory != null) {
@@ -387,6 +387,7 @@ public final class KeyUtils {
                 factory = ValidateUtils.checkNotNull(BuiltinDigests.fromFactoryName(propVal), "Unknown digest factory: %s", propVal);
             }
 
+            ValidateUtils.checkTrue(factory.isSupported(), "Selected fingerprint digest not supported: %s", factory.getName());
             DEFAULT_DIGEST_HOLDER.set(factory);
         }
 
@@ -394,10 +395,10 @@ public final class KeyUtils {
     }
 
     /**
-     * @param f The {@link Factory} of {@link Digest}s to be used - may
+     * @param f The {@link DigestFactory} of {@link Digest}s to be used - may
      *          not be {@code null}
      */
-    public static void setDefaultFingerPrintFactory(Factory<? extends Digest> f) {
+    public static void setDefaultFingerPrintFactory(DigestFactory f) {
         synchronized (DEFAULT_DIGEST_HOLDER) {
             DEFAULT_DIGEST_HOLDER.set(ValidateUtils.checkNotNull(f, "No digest factory"));
         }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7b18b090/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 d2388ea..5ea14aa 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
@@ -32,6 +32,7 @@ import java.util.Objects;
 
 import org.apache.sshd.common.util.Base64;
 import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.NumberUtils;
 
 /**
  * <P>Represents a {@link PublicKey} whose data is formatted according to
@@ -161,7 +162,7 @@ public class PublicKeyEntry implements Serializable {
     @Override
     public String toString() {
         byte[] data = getKeyData();
-        return getKeyType() + " " + (GenericUtils.isEmpty(data) ? "<no-key>" : Base64.encodeToString(data));
+        return getKeyType() + " " + (NumberUtils.isEmpty(data) ? "<no-key>" : Base64.encodeToString(data));
     }
 
     /**
@@ -206,7 +207,7 @@ public class PublicKeyEntry implements Serializable {
         String keyType = data.substring(0, startPos);
         String b64Data = data.substring(startPos + 1, endPos).trim();
         byte[] keyData = Base64.decodeString(b64Data);
-        if (GenericUtils.isEmpty(keyData)) {
+        if (NumberUtils.isEmpty(keyData)) {
             throw new IllegalArgumentException("Bad format (no BASE64 key data): " + data);
         }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7b18b090/sshd-core/src/main/java/org/apache/sshd/common/digest/BaseDigest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/digest/BaseDigest.java b/sshd-core/src/main/java/org/apache/sshd/common/digest/BaseDigest.java
index 2553fd6..793f571 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/digest/BaseDigest.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/digest/BaseDigest.java
@@ -22,6 +22,7 @@ import java.security.MessageDigest;
 import java.util.Objects;
 
 import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.NumberUtils;
 import org.apache.sshd.common.util.SecurityUtils;
 import org.apache.sshd.common.util.ValidateUtils;
 
@@ -71,17 +72,24 @@ public class BaseDigest implements Digest {
 
     @Override
     public void update(byte[] data) throws Exception {
-        update(data, 0, GenericUtils.length(data));
+        update(data, 0, NumberUtils.length(data));
     }
 
     @Override
     public void update(byte[] data, int start, int len) throws Exception {
-        md.update(data, start, len);
+        ValidateUtils.checkNotNull(md, "Digest not initialized").update(data, start, len);
+    }
+
+    /**
+     * @return The current {@link MessageDigest} - may be {@code null} if {@link #init()} not called
+     */
+    protected MessageDigest getMessageDigest() {
+        return md;
     }
 
     @Override
     public byte[] digest() throws Exception {
-        return md.digest();
+        return ValidateUtils.checkNotNull(md, "Digest not initialized").digest();
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7b18b090/sshd-core/src/main/java/org/apache/sshd/common/digest/BuiltinDigests.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/digest/BuiltinDigests.java b/sshd-core/src/main/java/org/apache/sshd/common/digest/BuiltinDigests.java
index cf61a78..19cd28c 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/digest/BuiltinDigests.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/digest/BuiltinDigests.java
@@ -26,15 +26,30 @@ import java.util.Set;
 import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.OsUtils;
+import org.apache.sshd.common.util.SecurityUtils;
+import org.apache.sshd.common.util.VersionInfo;
 
 /**
  * Provides easy access to the currently implemented digests
  *
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
-public enum BuiltinDigests implements DigestInformation, DigestFactory {
+public enum BuiltinDigests implements DigestFactory {
     md5(Constants.MD5, "MD5", 16),
     sha1(Constants.SHA1, "SHA-1", 20),
+    sha224(Constants.SHA224, "SHA-224", 28) {
+        @Override
+        public boolean isSupported() {
+            if (SecurityUtils.isBouncyCastleRegistered()) {
+                return true;
+            }
+
+            // SHA-224 was introduced in Java-8
+            VersionInfo version = OsUtils.getJavaVersion();
+            return version.getMinorVersion() >= 8;
+        }
+    },
     sha256(Constants.SHA256, "SHA-256", 32),
     sha384(Constants.SHA384, "SHA-384", 48),
     sha512(Constants.SHA512, "SHA-512", 64);
@@ -77,6 +92,11 @@ public enum BuiltinDigests implements DigestInformation, DigestFactory {
         return new BaseDigest(getAlgorithm(), getBlockSize());
     }
 
+    @Override
+    public boolean isSupported() {
+        return true;
+    }
+
     /**
      * @param s The {@link Enum}'s name - ignored if {@code null}/empty
      * @return The matching {@link org.apache.sshd.common.digest.BuiltinDigests} whose {@link Enum#name()} matches
@@ -102,7 +122,7 @@ public enum BuiltinDigests implements DigestInformation, DigestFactory {
      * (case <U>insensitive</U>) the digest factory name
      * @see #fromFactoryName(String)
      */
-    public static BuiltinDigests fromFactory(NamedFactory<Digest> factory) {
+    public static BuiltinDigests fromFactory(NamedFactory<? extends Digest> factory) {
         if (factory == null) {
             return null;
         } else {
@@ -140,6 +160,7 @@ public enum BuiltinDigests implements DigestInformation, DigestFactory {
     public static final class Constants {
         public static final String MD5 = "md5";
         public static final String SHA1 = "sha1";
+        public static final String SHA224 = "sha224";
         public static final String SHA256 = "sha256";
         public static final String SHA384 = "sha384";
         public static final String SHA512 = "sha512";

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7b18b090/sshd-core/src/main/java/org/apache/sshd/common/digest/DigestFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/digest/DigestFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/digest/DigestFactory.java
index 0bff18f..f00e7ba 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/digest/DigestFactory.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/digest/DigestFactory.java
@@ -20,13 +20,13 @@
 package org.apache.sshd.common.digest;
 
 import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.OptionalFeature;
 
 /**
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
-public interface DigestFactory extends NamedFactory<Digest> {
-    /**
-     * @return The underlying digest algorithm
-     */
-    String getAlgorithm();
+// CHECKSTYLE:OFF
+public interface DigestFactory extends DigestInformation, NamedFactory<Digest>, OptionalFeature {
+    // nothing extra
 }
+// CHECKSTYLE:ON

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7b18b090/sshd-core/src/main/java/org/apache/sshd/common/digest/DigestUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/digest/DigestUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/digest/DigestUtils.java
index 30fc9bb..fdf9adc 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/digest/DigestUtils.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/digest/DigestUtils.java
@@ -27,6 +27,7 @@ import java.util.Comparator;
 import org.apache.sshd.common.Factory;
 import org.apache.sshd.common.util.Base64;
 import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.NumberUtils;
 import org.apache.sshd.common.util.ValidateUtils;
 import org.apache.sshd.common.util.buffer.BufferUtils;
 
@@ -142,7 +143,7 @@ public final class DigestUtils {
      * @see #getFingerPrint(Factory, byte[], int, int)
      */
     public static String getFingerPrint(Factory<? extends Digest> f, byte... buf) throws Exception {
-        return getFingerPrint(f, buf, 0, GenericUtils.length(buf));
+        return getFingerPrint(f, buf, 0, NumberUtils.length(buf));
     }
 
     /**
@@ -165,7 +166,7 @@ public final class DigestUtils {
      * @see #getFingerPrint(Digest, byte[], int, int)
      */
     public static String getFingerPrint(Digest d, byte... buf) throws Exception {
-        return getFingerPrint(d, buf, 0, GenericUtils.length(buf));
+        return getFingerPrint(d, buf, 0, NumberUtils.length(buf));
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7b18b090/sshd-core/src/main/java/org/apache/sshd/common/kex/AbstractDH.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/kex/AbstractDH.java b/sshd-core/src/main/java/org/apache/sshd/common/kex/AbstractDH.java
index 087226b..251de67 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/kex/AbstractDH.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/kex/AbstractDH.java
@@ -21,7 +21,7 @@ package org.apache.sshd.common.kex;
 import java.math.BigInteger;
 
 import org.apache.sshd.common.digest.Digest;
-import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.NumberUtils;
 
 /**
  * Base class for the Diffie-Hellman key agreement.
@@ -63,7 +63,7 @@ public abstract class AbstractDH {
      * @see <A HREF="https://issues.apache.org/jira/browse/SSHD-330">SSHD-330</A>
      */
     public static byte[] stripLeadingZeroes(byte[] x) {
-        int length = GenericUtils.length(x);
+        int length = NumberUtils.length(x);
         for (int i = 0; i < x.length; i++) {
             if (x[i] == 0) {
                 continue;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7b18b090/sshd-core/src/main/java/org/apache/sshd/common/kex/BuiltinDHFactories.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/kex/BuiltinDHFactories.java b/sshd-core/src/main/java/org/apache/sshd/common/kex/BuiltinDHFactories.java
index 9428ee7..65af0b0 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/kex/BuiltinDHFactories.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/kex/BuiltinDHFactories.java
@@ -54,7 +54,7 @@ public enum BuiltinDHFactories implements DHFactory {
 
         @Override   // see https://tools.ietf.org/html/rfc4253#page-23
         public boolean isSupported() {
-            return SecurityUtils.isDHOakelyGroupSupported(1024);
+            return SecurityUtils.isDHOakelyGroupSupported(1024) && BuiltinDigests.sha1.isSupported();
         }
     },
     dhg14(Constants.DIFFIE_HELLMAN_GROUP14_SHA1) {
@@ -68,7 +68,7 @@ public enum BuiltinDHFactories implements DHFactory {
 
         @Override   // see https://tools.ietf.org/html/rfc4253#page-23
         public boolean isSupported() {
-            return SecurityUtils.isDHOakelyGroupSupported(2048);
+            return SecurityUtils.isDHOakelyGroupSupported(2048) && BuiltinDigests.sha1.isSupported();
         }
     },
     dhgex(Constants.DIFFIE_HELLMAN_GROUP_EXCHANGE_SHA1) {
@@ -89,7 +89,7 @@ public enum BuiltinDHFactories implements DHFactory {
 
         @Override
         public boolean isSupported() {  // avoid "Prime size must be multiple of 64, and can only range from 512 to 2048 (inclusive)"
-            return SecurityUtils.isDHGroupExchangeSupported();
+            return SecurityUtils.isDHGroupExchangeSupported() && BuiltinDigests.sha1.isSupported();
         }
     },
     dhgex256(Constants.DIFFIE_HELLMAN_GROUP_EXCHANGE_SHA256) {
@@ -105,7 +105,7 @@ public enum BuiltinDHFactories implements DHFactory {
 
         @Override
         public boolean isSupported() {  // avoid "Prime size must be multiple of 64, and can only range from 512 to 2048 (inclusive)"
-            return SecurityUtils.isDHGroupExchangeSupported();
+            return SecurityUtils.isDHGroupExchangeSupported() && BuiltinDigests.sha256.isSupported();
         }
 
         @Override
@@ -124,7 +124,7 @@ public enum BuiltinDHFactories implements DHFactory {
 
         @Override
         public boolean isSupported() {
-            return SecurityUtils.hasEcc();
+            return ECCurves.nistp256.isSupported();
         }
     },
     ecdhp384(Constants.ECDH_SHA2_NISTP384) {
@@ -138,7 +138,7 @@ public enum BuiltinDHFactories implements DHFactory {
 
         @Override
         public boolean isSupported() {
-            return SecurityUtils.hasEcc();
+            return ECCurves.nistp384.isSupported();
         }
     },
     ecdhp521(Constants.ECDH_SHA2_NISTP521) {
@@ -152,7 +152,7 @@ public enum BuiltinDHFactories implements DHFactory {
 
         @Override
         public boolean isSupported() {
-            return SecurityUtils.hasEcc();
+            return ECCurves.nistp521.isSupported();
         }
     };
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7b18b090/sshd-core/src/main/java/org/apache/sshd/common/kex/DHG.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/kex/DHG.java b/sshd-core/src/main/java/org/apache/sshd/common/kex/DHG.java
index 025fe46..6a7042f 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/kex/DHG.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/kex/DHG.java
@@ -45,13 +45,13 @@ public class DHG extends AbstractDH {
     private BigInteger f;  // your public key
     private KeyPairGenerator myKpairGen;
     private KeyAgreement myKeyAgree;
-    private Factory<Digest> factory;
+    private Factory<? extends Digest> factory;
 
-    public DHG(Factory<Digest> digestFactory) throws Exception {
+    public DHG(Factory<? extends Digest> digestFactory) throws Exception {
         this(digestFactory, null, null);
     }
 
-    public DHG(Factory<Digest> digestFactory, BigInteger pValue, BigInteger gValue) throws Exception {
+    public DHG(Factory<? extends Digest> digestFactory, BigInteger pValue, BigInteger gValue) throws Exception {
         myKpairGen = SecurityUtils.getKeyPairGenerator("DH");
         myKeyAgree = SecurityUtils.getKeyAgreement("DH");
         factory = digestFactory;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7b18b090/sshd-core/src/main/java/org/apache/sshd/common/mac/BuiltinMacs.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/mac/BuiltinMacs.java b/sshd-core/src/main/java/org/apache/sshd/common/mac/BuiltinMacs.java
index 8f55619..b501d69 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/mac/BuiltinMacs.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/mac/BuiltinMacs.java
@@ -33,7 +33,6 @@ import java.util.TreeMap;
 import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.config.NamedFactoriesListParseResult;
-import org.apache.sshd.common.digest.Digest;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.ValidateUtils;
 
@@ -173,12 +172,12 @@ public enum BuiltinMacs implements MacFactory {
     }
 
     /**
-     * @param factory The {@link org.apache.sshd.common.NamedFactory} for the Mac - ignored if {@code null}
+     * @param factory The {@link org.apache.sshd.common.NamedFactory} for the MAC - ignored if {@code null}
      * @return The matching {@link org.apache.sshd.common.mac.BuiltinMacs} whose factory name matches
      * (case <U>insensitive</U>) the digest factory name
      * @see #fromFactoryName(String)
      */
-    public static BuiltinMacs fromFactory(NamedFactory<Digest> factory) {
+    public static BuiltinMacs fromFactory(NamedFactory<Mac> factory) {
         if (factory == null) {
             return null;
         } else {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7b18b090/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 b9fe6b7..988ef32 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
@@ -45,9 +45,9 @@ 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.DirectoryScanner;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.SelectorUtils;
+import org.apache.sshd.common.util.io.DirectoryScanner;
 import org.apache.sshd.common.util.io.IoUtils;
 import org.apache.sshd.common.util.io.LimitInputStream;
 import org.apache.sshd.common.util.logging.AbstractLoggingBean;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7b18b090/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 71a3893..b7559ec 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
@@ -63,6 +63,7 @@ import org.apache.sshd.common.mac.Mac;
 import org.apache.sshd.common.random.Random;
 import org.apache.sshd.common.util.EventListenerUtils;
 import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.NumberUtils;
 import org.apache.sshd.common.util.Pair;
 import org.apache.sshd.common.util.Readable;
 import org.apache.sshd.common.util.ValidateUtils;
@@ -283,7 +284,7 @@ public abstract class AbstractSession extends AbstractKexFactoryManager implemen
     @Override
     public byte[] getSessionId() {
         // return a clone to avoid anyone changing the internal value
-        return GenericUtils.isEmpty(sessionId) ? sessionId : sessionId.clone();
+        return NumberUtils.isEmpty(sessionId) ? sessionId : sessionId.clone();
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7b18b090/sshd-core/src/main/java/org/apache/sshd/common/signature/AbstractSignature.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/signature/AbstractSignature.java b/sshd-core/src/main/java/org/apache/sshd/common/signature/AbstractSignature.java
index a33204a..b033465 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/signature/AbstractSignature.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/signature/AbstractSignature.java
@@ -22,7 +22,7 @@ import java.nio.charset.StandardCharsets;
 import java.security.PrivateKey;
 import java.security.PublicKey;
 
-import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.NumberUtils;
 import org.apache.sshd.common.util.Pair;
 import org.apache.sshd.common.util.SecurityUtils;
 import org.apache.sshd.common.util.ValidateUtils;
@@ -62,7 +62,7 @@ public abstract class AbstractSignature implements Signature {
 
     @Override
     public void update(byte[] hash) throws Exception {
-        update(hash, 0, GenericUtils.length(hash));
+        update(hash, 0, NumberUtils.length(hash));
     }
 
     @Override
@@ -78,7 +78,7 @@ public abstract class AbstractSignature implements Signature {
      * value is the data - {@code null} if not encoded
      */
     protected Pair<String, byte[]> extractEncodedSignature(byte[] sig) {
-        final int dataLen = GenericUtils.length(sig);
+        final int dataLen = NumberUtils.length(sig);
         // if it is encoded then we must have at least 2 UINT32 values
         if (dataLen < (2 * (Integer.SIZE / Byte.SIZE))) {
             return null;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7b18b090/sshd-core/src/main/java/org/apache/sshd/common/signature/BuiltinSignatures.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/signature/BuiltinSignatures.java b/sshd-core/src/main/java/org/apache/sshd/common/signature/BuiltinSignatures.java
index 7bc3a92..851987a 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/signature/BuiltinSignatures.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/signature/BuiltinSignatures.java
@@ -35,7 +35,6 @@ import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.cipher.ECCurves;
 import org.apache.sshd.common.config.NamedFactoriesListParseResult;
-import org.apache.sshd.common.digest.Digest;
 import org.apache.sshd.common.keyprovider.KeyPairProvider;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.SecurityUtils;
@@ -197,12 +196,12 @@ public enum BuiltinSignatures implements SignatureFactory {
     }
 
     /**
-     * @param factory The {@link org.apache.sshd.common.NamedFactory} for the cipher - ignored if {@code null}
+     * @param factory The {@link org.apache.sshd.common.NamedFactory} for the signature - ignored if {@code null}
      * @return The matching {@link org.apache.sshd.common.signature.BuiltinSignatures} whose factory name matches
      * (case <U>insensitive</U>) the digest factory name
      * @see #fromFactoryName(String)
      */
-    public static BuiltinSignatures fromFactory(NamedFactory<Digest> factory) {
+    public static BuiltinSignatures fromFactory(NamedFactory<Signature> factory) {
         if (factory == null) {
             return null;
         } else {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7b18b090/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureDSA.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureDSA.java b/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureDSA.java
index 8c16a7f..eca665d 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureDSA.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureDSA.java
@@ -23,7 +23,7 @@ import java.math.BigInteger;
 import java.security.SignatureException;
 
 import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.NumberUtils;
 import org.apache.sshd.common.util.Pair;
 import org.apache.sshd.common.util.ValidateUtils;
 import org.apache.sshd.common.util.buffer.BufferUtils;
@@ -90,7 +90,7 @@ public class SignatureDSA extends AbstractSignature {
 
     @Override
     public boolean verify(byte[] sig) throws Exception {
-        int sigLen = GenericUtils.length(sig);
+        int sigLen = NumberUtils.length(sig);
         byte[] data = sig;
 
         if (sigLen != DSA_SIGNATURE_LENGTH) {
@@ -100,7 +100,7 @@ public class SignatureDSA extends AbstractSignature {
                 String keyType = encoding.getFirst();
                 ValidateUtils.checkTrue(KeyPairProvider.SSH_DSS.equals(keyType), "Mismatched key type: %s", keyType);
                 data = encoding.getSecond();
-                sigLen = GenericUtils.length(data);
+                sigLen = NumberUtils.length(data);
             }
         }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7b18b090/sshd-core/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/AbstractParser.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/AbstractParser.java b/sshd-core/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/AbstractParser.java
index 41eba37..e4e1c9d 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/AbstractParser.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/AbstractParser.java
@@ -19,7 +19,7 @@
 
 package org.apache.sshd.common.subsystem.sftp.extensions;
 
-import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.NumberUtils;
 import org.apache.sshd.common.util.ValidateUtils;
 
 /**
@@ -45,6 +45,6 @@ public abstract class AbstractParser<T> implements ExtensionParser<T> {
 
     @Override   // TODO in JDK-8 make this a default method
     public T parse(byte[] input) {
-        return parse(input, 0, GenericUtils.length(input));
+        return parse(input, 0, NumberUtils.length(input));
     }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7b18b090/sshd-core/src/main/java/org/apache/sshd/common/util/Base64.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/Base64.java b/sshd-core/src/main/java/org/apache/sshd/common/util/Base64.java
index 528bb16..c392420 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/Base64.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/Base64.java
@@ -354,7 +354,7 @@ public class Base64 {
         base64Data = discardNonBase64(base64Data);
 
         // handle the edge case, so we don't have to worry about it later
-        if (GenericUtils.isEmpty(base64Data)) {
+        if (NumberUtils.isEmpty(base64Data)) {
             return GenericUtils.EMPTY_BYTE_ARRAY;
         }
 
@@ -454,7 +454,7 @@ public class Base64 {
      * may be same as input if all data was base-64
      */
     public static byte[] discardNonBase64(byte[] data) {
-        if (GenericUtils.isEmpty(data)) {
+        if (NumberUtils.isEmpty(data)) {
             return data;
         }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7b18b090/sshd-core/src/main/java/org/apache/sshd/common/util/DirectoryScanner.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/DirectoryScanner.java b/sshd-core/src/main/java/org/apache/sshd/common/util/DirectoryScanner.java
deleted file mode 100644
index 43703ef..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/DirectoryScanner.java
+++ /dev/null
@@ -1,380 +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.File;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * <p>Class for scanning a directory for files/directories which match certain
- * criteria.</p>
- *
- * <p>These criteria consist of selectors and patterns which have been specified.
- * With the selectors you can select which files you want to have included.
- * Files which are not selected are excluded. With patterns you can include
- * or exclude files based on their filename.</p>
- *
- * <p>The idea is simple. A given directory is recursively scanned for all files
- * and directories. Each file/directory is matched against a set of selectors,
- * including special support for matching against filenames with include and
- * and exclude patterns. Only files/directories which match at least one
- * pattern of the include pattern list or other file selector, and don't match
- * any pattern of the exclude pattern list or fail to match against a required
- * selector will be placed in the list of files/directories found.</p>
- *
- * <p>When no list of include patterns is supplied, "**" will be used, which
- * means that everything will be matched. When no list of exclude patterns is
- * supplied, an empty list is used, such that nothing will be excluded. When
- * no selectors are supplied, none are applied.</p>
- *
- * <p>The filename pattern matching is done as follows:
- * The name to be matched is split up in path segments. A path segment is the
- * name of a directory or file, which is bounded by
- * <code>File.separator</code> ('/' under UNIX, '\' under Windows).
- * For example, "abc/def/ghi/xyz.java" is split up in the segments "abc",
- * "def","ghi" and "xyz.java".
- * The same is done for the pattern against which should be matched.</p>
- *
- * <p>The segments of the name and the pattern are then matched against each
- * other. When '**' is used for a path segment in the pattern, it matches
- * zero or more path segments of the name.</p>
- *
- * <p>There is a special case regarding the use of <code>File.separator</code>s
- * at the beginning of the pattern and the string to match:<br>
- * When a pattern starts with a <code>File.separator</code>, the string
- * to match must also start with a <code>File.separator</code>.
- * When a pattern does not start with a <code>File.separator</code>, the
- * string to match may not start with a <code>File.separator</code>.
- * When one of these rules is not obeyed, the string will not
- * match.</p>
- *
- * <p>When a name path segment is matched against a pattern path segment, the
- * following special characters can be used:<br>
- * '*' matches zero or more characters<br>
- * '?' matches one character.</p>
- *
- * <p>Examples:
- * <br>
- * <code>"**\*.class"</code> matches all <code>.class</code> files/dirs in a directory tree.
- * <br>
- * <code>"test\a??.java"</code> matches all files/dirs which start with an 'a', then two
- * more characters and then <code>".java"</code>, in a directory called test.
- * <br>
- * <code>"**"</code> matches everything in a directory tree.
- * <br>
- * <code>"**\test\**\XYZ*"</code> matches all files/dirs which start with <code>"XYZ"</code> and where
- * there is a parent directory called test (e.g. <code>"abc\test\def\ghi\XYZ123"</code>).
- * </p>
- *
- * <p>Case sensitivity may be turned off if necessary. By default, it is
- * turned on.</p>
- *
- * <p>Example of usage:</p>
- * <pre>
- *   String[] includes = {"**\\*.class"};
- *   String[] excludes = {"modules\\*\\**"};
- *   ds.setIncludes(includes);
- *   ds.setExcludes(excludes);
- *   ds.setBasedir(new File("test"));
- *   ds.setCaseSensitive(true);
- *   ds.scan();
- *
- *   System.out.println("FILES:");
- *   String[] files = ds.getIncludedFiles();
- *   for (int i = 0; i &lt; files.length; i++) {
- *     System.out.println(files[i]);
- *   }
- * </pre>
- * <p>This will scan a directory called test for .class files, but excludes all
- * files in all proper subdirectories of a directory called "modules".</p>
- *
- * @author Arnout J. Kuiper
- *         <a href="mailto:ajkuiper@wxs.nl">ajkuiper@wxs.nl</a>
- * @author Magesh Umasankar
- * @author <a href="mailto:bruce@callenish.com">Bruce Atherton</a>
- * @author <a href="mailto:levylambert@tiscali-dsl.de">Antoine Levy-Lambert</a>
- */
-public class DirectoryScanner {
-
-    /**
-     * The base directory to be scanned.
-     */
-    protected File basedir;
-
-    /**
-     * The patterns for the files to be included.
-     */
-    protected String[] includes;
-
-    /**
-     * The files which matched at least one include and no excludes
-     * and were selected.
-     */
-    protected List<String> filesIncluded;
-
-    /**
-     * Whether or not the file system should be treated as a case sensitive
-     * one.
-     */
-    protected boolean isCaseSensitive = true;
-
-    public DirectoryScanner() {
-    }
-
-    public DirectoryScanner(String basedir, String... includes) {
-        setBasedir(basedir);
-        setIncludes(includes);
-    }
-
-    /**
-     * Sets the base directory to be scanned. This is the directory which is
-     * scanned recursively. All '/' and '\' characters are replaced by
-     * <code>File.separatorChar</code>, so the separator used need not match
-     * <code>File.separatorChar</code>.
-     *
-     * @param basedir The base directory to scan.
-     *                Must not be {@code null}.
-     */
-    public void setBasedir(String basedir) {
-        setBasedir(new File(basedir.replace('/', File.separatorChar).replace(
-                '\\', File.separatorChar)));
-    }
-
-    /**
-     * Sets the base directory to be scanned. This is the directory which is
-     * scanned recursively.
-     *
-     * @param basedir The base directory for scanning.
-     *                Should not be {@code null}.
-     */
-    public void setBasedir(File basedir) {
-        this.basedir = basedir;
-    }
-
-    /**
-     * Returns the base directory to be scanned.
-     * This is the directory which is scanned recursively.
-     *
-     * @return the base directory to be scanned
-     */
-    public File getBasedir() {
-        return basedir;
-    }
-
-    /**
-     * <p>Sets the list of include patterns to use. All '/' and '\' characters
-     * are replaced by <code>File.separatorChar</code>, so the separator used
-     * need not match <code>File.separatorChar</code>.</p>
-     *
-     * <p>When a pattern ends with a '/' or '\', "**" is appended.</p>
-     *
-     * @param includes A list of include patterns.
-     *                 May be {@code null}, indicating that all files
-     *                 should be included. If a non-{@code null}
-     *                 list is given, all elements must be
-     *                 non-{@code null}.
-     */
-    public void setIncludes(String[] includes) {
-        if (includes == null) {
-            this.includes = null;
-        } else {
-            this.includes = new String[includes.length];
-            for (int i = 0; i < includes.length; i++) {
-                this.includes[i] = normalizePattern(includes[i]);
-            }
-        }
-    }
-
-
-    /**
-     * Scans the base directory for files which match at least one include
-     * pattern and don't match any exclude patterns. If there are selectors
-     * then the files must pass muster there, as well.
-     *
-     * @return the matching files
-     * @throws IllegalStateException if the base directory was set
-     *                               incorrectly (i.e. if it is {@code null}, doesn't exist,
-     *                               or isn't a directory).
-     */
-    public String[] scan() throws IllegalStateException {
-        if (basedir == null) {
-            throw new IllegalStateException("No basedir set");
-        }
-        if (!basedir.exists()) {
-            throw new IllegalStateException("basedir " + basedir
-                    + " does not exist");
-        }
-        if (!basedir.isDirectory()) {
-            throw new IllegalStateException("basedir " + basedir
-                    + " is not a directory");
-        }
-        if (includes == null || includes.length == 0) {
-            throw new IllegalStateException("No includes set ");
-        }
-
-        filesIncluded = new ArrayList<>();
-
-        scandir(basedir, "");
-
-        return getIncludedFiles();
-    }
-
-    /**
-     * Scans the given directory for files and directories. Found files and
-     * directories are placed in their respective collections, based on the
-     * matching of includes, excludes, and the selectors.  When a directory
-     * is found, it is scanned recursively.
-     *
-     * @param dir   The directory to scan. Must not be {@code null}.
-     * @param vpath The path relative to the base directory (needed to
-     *              prevent problems with an absolute path when using
-     *              dir). Must not be {@code null}.
-     */
-    protected void scandir(File dir, String vpath) {
-        String[] newfiles = dir.list();
-        if (newfiles == null) {
-            newfiles = new String[0];
-        }
-
-        for (String newfile : newfiles) {
-            String name = vpath + newfile;
-            File file = new File(dir, newfile);
-            if (file.isDirectory()) {
-                if (isIncluded(name)) {
-                    filesIncluded.add(name);
-                    scandir(file, name + File.separator);
-                } else if (couldHoldIncluded(name)) {
-                    scandir(file, name + File.separator);
-                }
-            } else if (file.isFile()) {
-                if (isIncluded(name)) {
-                    filesIncluded.add(name);
-                }
-            }
-        }
-    }
-
-    /**
-     * Returns the names of the files which matched at least one of the
-     * include patterns and none of the exclude patterns.
-     * The names are relative to the base directory.
-     *
-     * @return the names of the files which matched at least one of the
-     * include patterns and none of the exclude patterns.
-     */
-    public String[] getIncludedFiles() {
-        String[] files = new String[filesIncluded.size()];
-        filesIncluded.toArray(files);
-        return files;
-    }
-
-    /**
-     * Tests whether or not a name matches against at least one include
-     * pattern.
-     *
-     * @param name The name to match. Must not be {@code null}.
-     * @return <code>true</code> when the name matches against at least one
-     * include pattern, or <code>false</code> otherwise.
-     */
-    protected boolean isIncluded(String name) {
-        for (String include : includes) {
-            if (SelectorUtils.matchPath(include, name, isCaseSensitive)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Tests whether or not a name matches the start of at least one include
-     * pattern.
-     *
-     * @param name The name to match. Must not be {@code null}.
-     * @return <code>true</code> when the name matches against the start of at
-     * least one include pattern, or <code>false</code> otherwise.
-     */
-    protected boolean couldHoldIncluded(String name) {
-        for (String include : includes) {
-            if (SelectorUtils.matchPatternStart(include, name, isCaseSensitive)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Normalizes the pattern, e.g. converts forward and backward slashes to the platform-specific file separator.
-     *
-     * @param pattern The pattern to normalize, must not be {@code null}.
-     * @return The normalized pattern, never {@code null}.
-     */
-    private String normalizePattern(String pattern) {
-        pattern = pattern.trim();
-
-        if (pattern.startsWith(SelectorUtils.REGEX_HANDLER_PREFIX)) {
-            if (File.separatorChar == '\\') {
-                pattern = replace(pattern, "/", "\\\\", -1);
-            } else {
-                pattern = replace(pattern, "\\\\", "/", -1);
-            }
-        } else {
-            pattern = pattern.replace(File.separatorChar == '/' ? '\\' : '/', File.separatorChar);
-
-            if (pattern.endsWith(File.separator)) {
-                pattern += "**";
-            }
-        }
-
-        return pattern;
-    }
-
-    /**
-     * <p>Replace a String with another String inside a larger String,
-     * for the first <code>max</code> values of the search String.</p>
-     *
-     * <p>A {@code null} reference passed to this method is a no-op.</p>
-     *
-     * @param text text to search and replace in
-     * @param repl String to search for
-     * @param with String to replace with
-     * @param max  maximum number of values to replace, or <code>-1</code> if no maximum
-     * @return the text with any replacements processed
-     */
-    public static String replace(String text, String repl, String with, int max) {
-        if ((text == null) || (repl == null) || (with == null) || (repl.length() == 0)) {
-            return text;
-        }
-
-        StringBuilder buf = new StringBuilder(text.length());
-        int start = 0;
-        int end;
-        while ((end = text.indexOf(repl, start)) != -1) {
-            buf.append(text.substring(start, end)).append(with);
-            start = end + repl.length();
-
-            if (--max == 0) {
-                break;
-            }
-        }
-        buf.append(text.substring(start));
-        return buf.toString();
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7b18b090/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 39c08d2..13fc270 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
@@ -226,30 +226,6 @@ public final class GenericUtils {
         return size(m) <= 0;
     }
 
-    public static boolean isEmpty(byte[] a) {
-        return length(a) <= 0;
-    }
-
-    public static boolean isEmpty(int[] a) {
-        return length(a) <= 0;
-    }
-
-    public static boolean isEmpty(long[] a) {
-        return length(a) <= 0;
-    }
-
-    public static int length(byte... a) {
-        return a == null ? 0 : a.length;
-    }
-
-    public static int length(int... a) {
-        return a == null ? 0 : a.length;
-    }
-
-    public static int length(long... a) {
-        return a == null ? 0 : a.length;
-    }
-
     @SafeVarargs
     public static <T> int length(T... a) {
         return a == null ? 0 : a.length;
@@ -310,20 +286,6 @@ public final class GenericUtils {
         }
     }
 
-    public static List<Integer> asList(int ... values) {
-        int len = length(values);
-        if (len <= 0) {
-            return Collections.emptyList();
-        }
-
-        List<Integer> l = new ArrayList<>(len);
-        for (int v : values) {
-            l.add(Integer.valueOf(v));
-        }
-
-        return l;
-    }
-
     @SuppressWarnings({"unchecked", "rawtypes"})
     public static <V extends Comparable<V>> Comparator<V> naturalComparator() {
         // TODO for JDK-8 use Comparator.naturalOrder()

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7b18b090/sshd-core/src/main/java/org/apache/sshd/common/util/NumberUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/NumberUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/NumberUtils.java
index 686762f..0c61c03 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/NumberUtils.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/NumberUtils.java
@@ -85,7 +85,7 @@ public final class NumberUtils {
     }
 
     public static int hashCode(long ... values) {
-        if (GenericUtils.length(values) <= 0) {
+        if (NumberUtils.length(values) <= 0) {
             return 0;
         }
 
@@ -97,6 +97,19 @@ public final class NumberUtils {
         return hash;
     }
 
+    public static int hashCode(int ... values) {
+        if (NumberUtils.length(values) <= 0) {
+            return 0;
+        }
+
+        int hash = values[0];
+        for (int index = 1; index < values.length; index++) {
+            hash += 31 * hash + values[index];
+        }
+
+        return hash;
+    }
+
     // TODO in JDK-8 use Long.hashCode(long)
     public static int hashCode(long value) {
         return (int) (value ^ (value >>> 32));
@@ -135,4 +148,138 @@ public final class NumberUtils {
             return Integer.valueOf(n.intValue());
         }
     }
+
+    public static String join(CharSequence separator, long ... values) {
+        if (NumberUtils.isEmpty(values)) {
+            return "";
+        }
+
+        StringBuilder sb = new StringBuilder(values.length * Byte.SIZE);
+        for (long v : values) {
+            if (sb.length() > 0) {
+                sb.append(separator);
+            }
+            sb.append(v);
+        }
+
+        return sb.toString();
+    }
+
+    public static String join(char separator, long ... values) {
+        if (NumberUtils.isEmpty(values)) {
+            return "";
+        }
+
+        StringBuilder sb = new StringBuilder(values.length * Byte.SIZE);
+        for (long v : values) {
+            if (sb.length() > 0) {
+                sb.append(separator);
+            }
+            sb.append(v);
+        }
+
+        return sb.toString();
+    }
+
+    public static String join(CharSequence separator, boolean unsigned, byte ... values) {
+        if (NumberUtils.isEmpty(values)) {
+            return "";
+        }
+
+        StringBuilder sb = new StringBuilder(values.length * Byte.SIZE);
+        for (byte v : values) {
+            if (sb.length() > 0) {
+                sb.append(separator);
+            }
+            sb.append(unsigned ? (v & 0xFF) : v);
+        }
+
+        return sb.toString();
+    }
+
+    public static String join(char separator, boolean unsigned, byte ... values) {
+        if (NumberUtils.isEmpty(values)) {
+            return "";
+        }
+
+        StringBuilder sb = new StringBuilder(values.length * Byte.SIZE);
+        for (byte v : values) {
+            if (sb.length() > 0) {
+                sb.append(separator);
+            }
+            sb.append(unsigned ? (v & 0xFF) : v);
+        }
+
+        return sb.toString();
+    }
+
+    public static String join(CharSequence separator, int ... values) {
+        if (NumberUtils.isEmpty(values)) {
+            return "";
+        }
+
+        StringBuilder sb = new StringBuilder(values.length * Byte.SIZE);
+        for (int v : values) {
+            if (sb.length() > 0) {
+                sb.append(separator);
+            }
+            sb.append(v);
+        }
+
+        return sb.toString();
+    }
+
+    public static String join(char separator, int ... values) {
+        if (NumberUtils.isEmpty(values)) {
+            return "";
+        }
+
+        StringBuilder sb = new StringBuilder(values.length * Byte.SIZE);
+        for (int v : values) {
+            if (sb.length() > 0) {
+                sb.append(separator);
+            }
+            sb.append(v);
+        }
+
+        return sb.toString();
+    }
+
+    public static boolean isEmpty(byte[] a) {
+        return NumberUtils.length(a) <= 0;
+    }
+
+    public static boolean isEmpty(int[] a) {
+        return NumberUtils.length(a) <= 0;
+    }
+
+    public static boolean isEmpty(long[] a) {
+        return NumberUtils.length(a) <= 0;
+    }
+
+    public static int length(byte... a) {
+        return a == null ? 0 : a.length;
+    }
+
+    public static int length(int... a) {
+        return a == null ? 0 : a.length;
+    }
+
+    public static int length(long... a) {
+        return a == null ? 0 : a.length;
+    }
+
+    public static List<Integer> asList(int ... values) {
+        int len = length(values);
+        if (len <= 0) {
+            return Collections.emptyList();
+        }
+    
+        List<Integer> l = new ArrayList<>(len);
+        for (int v : values) {
+            l.add(Integer.valueOf(v));
+        }
+    
+        return l;
+    }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7b18b090/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 55cf1e0..fac8ad3 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
@@ -36,6 +36,18 @@ public final class OsUtils {
      */
     public static final String CURRENT_USER_OVERRIDE_PROP = "org.apache.sshd.currentUser";
 
+    /**
+     * Property that can be used to override the reported value from {@link #getJavaVersion()}.
+     * If not set then &quot;java.version&quot; system property is used
+     */
+    public static final String JAVA_VERSION_OVERRIDE_PROP = "org.apache.sshd.javaVersion";
+
+    /**
+     * Property that can be used to override the reported value from {@link #isWin32()}.
+     * If not set then &quot;os.name&quot; system property is used
+     */
+    public static final String OS_TYPE_OVERRIDE_PROP = "org.apache.sshd.osType";
+
     public static final String WINDOWS_SHELL_COMMAND_NAME = "cmd.exe";
     public static final String LINUX_SHELL_COMMAND_NAME = "/bin/sh";
 
@@ -47,7 +59,8 @@ public final class OsUtils {
             Collections.unmodifiableList(Collections.singletonList(WINDOWS_SHELL_COMMAND_NAME));
 
     private static final AtomicReference<String> CURRENT_USER_HOLDER = new AtomicReference<>(null);
-    private static final boolean WIN32 = GenericUtils.trimToEmpty(System.getProperty("os.name")).toLowerCase().contains("windows");
+    private static final AtomicReference<VersionInfo> JAVA_VERSION_HOLDER = new AtomicReference<>(null);
+    private static final AtomicReference<Boolean> OS_TYPE_HOLDER = new AtomicReference<>(null);
 
     private OsUtils() {
         throw new UnsupportedOperationException("No instance allowed");
@@ -57,14 +70,39 @@ public final class OsUtils {
      * @return true if the host is a UNIX system (and not Windows).
      */
     public static boolean isUNIX() {
-        return !WIN32;
+        return !isWin32();
     }
 
     /**
      * @return true if the host is Windows (and not UNIX).
+     * @see #OS_TYPE_OVERRIDE_PROP
+     * @see #setWin32(Boolean)
      */
     public static boolean isWin32() {
-        return WIN32;
+        Boolean typeValue;
+        synchronized (OS_TYPE_HOLDER) {
+            typeValue = OS_TYPE_HOLDER.get();
+            if (typeValue != null) {    // is it the 1st time
+                return typeValue.booleanValue();
+            }
+
+            String value = System.getProperty(OS_TYPE_OVERRIDE_PROP, System.getProperty("os.name"));
+            typeValue = GenericUtils.trimToEmpty(value).toLowerCase().contains("windows");
+            OS_TYPE_HOLDER.set(typeValue);
+        }
+
+        return typeValue.booleanValue();
+    }
+
+    /**
+     * Can be used to enforce Win32 or Linux report from {@link #isWin32()} or {@link #isUNIX()}
+     * @param win32 The value to set - if {@code null} then O/S type is auto-detected
+     * @see #isWin32()
+     */
+    public static void setWin32(Boolean win32) {
+        synchronized (OS_TYPE_HOLDER) {
+            OS_TYPE_HOLDER.set(win32);
+        }
     }
 
     public static List<String> resolveDefaultInteractiveCommand() {
@@ -111,4 +149,50 @@ public final class OsUtils {
             CURRENT_USER_HOLDER.set(username);
         }
     }
+
+    /**
+     * Resolves the reported Java version by consulting {@link #JAVA_VERSION_OVERRIDE_PROP}.
+     * If not set, then &quot;java.version&quot; property is used
+     * @return The resolved {@link VersionInfo} - never {@code null}
+     * @see #setJavaVersion(VersionInfo)
+     */
+    public static VersionInfo getJavaVersion() {
+        VersionInfo version;
+        synchronized (JAVA_VERSION_HOLDER) {
+            version = JAVA_VERSION_HOLDER.get();
+            if (version != null) {  // first time ?
+                return version;
+            }
+
+            String value = System.getProperty(JAVA_VERSION_OVERRIDE_PROP, System.getProperty("java.version"));
+            // e.g.: 1.7.5_30
+            value = ValidateUtils.checkNotNullAndNotEmpty(value, "No configured Java version value").replace('_', '.');
+            // clean up any non-digits - in case something like 1.6.8_25-b323
+            for (int index = 0; index < value.length(); index++) {
+                char ch = value.charAt(index);
+                if ((ch == '.') || ((ch >= '0') && (ch <= '9'))) {
+                    continue;
+                }
+
+                value = value.substring(0, index);
+                break;
+            }
+
+            version = ValidateUtils.checkNotNull(VersionInfo.parse(value), "No version parsed for %s", value);
+            JAVA_VERSION_HOLDER.set(version);
+        }
+
+        return version;
+    }
+
+    /**
+     * Set programmatically the reported Java version
+     * @param version The version - if {@code null} then it will be automatically resolved
+     */
+    public static void setJavaVersion(VersionInfo version) {
+        synchronized (JAVA_VERSION_HOLDER) {
+            JAVA_VERSION_HOLDER.set(version);
+        }
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7b18b090/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 4a7012f..7e23452 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
@@ -198,6 +198,17 @@ public final class SecurityUtils {
         return maxSupportedKeySize;
     }
 
+    /**
+     * Set programmatically the reported value for {@link #getMaxDHGroupExchangeKeySize()}
+     * @param keySize The reported key size - if zero, then it will be auto-detected, if
+     * negative then DH group exchange will be disabled
+     */
+    public static void setMaxDHGroupExchangeKeySize(int keySize) {
+        synchronized (MAX_DHG_KEY_SIZE_HOLDER) {
+            MAX_DHG_KEY_SIZE_HOLDER.set(keySize);
+        }
+    }
+
     public static boolean isDHGroupExchangeSupported(int maxKeySize) {
         ValidateUtils.checkTrue(maxKeySize > Byte.SIZE, "Invalid max. key size: %d", maxKeySize);
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7b18b090/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 b48f811..3679471 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
@@ -82,25 +82,25 @@ public final class ValidateUtils {
 
     public static byte[] checkNotNullAndNotEmpty(byte[] a, String message) {
         a = checkNotNull(a, message);
-        checkTrue(GenericUtils.length(a) > 0, message);
+        checkTrue(NumberUtils.length(a) > 0, message);
         return a;
     }
 
     public static byte[] checkNotNullAndNotEmpty(byte[] a, String message, Object... args) {
         a = checkNotNull(a, message, args);
-        checkTrue(GenericUtils.length(a) > 0, message, args);
+        checkTrue(NumberUtils.length(a) > 0, message, args);
         return a;
     }
 
     public static int[] checkNotNullAndNotEmpty(int[] a, String message) {
         a = checkNotNull(a, message);
-        checkTrue(GenericUtils.length(a) > 0, message);
+        checkTrue(NumberUtils.length(a) > 0, message);
         return a;
     }
 
     public static int[] checkNotNullAndNotEmpty(int[] a, String message, Object... args) {
         a = checkNotNull(a, message, args);
-        checkTrue(GenericUtils.length(a) > 0, message, args);
+        checkTrue(NumberUtils.length(a) > 0, message, args);
         return a;
     }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7b18b090/sshd-core/src/main/java/org/apache/sshd/common/util/VersionInfo.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/VersionInfo.java b/sshd-core/src/main/java/org/apache/sshd/common/util/VersionInfo.java
new file mode 100644
index 0000000..ed1aab7
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/VersionInfo.java
@@ -0,0 +1,137 @@
+/*
+ * 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.Serializable;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class VersionInfo implements Serializable, Comparable<VersionInfo> {
+    private static final long serialVersionUID = -9127482432228413836L;
+
+    private final int majorVersion;
+    private final int minorVersion;
+    private final int release;
+    private final int buildNumber;
+
+    public VersionInfo(int major, int minor) {
+        this(major, minor, 0, 0);
+    }
+
+    public VersionInfo(int major, int minor, int release, int build) {
+        this.majorVersion = major;
+        this.minorVersion = minor;
+        this.release = release;
+        this.buildNumber = build;
+    }
+
+    public final int getMajorVersion() {
+        return majorVersion;
+    }
+
+    public final int getMinorVersion() {
+        return minorVersion;
+    }
+
+    public final int getRelease() {
+        return release;
+    }
+
+    public final int getBuildNumber() {
+        return buildNumber;
+    }
+
+    @Override
+    public int hashCode() {
+        return NumberUtils.hashCode(getMajorVersion(), getMinorVersion(), getRelease(), getBuildNumber());
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (this == obj) {
+            return true;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        return compareTo((VersionInfo) obj) == 0;
+    }
+
+    @Override
+    public int compareTo(VersionInfo o) {
+        if (o == null) {
+            return -1;  // push nulls to end
+        }
+        if (o == this) {
+            return 0;
+        }
+
+        int nRes = Integer.compare(getMajorVersion(), o.getMajorVersion());
+        if (nRes == 0) {
+            nRes = Integer.compare(getMinorVersion(), o.getMinorVersion());
+        }
+        if (nRes == 0) {
+            nRes = Integer.compare(getRelease(), o.getRelease());
+        }
+        if (nRes == 0) {
+            nRes = Integer.compare(getBuildNumber(), o.getBuildNumber());
+        }
+
+        return nRes;
+    }
+
+    @Override
+    public String toString() {
+        return NumberUtils.join('.', getMajorVersion(), getMinorVersion(), getRelease(), getBuildNumber());
+    }
+
+    /**
+     * Parses a version string - assumed to contain at most 4 non-negative
+     * components separated by a '.'. If less than 4 components are found, then
+     * the rest are assumed to be zero. If more than 4 components found, then
+     * only the 1st ones are parsed.
+     *
+     * @param version The version string - ignored if {@code null}/empty
+     * @return The parsed {@link VersionInfo} - or {@code null} if empty input
+     * @throws NumberFormatException If failed to parse any of the components
+     * @throws IllegalArgumentException If any of the parsed components is negative
+     */
+    public static VersionInfo parse(String version) throws NumberFormatException {
+        String[] comps = GenericUtils.split(version, '.');
+        if (GenericUtils.isEmpty(comps)) {
+            return null;
+        }
+
+        int[] values = new int[4];
+        int maxValues = Math.min(comps.length, values.length);
+        for (int index = 0; index < maxValues; index++) {
+            String c = comps[index];
+            int v = Integer.parseInt(c);
+            ValidateUtils.checkTrue(v >= 0, "Invalid version component in %s", version);
+            values[index] = v;
+        }
+
+        return new VersionInfo(values[0], values[1], values[2], values[3]);
+    }
+}