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 2018/09/06 16:03:11 UTC

[01/51] [abbrv] mina-sshd git commit: [SSHD-842] Split Putty key files support code to separate artifact

Repository: mina-sshd
Updated Branches:
  refs/heads/master 1e8254794 -> 3668d92e4


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/DSSPuttyKeyDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/DSSPuttyKeyDecoder.java b/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/DSSPuttyKeyDecoder.java
new file mode 100644
index 0000000..366aead
--- /dev/null
+++ b/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/DSSPuttyKeyDecoder.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.config.keys.loader.putty;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.interfaces.DSAPrivateKey;
+import java.security.interfaces.DSAPublicKey;
+import java.security.spec.DSAPrivateKeySpec;
+import java.security.spec.DSAPublicKeySpec;
+import java.util.Collection;
+import java.util.Collections;
+
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class DSSPuttyKeyDecoder extends AbstractPuttyKeyDecoder<DSAPublicKey, DSAPrivateKey> {
+    public static final DSSPuttyKeyDecoder INSTANCE = new DSSPuttyKeyDecoder();
+
+    public DSSPuttyKeyDecoder() {
+        super(DSAPublicKey.class, DSAPrivateKey.class, Collections.singletonList(KeyPairProvider.SSH_DSS));
+    }
+
+    @Override
+    public Collection<KeyPair> loadKeyPairs(String resourceKey, PuttyKeyReader pubReader, PuttyKeyReader prvReader)
+            throws IOException, GeneralSecurityException {
+        pubReader.skip();   // skip version
+
+        BigInteger p = pubReader.readInt();
+        BigInteger q = pubReader.readInt();
+        BigInteger g = pubReader.readInt();
+        BigInteger y = pubReader.readInt();
+        BigInteger x = prvReader.readInt();
+        KeyFactory kf = SecurityUtils.getKeyFactory(KeyUtils.DSS_ALGORITHM);
+        PublicKey pubKey = kf.generatePublic(new DSAPublicKeySpec(y, p, q, g));
+        PrivateKey prvKey = kf.generatePrivate(new DSAPrivateKeySpec(x, p, q, g));
+        return Collections.singletonList(new KeyPair(pubKey, prvKey));
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/ECDSAPuttyKeyDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/ECDSAPuttyKeyDecoder.java b/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/ECDSAPuttyKeyDecoder.java
new file mode 100644
index 0000000..a257ff8
--- /dev/null
+++ b/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/ECDSAPuttyKeyDecoder.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.config.keys.loader.putty;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPoint;
+import java.security.spec.ECPrivateKeySpec;
+import java.security.spec.ECPublicKeySpec;
+import java.security.spec.InvalidKeySpecException;
+import java.util.Collection;
+import java.util.Collections;
+
+import org.apache.sshd.common.cipher.ECCurves;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.util.buffer.BufferUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * TODO Add javadoc
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class ECDSAPuttyKeyDecoder extends AbstractPuttyKeyDecoder<ECPublicKey, ECPrivateKey> {
+    public static final ECDSAPuttyKeyDecoder INSTANCE = new ECDSAPuttyKeyDecoder();
+
+    public ECDSAPuttyKeyDecoder() {
+        super(ECPublicKey.class, ECPrivateKey.class, ECCurves.KEY_TYPES);
+    }
+
+    @Override
+    public Collection<KeyPair> loadKeyPairs(String resourceKey, PuttyKeyReader pubReader, PuttyKeyReader prvReader)
+            throws IOException, GeneralSecurityException {
+        if (!SecurityUtils.isECCSupported()) {
+            throw new NoSuchAlgorithmException("ECC not supported for " + resourceKey);
+        }
+
+        String keyType = pubReader.readString();
+        ECCurves curve = ECCurves.fromKeyType(keyType);
+        if (curve == null) {
+            throw new InvalidKeySpecException("Not an EC curve name: " + keyType);
+        }
+
+        String encCurveName = pubReader.readString();
+        String keyCurveName = curve.getName();
+        if (!keyCurveName.equals(encCurveName)) {
+            throw new InvalidKeySpecException("Mismatched key curve name (" + keyCurveName + ") vs. encoded one (" + encCurveName + ")");
+        }
+
+        byte[] octets = pubReader.read();
+        ECPoint w;
+        try {
+            w = ECCurves.octetStringToEcPoint(octets);
+            if (w == null) {
+                throw new InvalidKeySpecException("No public ECPoint generated for curve=" + keyCurveName
+                        + " from octets=" + BufferUtils.toHex(':', octets));
+            }
+        } catch (RuntimeException e) {
+            throw new InvalidKeySpecException("Failed (" + e.getClass().getSimpleName() + ")"
+                    + " to generate public ECPoint for curve=" + keyCurveName
+                    + " from octets=" + BufferUtils.toHex(':', octets)
+                    + ": " + e.getMessage());
+        }
+
+        KeyFactory kf = SecurityUtils.getKeyFactory(KeyUtils.EC_ALGORITHM);
+        ECParameterSpec paramSpec = curve.getParameters();
+        PublicKey pubKey = kf.generatePublic(new ECPublicKeySpec(w, paramSpec));
+
+        BigInteger s = prvReader.readInt();
+        PrivateKey prvKey = kf.generatePrivate(new ECPrivateKeySpec(s, paramSpec));
+        return Collections.singletonList(new KeyPair(pubKey, prvKey));
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/EdDSAPuttyKeyDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/EdDSAPuttyKeyDecoder.java b/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/EdDSAPuttyKeyDecoder.java
new file mode 100644
index 0000000..f5980ab
--- /dev/null
+++ b/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/EdDSAPuttyKeyDecoder.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.config.keys.loader.putty;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.util.Collection;
+import java.util.Collections;
+
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.security.SecurityUtils;
+import org.apache.sshd.common.util.security.eddsa.EdDSASecurityProviderUtils;
+
+import net.i2p.crypto.eddsa.EdDSAPrivateKey;
+import net.i2p.crypto.eddsa.EdDSAPublicKey;
+
+/**
+ * TODO Add javadoc
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class EdDSAPuttyKeyDecoder extends AbstractPuttyKeyDecoder<EdDSAPublicKey, EdDSAPrivateKey> {
+    public static final EdDSAPuttyKeyDecoder INSTANCE = new EdDSAPuttyKeyDecoder();
+
+    public EdDSAPuttyKeyDecoder() {
+        super(EdDSAPublicKey.class, EdDSAPrivateKey.class, Collections.singletonList(KeyPairProvider.SSH_ED25519));
+    }
+
+    @Override
+    public Collection<KeyPair> loadKeyPairs(String resourceKey, PuttyKeyReader pubReader, PuttyKeyReader prvReader)
+            throws IOException, GeneralSecurityException {
+        if (!SecurityUtils.isEDDSACurveSupported()) {
+            throw new NoSuchAlgorithmException(SecurityUtils.EDDSA + " provider not supported for " + resourceKey);
+        }
+
+        String keyType = pubReader.readString();
+        if (!KeyPairProvider.SSH_ED25519.equals(keyType)) {
+            throw new InvalidKeySpecException("Not an " + SecurityUtils.EDDSA + " key: " + keyType);
+        }
+
+        byte[] seed = pubReader.read();
+        PublicKey pubKey = EdDSASecurityProviderUtils.generateEDDSAPublicKey(seed);
+        seed = prvReader.read();
+        PrivateKey prvKey = EdDSASecurityProviderUtils.generateEDDSAPrivateKey(seed);
+        return Collections.singletonList(new KeyPair(pubKey, prvKey));
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyPairResourceParser.java
----------------------------------------------------------------------
diff --git a/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyPairResourceParser.java b/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyPairResourceParser.java
new file mode 100644
index 0000000..06443d9
--- /dev/null
+++ b/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyPairResourceParser.java
@@ -0,0 +1,200 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config.keys.loader.putty;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.apache.sshd.common.config.keys.IdentityResourceLoader;
+import org.apache.sshd.common.config.keys.loader.KeyPairResourceParser;
+import org.apache.sshd.common.digest.BuiltinDigests;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+//CHECKSTYLE:OFF
+/**
+ * Loads a {@link KeyPair} from PuTTY's &quot;.ppk&quot; file.
+ * <P>Note(s):</P>
+ * <UL>
+ *      <P><LI>
+ *      The file appears to be a text file but it doesn't have a fixed encoding like UTF-8.
+ *      We use UTF-8 as the default encoding - since the important part is all ASCII,
+ *      this shouldn't really hurt the interpretation of the key.
+ *      </LI></P>
+ *
+ *      <P><LI>
+ *      Based on code from <A HREF="https://github.com/kohsuke/trilead-putty-extension">Kohsuke's Trilead Putty Extension</A>
+ *      </LI></P>
+ *
+ *      <P><LI>
+ *      Encrypted keys requires AES-256-CBC support, which is available only if the
+ *      <A HREF="http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html">
+ *      Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files</A> are installed
+ *      </LI></P>
+ * </UL>
+ *
+ * <P>Sample PuTTY file format</P>
+ * <PRE>
+ * PuTTY-User-Key-File-2: ssh-rsa
+ * Encryption: none
+ * Comment: rsa-key-20080514
+ * Public-Lines: 4
+ * AAAAB3NzaC1yc2EAAAABJQAAAIEAiPVUpONjGeVrwgRPOqy3Ym6kF/f8bltnmjA2
+ * BMdAtaOpiD8A2ooqtLS5zWYuc0xkW0ogoKvORN+RF4JI+uNUlkxWxnzJM9JLpnvA
+ * HrMoVFaQ0cgDMIHtE1Ob1cGAhlNInPCRnGNJpBNcJ/OJye3yt7WqHP4SPCCLb6nL
+ * nmBUrLM=
+ * Private-Lines: 8
+ * AAAAgGtYgJzpktzyFjBIkSAmgeVdozVhgKmF6WsDMUID9HKwtU8cn83h6h7ug8qA
+ * hUWcvVxO201/vViTjWVz9ALph3uMnpJiuQaaNYIGztGJBRsBwmQW9738pUXcsUXZ
+ * 79KJP01oHn6Wkrgk26DIOsz04QOBI6C8RumBO4+F1WdfueM9AAAAQQDmA4hcK8Bx
+ * nVtEpcF310mKD3nsbJqARdw5NV9kCxPnEsmy7Sy1L4Ob/nTIrynbc3MA9HQVJkUz
+ * 7V0va5Pjm/T7AAAAQQCYbnG0UEekwk0LG1Hkxh1OrKMxCw2KWMN8ac3L0LVBg/Tk
+ * 8EnB2oT45GGeJaw7KzdoOMFZz0iXLsVLNUjNn2mpAAAAQQCN6SEfWqiNzyc/w5n/
+ * lFVDHExfVUJp0wXv+kzZzylnw4fs00lC3k4PZDSsb+jYCMesnfJjhDgkUA0XPyo8
+ * Emdk
+ * Private-MAC: 50c45751d18d74c00fca395deb7b7695e3ed6f77
+ * </PRE>
+ * @param <PUB> Generic public key type
+ * @param <PRV> Generic private key type
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+//CHECKSTYLE:ON
+public interface PuttyKeyPairResourceParser<PUB extends PublicKey, PRV extends PrivateKey>
+        extends IdentityResourceLoader<PUB, PRV>, KeyPairResourceParser {
+    String KEY_FILE_HEADER_PREFIX = "PuTTY-User-Key-File";
+    String PUBLIC_LINES_HEADER = "Public-Lines";
+    String PRIVATE_LINES_HEADER = "Private-Lines";
+    String PPK_FILE_SUFFIX = ".ppk";
+
+    List<String> KNOWN_HEADERS =
+            Collections.unmodifiableList(
+                    Arrays.asList(
+                            KEY_FILE_HEADER_PREFIX,
+                            PUBLIC_LINES_HEADER,
+                            PRIVATE_LINES_HEADER));
+
+    /**
+     * Value (case insensitive) used to denote that private key is not encrypted
+     */
+    String NO_PRIVATE_KEY_ENCRYPTION_VALUE = "none";
+
+    @Override
+    default boolean canExtractKeyPairs(String resourceKey, List<String> lines)
+            throws IOException, GeneralSecurityException {
+        if (GenericUtils.isEmpty(lines)) {
+            return false;
+        }
+
+        for (String l : lines) {
+            l = GenericUtils.trimToEmpty(l);
+            for (String hdr : KNOWN_HEADERS) {
+                if (l.startsWith(hdr)) {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+    static byte[] decodePrivateKeyBytes(byte[] prvBytes, String algName, int numBits, String algMode, String password)
+            throws GeneralSecurityException {
+        Objects.requireNonNull(prvBytes, "No encrypted key bytes");
+        ValidateUtils.checkNotNullAndNotEmpty(algName, "No encryption algorithm", GenericUtils.EMPTY_OBJECT_ARRAY);
+        ValidateUtils.checkTrue(numBits > 0, "Invalid encryption key size: %d", numBits);
+        ValidateUtils.checkNotNullAndNotEmpty(algMode, "No encryption mode", GenericUtils.EMPTY_OBJECT_ARRAY);
+        ValidateUtils.checkNotNullAndNotEmpty(password, "No encryption password", GenericUtils.EMPTY_OBJECT_ARRAY);
+
+        if (!"AES".equalsIgnoreCase(algName)) {
+            throw new NoSuchAlgorithmException("decodePrivateKeyBytes(" + algName + "-" + numBits + "-" + algMode + ") N/A");
+        }
+
+        return decodePrivateKeyBytes(prvBytes, algName, algMode, numBits, new byte[16], toEncryptionKey(password));
+    }
+
+    static byte[] decodePrivateKeyBytes(
+            byte[] encBytes, String cipherName, String cipherMode, int numBits, byte[] initVector, byte[] keyValue)
+                    throws GeneralSecurityException {
+        String xform = cipherName + "/" + cipherMode + "/NoPadding";
+        int maxAllowedBits = Cipher.getMaxAllowedKeyLength(xform);
+        // see http://www.javamex.com/tutorials/cryptography/unrestricted_policy_files.shtml
+        if (numBits > maxAllowedBits) {
+            throw new InvalidKeySpecException("decodePrivateKeyBytes(" + xform + ")"
+                    + " required key length (" + numBits + ") exceeds max. available: " + maxAllowedBits);
+        }
+
+        SecretKeySpec skeySpec = new SecretKeySpec(keyValue, cipherName);
+        IvParameterSpec ivspec = new IvParameterSpec(initVector);
+        Cipher cipher = SecurityUtils.getCipher(xform);
+        cipher.init(Cipher.DECRYPT_MODE, skeySpec, ivspec);
+
+        return cipher.doFinal(encBytes);
+    }
+
+    /**
+     * Converts a pass-phrase into a key, by following the convention that PuTTY uses.
+     * Used to decrypt the private key when it's encrypted.
+     * @param passphrase the Password to be used as seed for the key - ignored
+     * if {@code null}/empty
+     * @return The encryption key bytes - {@code null} if no pass-phrase
+     * @throws GeneralSecurityException If cannot retrieve SHA-1 digest
+     * @see <A HREF="http://security.stackexchange.com/questions/71341/how-does-putty-derive-the-encryption-key-in-its-ppk-format">
+     * How does Putty derive the encryption key in its .ppk format ?</A>
+     */
+    static byte[] toEncryptionKey(String passphrase) throws GeneralSecurityException {
+        if (GenericUtils.isEmpty(passphrase)) {
+            return null;
+        }
+
+        MessageDigest hash = SecurityUtils.getMessageDigest(BuiltinDigests.sha1.getAlgorithm());
+        byte[] stateValue = {0, 0, 0, 0};
+        byte[] passBytes = passphrase.getBytes(StandardCharsets.UTF_8);
+        byte[] keyValue = new byte[32];
+        for (int i = 0, remLen = keyValue.length; i < 2; i++) {
+            hash.reset(); // just making sure
+
+            stateValue[3] = (byte) i;
+            hash.update(stateValue);
+            hash.update(passBytes);
+
+            byte[] digest = hash.digest();
+            System.arraycopy(digest, 0, keyValue, i * 20, Math.min(20, remLen));
+            remLen -= 20;
+        }
+
+        return keyValue;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyReader.java
----------------------------------------------------------------------
diff --git a/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyReader.java b/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyReader.java
new file mode 100644
index 0000000..4fb63d1
--- /dev/null
+++ b/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyReader.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config.keys.loader.putty;
+
+import java.io.Closeable;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StreamCorruptedException;
+import java.math.BigInteger;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * Helper class for {@code Putty} key files decoders
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class PuttyKeyReader implements Closeable {
+    private final DataInputStream di;
+
+    public PuttyKeyReader(InputStream s) {
+        di = new DataInputStream(s);
+    }
+
+    public void skip() throws IOException {
+        int skipSize = di.readInt();
+        int effectiveSkip = di.skipBytes(skipSize);
+        if (skipSize != effectiveSkip) {
+            throw new StreamCorruptedException("Mismatched skip size: expected" + skipSize + ", actual=" + effectiveSkip);
+        }
+    }
+
+    public String readString() throws IOException {
+        return readString(StandardCharsets.UTF_8);
+    }
+
+    public String readString(Charset cs) throws IOException {
+        byte[] data = read();
+        return new String(data, cs);
+    }
+
+    public BigInteger readInt() throws IOException {
+        byte[] bytes = read();
+        return new BigInteger(bytes);
+    }
+
+    public byte[] read() throws IOException {
+        int len = di.readInt();
+        byte[] r = new byte[len];
+        di.readFully(r);
+        return r;
+    }
+
+    @Override
+    public void close() throws IOException {
+        di.close();
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtils.java
----------------------------------------------------------------------
diff --git a/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtils.java b/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtils.java
new file mode 100644
index 0000000..e750ace
--- /dev/null
+++ b/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtils.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config.keys.loader.putty;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.NavigableMap;
+import java.util.TreeMap;
+
+import org.apache.sshd.common.config.keys.loader.KeyPairResourceParser;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class PuttyKeyUtils {
+    public static final List<PuttyKeyPairResourceParser<?, ?>> DEFAULT_PARSERS;
+
+    public static final NavigableMap<String, PuttyKeyPairResourceParser<?, ?>> BY_KEY_TYPE;
+
+    public static final KeyPairResourceParser DEFAULT_INSTANCE;
+
+    static {
+        List<PuttyKeyPairResourceParser<?, ?>> parsers = new ArrayList<>();
+        parsers.add(RSAPuttyKeyDecoder.INSTANCE);
+        parsers.add(DSSPuttyKeyDecoder.INSTANCE);
+        if (SecurityUtils.isECCSupported()) {
+            parsers.add(ECDSAPuttyKeyDecoder.INSTANCE);
+        }
+        if (SecurityUtils.isEDDSACurveSupported()) {
+            parsers.add(EdDSAPuttyKeyDecoder.INSTANCE);
+        }
+        NavigableMap<String, PuttyKeyPairResourceParser<?, ?>> map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+        for (PuttyKeyPairResourceParser<?, ?> p : parsers) {
+            Collection<String> supported = p.getSupportedTypeNames();
+            for (String k : supported) {
+                map.put(k, p);
+            }
+        }
+        DEFAULT_PARSERS = Collections.unmodifiableList(parsers);
+        BY_KEY_TYPE = Collections.unmodifiableNavigableMap(map);
+        DEFAULT_INSTANCE = KeyPairResourceParser.aggregate(parsers);
+    }
+
+    private PuttyKeyUtils() {
+        throw new UnsupportedOperationException("No instance");
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/RSAPuttyKeyDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/RSAPuttyKeyDecoder.java b/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/RSAPuttyKeyDecoder.java
new file mode 100644
index 0000000..0a55d55
--- /dev/null
+++ b/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/RSAPuttyKeyDecoder.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config.keys.loader.putty;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.RSAPrivateCrtKeySpec;
+import java.security.spec.RSAPrivateKeySpec;
+import java.security.spec.RSAPublicKeySpec;
+import java.util.Collection;
+import java.util.Collections;
+
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class RSAPuttyKeyDecoder extends AbstractPuttyKeyDecoder<RSAPublicKey, RSAPrivateKey> {
+    public static final RSAPuttyKeyDecoder INSTANCE = new RSAPuttyKeyDecoder();
+
+    public RSAPuttyKeyDecoder() {
+        super(RSAPublicKey.class, RSAPrivateKey.class, Collections.singletonList(KeyPairProvider.SSH_RSA));
+    }
+
+    @Override
+    public Collection<KeyPair> loadKeyPairs(String resourceKey, PuttyKeyReader pubReader, PuttyKeyReader prvReader)
+            throws IOException, GeneralSecurityException {
+        pubReader.skip();   // skip version
+
+        KeyFactory kf = SecurityUtils.getKeyFactory(KeyUtils.RSA_ALGORITHM);
+        BigInteger publicExp = pubReader.readInt();
+        BigInteger modulus = pubReader.readInt();
+        PublicKey pubKey = kf.generatePublic(new RSAPublicKeySpec(modulus, publicExp));
+
+        BigInteger privateExp = prvReader.readInt();
+        BigInteger primeP = prvReader.readInt();
+        BigInteger primeQ = prvReader.readInt();
+        BigInteger crtCoef = prvReader.readInt();
+        BigInteger primeExponentP = privateExp.mod(primeP.subtract(BigInteger.ONE));
+        BigInteger primeExponentQ = privateExp.mod(primeQ.subtract(BigInteger.ONE));
+        RSAPrivateKeySpec prvSpec = new RSAPrivateCrtKeySpec(
+                modulus, publicExp, privateExp, primeP, primeQ, primeExponentP, primeExponentQ, crtCoef);
+        PrivateKey prvKey = kf.generatePrivate(prvSpec);
+        return Collections.singletonList(new KeyPair(pubKey, prvKey));
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-putty/src/main/resources/.gitignore
----------------------------------------------------------------------
diff --git a/sshd-putty/src/main/resources/.gitignore b/sshd-putty/src/main/resources/.gitignore
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-putty/src/test/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-putty/src/test/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest.java b/sshd-putty/src/test/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest.java
new file mode 100644
index 0000000..b9f030f
--- /dev/null
+++ b/sshd-putty/src/test/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest.java
@@ -0,0 +1,153 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config.keys.loader.putty;
+
+import java.io.IOException;
+import java.net.URL;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.sshd.common.cipher.BuiltinCiphers;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.config.keys.PrivateKeyEntryDecoder;
+import org.apache.sshd.common.config.keys.loader.openssh.OpenSSHKeyPairResourceParser;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.Assume;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.junit.runners.Parameterized.UseParametersRunnerFactory;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RunWith(Parameterized.class)   // see https://github.com/junit-team/junit/wiki/Parameterized-tests
+@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
+@Category({ NoIoTestCase.class })
+public class PuttyKeyUtilsTest extends JUnitTestSupport {
+    public static final String PASSWORD = "super secret passphrase";
+
+    private final String keyType;
+    private final String regularFile;
+    private final String encryptedFile;
+    private final PuttyKeyPairResourceParser<?, ?> parser;
+
+    public PuttyKeyUtilsTest(String keyType) {
+        this.keyType = keyType;
+        this.parser = PuttyKeyUtils.BY_KEY_TYPE.get(keyType);
+        this.regularFile = getClass().getSimpleName()
+                + "-" + keyType + "-" + KeyPair.class.getSimpleName()
+                + PuttyKeyPairResourceParser.PPK_FILE_SUFFIX;
+        this.encryptedFile = PASSWORD.replace(' ', '-') + "-AES-256-CBC"
+                + "-" + keyType + "-" + KeyPair.class.getSimpleName()
+                + PuttyKeyPairResourceParser.PPK_FILE_SUFFIX;
+    }
+
+    @Parameters(name = "{0}")
+    public static List<Object[]> parameters() {
+        return parameterize(PuttyKeyUtils.BY_KEY_TYPE.keySet());
+    }
+
+    @Test
+    public void testCanDecodePuttyKeyFile() throws IOException, GeneralSecurityException {
+        for (String resource : new String[]{regularFile, encryptedFile}) {
+            URL url = getClass().getResource(resource);
+            if (GenericUtils.isSameReference(regularFile, resource)) {
+                assertNotNull("Missing test resource: " + resource, url);
+            } else {
+                if (url == null) {
+                    outputDebugMessage("Skip non-existing encrypted file: %s", resource);
+                    continue;
+                }
+            }
+
+            List<String> lines = IoUtils.readAllLines(url);
+            assertTrue(resource + " - can extract key pair", parser.canExtractKeyPairs(resource, lines));
+
+            for (PuttyKeyPairResourceParser<?, ?> other : PuttyKeyUtils.BY_KEY_TYPE.values()) {
+                if (parser == other) {
+                    continue;
+                }
+
+                assertFalse(other.getClass().getSimpleName() + "/" + resource + " - unexpected extraction capability",
+                        other.canExtractKeyPairs(resource, lines));
+            }
+        }
+    }
+
+    @Test
+    public void testDecodePuttyKeyFile() throws IOException, GeneralSecurityException {
+        URL url = getClass().getResource(regularFile);
+        assertNotNull("Missing test resource: " + regularFile, url);
+
+        Collection<KeyPair> keys = parser.loadKeyPairs(url, null);
+        assertEquals("Mismatched loaded keys count from " + regularFile, 1, GenericUtils.size(keys));
+        assertLoadedKeyPair(regularFile, keys.iterator().next());
+    }
+
+    @Test
+    public void testDecodeEncryptedPuttyKeyFile() throws IOException, GeneralSecurityException {
+        Assume.assumeTrue(BuiltinCiphers.aes256cbc.getTransformation() + " N/A", BuiltinCiphers.aes256cbc.isSupported());
+        URL url = getClass().getResource(encryptedFile);
+        Assume.assumeTrue("Skip non-existent encrypted file: " + encryptedFile, url != null);
+        assertNotNull("Missing test resource: " + encryptedFile, url);
+
+        Collection<KeyPair> keys = parser.loadKeyPairs(url, r -> PASSWORD);
+        assertEquals("Mismatched loaded keys count from " + encryptedFile, 1, GenericUtils.size(keys));
+
+        assertLoadedKeyPair(encryptedFile, keys.iterator().next());
+    }
+
+    private void assertLoadedKeyPair(String prefix, KeyPair kp) throws GeneralSecurityException {
+        assertNotNull(prefix + ": no key pair loaded", kp);
+
+        PublicKey pubKey = kp.getPublic();
+        assertNotNull(prefix + ": no public key loaded", pubKey);
+        assertEquals(prefix + ": mismatched public key type", keyType, KeyUtils.getKeyType(pubKey));
+
+        PrivateKey prvKey = kp.getPrivate();
+        assertNotNull(prefix + ": no private key loaded", prvKey);
+        assertEquals(prefix + ": mismatched private key type", keyType, KeyUtils.getKeyType(prvKey));
+
+        @SuppressWarnings("rawtypes")
+        PrivateKeyEntryDecoder decoder =
+            OpenSSHKeyPairResourceParser.getPrivateKeyEntryDecoder(prvKey);
+        assertNotNull("No private key decoder", decoder);
+
+        if (decoder.isPublicKeyRecoverySupported()) {
+            @SuppressWarnings("unchecked")
+            PublicKey recKey = decoder.recoverPublicKey(prvKey);
+            assertKeyEquals("Mismatched recovered public key", pubKey, recKey);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-putty/src/test/resources/.gitignore
----------------------------------------------------------------------
diff --git a/sshd-putty/src/test/resources/.gitignore b/sshd-putty/src/test/resources/.gitignore
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-putty/src/test/resources/log4j.properties
----------------------------------------------------------------------
diff --git a/sshd-putty/src/test/resources/log4j.properties b/sshd-putty/src/test/resources/log4j.properties
new file mode 100644
index 0000000..351467b
--- /dev/null
+++ b/sshd-putty/src/test/resources/log4j.properties
@@ -0,0 +1,38 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed  under the  License is distributed on an "AS IS" BASIS,
+# WITHOUT  WARRANTIES OR CONDITIONS  OF ANY KIND, either  express  or
+# implied.
+#
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#
+
+#
+# The logging properties used during tests..
+#
+log4j.rootLogger=INFO, stdout, logfile
+#log4j.logger.org.apache.sshd=TRACE
+#log4j.logger.org.apache.sshd.common.channel.Window=DEBUG
+
+# CONSOLE appender
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n
+
+# File appender
+log4j.appender.logfile=org.apache.log4j.FileAppender
+log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
+log4j.appender.logfile.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n
+log4j.appender.logfile.file=target/sshd-putty-tests.log
+log4j.appender.logfile.append=true

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp256-KeyPair.ppk
----------------------------------------------------------------------
diff --git a/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp256-KeyPair.ppk b/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp256-KeyPair.ppk
new file mode 100644
index 0000000..509538a
--- /dev/null
+++ b/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp256-KeyPair.ppk
@@ -0,0 +1,10 @@
+PuTTY-User-Key-File-2: ecdsa-sha2-nistp256
+Encryption: none
+Comment: ecdsa-key-20170917
+Public-Lines: 3
+AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBM99zj2+E6AN
+xMZ/2SKFP/fAvPfUJUdsgJyn4g7nf36vNpoaRyq1FyHLxyT34AgTl1n3DwcaBXXC
+O5pCv6xFwYk=
+Private-Lines: 1
+AAAAIQDdmu/Dr68r6a0PNbQUN1bX+zS6J5jFsOlEAx8sR73Tuw==
+Private-MAC: 012e0d61593a431ae84beb6216dd29e4b203c1c0

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp384-KeyPair.ppk
----------------------------------------------------------------------
diff --git a/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp384-KeyPair.ppk b/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp384-KeyPair.ppk
new file mode 100644
index 0000000..238261c
--- /dev/null
+++ b/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp384-KeyPair.ppk
@@ -0,0 +1,11 @@
+PuTTY-User-Key-File-2: ecdsa-sha2-nistp384
+Encryption: none
+Comment: ecdsa-key-20170917
+Public-Lines: 3
+AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBNHjIMrdMfXw
+CUqBAhkZw0vXB+qypkiTcL1CmcopmPrKvGHFieFmedeCQotjwJkoAAeb5isZNOXy
+h+7TnHGNrE/pZkHuNwACilpOt659hbhR2OGHX0jdpb8y4RVkuPQssg==
+Private-Lines: 2
+AAAAMHNGt3UPG3evJVl1GRoXXnqTafWLDQdWA2A1tXi8oRW0hhHMRe9/v0SCGL7S
+nL3asg==
+Private-MAC: 6fb6e93559ecacfa468aa5ff9776e4d4283db5ba

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp521-KeyPair.ppk
----------------------------------------------------------------------
diff --git a/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp521-KeyPair.ppk b/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp521-KeyPair.ppk
new file mode 100644
index 0000000..f60a78e
--- /dev/null
+++ b/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp521-KeyPair.ppk
@@ -0,0 +1,12 @@
+PuTTY-User-Key-File-2: ecdsa-sha2-nistp521
+Encryption: none
+Comment: ecdsa-key-20170917
+Public-Lines: 4
+AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBADGM237T9rT
+zE++sOFDN0VWfYfojlQ8dYP82OlgA24Yh0ZpOsezBBiHtHfMHl9tWHmch1YKmH7B
+lOfqbOgcIhz9cwA2V7Nu3IUGqxZT18LOXEpcdyDSphJ6jsy1urqBLrOz4DF6Udyr
+rFV4OQELovf8YdUsM91YPfe1DfnSRi1I5v20uA==
+Private-Lines: 2
+AAAAQgE28iZoHARx+2VzL7Y45FaY44TngX2b4StlC8wOlYF7NY/ba+Pt2RT/WcNL
+ytmLdj5QeV/fFJ9x8i00mTU6KCF2AA==
+Private-MAC: 7379e9986066087dff9339d2b0b968c2b31f45c7

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-dss-KeyPair.ppk
----------------------------------------------------------------------
diff --git a/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-dss-KeyPair.ppk b/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-dss-KeyPair.ppk
new file mode 100644
index 0000000..59a8fac
--- /dev/null
+++ b/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-dss-KeyPair.ppk
@@ -0,0 +1,17 @@
+PuTTY-User-Key-File-2: ssh-dss
+Encryption: none
+Comment: dsa-key-20130709
+Public-Lines: 10
+AAAAB3NzaC1kc3MAAACBAMg/IxsG5BxnF5gM7IKqqR0rftxZC+n5GlbO+J4H+iIb
+/KR8NBehkxG3CrBZMF96M2K1sEGYLob+3k4r71oWaPul8n5rt9kpd+JSq4iD2ygO
+yg6Kd1/YDBHoxneizy6I/bGsLwhAAKWcRNrXmYVKGzhrhvZWN12AJDq2mGdj3szL
+AAAAFQD7a2MltdUSF7FU3//SpW4WGjZbeQAAAIBf0nNsfKQL/TEMo7IpTrEMg5V0
+RnSigCX0+yUERS42GW/ZeCZBJw7oL2XZbuBtu63vMjDgVpnb92BdrcPgjJ7EFW6D
+lcyeuywStmg1ygXmDR2AQCxv0eX2CQgrdUczmRa155SDVUTvTQlO1IyKx0vwKAh1
+H7E3yJUfkTAJstbGYQAAAIEAtv+cdRfNevYFkp55jVqazc8zRLvfb64jzgc5oSJV
+c64kFs4yx+abYpGX9WxNxDlG6g2WiY8voDBB0YnUJsn0kVRjBKX9OceROxrfT4K4
+dVbQZsdt+SLaXWL4lGJFrFZL3LZqvySvq6xfhJfakQDDivW4hUOhFPXPHrE5/Ia3
+T7A=
+Private-Lines: 1
+AAAAFQCE6flGnmVCAbzo9YsbdJWBnxMnBA==
+Private-MAC: 6ace0a5e5bf23649c1375e91dcd71d1def6c6aa1

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-ed25519-KeyPair.ppk
----------------------------------------------------------------------
diff --git a/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-ed25519-KeyPair.ppk b/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-ed25519-KeyPair.ppk
new file mode 100644
index 0000000..614ac69
--- /dev/null
+++ b/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-ed25519-KeyPair.ppk
@@ -0,0 +1,9 @@
+PuTTY-User-Key-File-2: ssh-ed25519
+Encryption: none
+Comment: ed25519-key-20170917
+Public-Lines: 2
+AAAAC3NzaC1lZDI1NTE5AAAAIN7fuKSIM5TbAX/1I1Ts3tfyo5eEs7JpmKsegHs/
+9fIi
+Private-Lines: 1
+AAAAIADKJJPxsUp7JXLzm1zwk8UswW/lkiwPJ73CbqGvalgP
+Private-MAC: 28a22234152feaf1d9a6a10ca0ae3a51b5e6dd52

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-rsa-KeyPair.ppk
----------------------------------------------------------------------
diff --git a/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-rsa-KeyPair.ppk b/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-rsa-KeyPair.ppk
new file mode 100644
index 0000000..da2868e
--- /dev/null
+++ b/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-rsa-KeyPair.ppk
@@ -0,0 +1,18 @@
+PuTTY-User-Key-File-2: ssh-rsa
+Encryption: none
+Comment: rsa-key-20130709
+Public-Lines: 4
+AAAAB3NzaC1yc2EAAAABJQAAAIBLSj7+SllQdWjkD8EN4sA/fUv/jhc+ssGmCYx3
+uoiPMxjKH3xUPWu4zxJzhdlgFy4gchzjU51fDS4Oel6xGoWbGKGe4ZLQdE/t8N8l
+jAfOm/5lGp5tFhHs9UHoSm/h3RsErWNjKPjTGIlID35IcOXVhfhp9fX0RU6y/ZBI
+PhM20w==
+Private-Lines: 8
+AAAAgBx89T2fl2qNSkh0qemUEWQhkmCyTfwMSUW+bIBUawXATpGrDHLmztBOWgIy
+pUb0A52S9iyAgLwugCEnYhl/qCxvoARH7ZyTdYAL4KjJDySxVuqeo/ZhLscYcMAz
+MOyn8g5cR4dRgEwJ1/pRuK8r4+Z96zJG4NlxlHsUjHuj7t1dAAAAQQCTrj48XKIX
+M3dxWLSsSXbUCOpmAOTviuz9LD0H1ik7a6ebr0P6GTl9z7iscBgzdjBIHMFcdvar
+ophUJ5iRanCvAAAAQQCCg1VU1H5FHMipRvvw8b/zRqDsx6GTZs03ffhyKrTl1Dcd
+0oKy5/U3kQwdXPOSlVZeyX8nUVE2o7DOh7INsX0dAAAAQHkPjxivrN0SQuVAx+tK
+uRJ8vGL7sBOZ9gdTS25T3BVEkhRt37aDcshrodzDCzd515cwhmbLSsOsgyxcTwcX
+7SA=
+Private-MAC: 2416438f1a7ebdd33d519f6102d843b5f2c565d4

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp256-KeyPair.ppk
----------------------------------------------------------------------
diff --git a/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp256-KeyPair.ppk b/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp256-KeyPair.ppk
new file mode 100644
index 0000000..4c601c7
--- /dev/null
+++ b/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp256-KeyPair.ppk
@@ -0,0 +1,10 @@
+PuTTY-User-Key-File-2: ecdsa-sha2-nistp256
+Encryption: aes256-cbc
+Comment: ecdsa-key-20170917
+Public-Lines: 3
+AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBM99zj2+E6AN
+xMZ/2SKFP/fAvPfUJUdsgJyn4g7nf36vNpoaRyq1FyHLxyT34AgTl1n3DwcaBXXC
+O5pCv6xFwYk=
+Private-Lines: 1
+/8MdniIqAaST5t3/bRx4mFdFxqN8jIwI0Hh7VTy8IV143j+PktgGIoHsUwTiEujQ
+Private-MAC: be4b37d4b65ad09e6890534a2ba355599da796c6

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp384-KeyPair.ppk
----------------------------------------------------------------------
diff --git a/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp384-KeyPair.ppk b/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp384-KeyPair.ppk
new file mode 100644
index 0000000..f57f5fd
--- /dev/null
+++ b/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp384-KeyPair.ppk
@@ -0,0 +1,11 @@
+PuTTY-User-Key-File-2: ecdsa-sha2-nistp384
+Encryption: aes256-cbc
+Comment: ecdsa-key-20170917
+Public-Lines: 3
+AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBNHjIMrdMfXw
+CUqBAhkZw0vXB+qypkiTcL1CmcopmPrKvGHFieFmedeCQotjwJkoAAeb5isZNOXy
+h+7TnHGNrE/pZkHuNwACilpOt659hbhR2OGHX0jdpb8y4RVkuPQssg==
+Private-Lines: 2
+MOgJnSZ8ZqGHFVtQvYKpOyTGWjMVIjIOMIUwhbxBuTiQ7WEyNPn9jjTsSwXtJxrG
+UI6NGeIqZ41P2e8JINhMIg==
+Private-MAC: 5ec40946270150ddfca35cce61f4265d7bfe7b7f

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp521-KeyPair.ppk
----------------------------------------------------------------------
diff --git a/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp521-KeyPair.ppk b/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp521-KeyPair.ppk
new file mode 100644
index 0000000..97077fd
--- /dev/null
+++ b/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp521-KeyPair.ppk
@@ -0,0 +1,12 @@
+PuTTY-User-Key-File-2: ecdsa-sha2-nistp521
+Encryption: aes256-cbc
+Comment: ecdsa-key-20170917
+Public-Lines: 4
+AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBADGM237T9rT
+zE++sOFDN0VWfYfojlQ8dYP82OlgA24Yh0ZpOsezBBiHtHfMHl9tWHmch1YKmH7B
+lOfqbOgcIhz9cwA2V7Nu3IUGqxZT18LOXEpcdyDSphJ6jsy1urqBLrOz4DF6Udyr
+rFV4OQELovf8YdUsM91YPfe1DfnSRi1I5v20uA==
+Private-Lines: 2
++44AQO4aflPquBZbdB3UtdLuXuHV2u1YoghxYXPFGdhskMt+XjJhUlrOHNX8rmgR
+E9nni474zGw9ni/3LIZwMILQI/xTfiQm3t6nKFV8nyI=
+Private-MAC: 99c25e348a84de4876163758ad13b2ad1dc43629

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-dss-KeyPair.ppk
----------------------------------------------------------------------
diff --git a/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-dss-KeyPair.ppk b/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-dss-KeyPair.ppk
new file mode 100644
index 0000000..9da31f1
--- /dev/null
+++ b/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-dss-KeyPair.ppk
@@ -0,0 +1,17 @@
+PuTTY-User-Key-File-2: ssh-dss
+Encryption: aes256-cbc
+Comment: dsa-key-20130909
+Public-Lines: 10
+AAAAB3NzaC1kc3MAAACBALVdprWfZ7+FiITCILnDkeMZ2ntkV2WjW5RcyiQvJvBO
+jCNiVtK87xATEOfBb20YvNZ/CibBjGS1TL5TBqRV5XleucPHMJZ5rXdJ2FH5oZnL
+kna3Et+L1/O/GQMmp2vfSFrO3n3+mI1Jozx3FoQO8jr1zIerJ5Mc4LKqsIQB9hvR
+AAAAFQD+z1y1/4ll4ax3rri8mkYgGDhqIQAAAIBVU4VJ7V7GoEQJ5WBMbpDEcLIZ
+KUgSHsJMQzWnLOi/DcsPjVMDX6FWGPLtrjd7fgInlPMCC/SPAhXdaXMvHZSkvBHV
+DfNjpsDgsxBnK1FKqRGtD49rETFGDl92EOsyBhv+9ymdOPX6R0hCqS+ulZheQPXI
+iHXdIvQK2Ev5Dy3xNgAAAIA8qhumHZcKss+Puuw+mY5J5Qt7Omv16MuDsYiVqrBq
+1V2C9gutx3Tu+n5QYi0xPlkkP/knMtkUZS+Wt3Dr8zPcEzNBc/Tm2EdYp11jZNx4
+4PM4ing+aCU5oGcg/7RS5CrY5Fn/rvgHqK22XiC8/U55iti44bWKvI6HCejExeZX
+iA==
+Private-Lines: 1
+64IdcIX48CNGjhcxsVN0vSNpT7S72e3FVdQ3t4ENAvI=
+Private-MAC: 8e62b44b5c080965e361936520be4feaa50285b1

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-ed25519-KeyPair.ppk
----------------------------------------------------------------------
diff --git a/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-ed25519-KeyPair.ppk b/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-ed25519-KeyPair.ppk
new file mode 100644
index 0000000..668ef1e
--- /dev/null
+++ b/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-ed25519-KeyPair.ppk
@@ -0,0 +1,9 @@
+PuTTY-User-Key-File-2: ssh-ed25519
+Encryption: aes256-cbc
+Comment: ed25519-key-20170917
+Public-Lines: 2
+AAAAC3NzaC1lZDI1NTE5AAAAIN7fuKSIM5TbAX/1I1Ts3tfyo5eEs7JpmKsegHs/
+9fIi
+Private-Lines: 1
+0cPG5BR80jQcJmHKs6IjpHS3R4/CTnudnJB4BcjaqKlRk0l603GVMDzTxkaICCb8
+Private-MAC: 381cff136b2516331ff4511cf382533fc14f0aeb

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-rsa-KeyPair.ppk
----------------------------------------------------------------------
diff --git a/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-rsa-KeyPair.ppk b/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-rsa-KeyPair.ppk
new file mode 100644
index 0000000..2a11d04
--- /dev/null
+++ b/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-rsa-KeyPair.ppk
@@ -0,0 +1,18 @@
+PuTTY-User-Key-File-2: ssh-rsa
+Encryption: aes256-cbc
+Comment: rsa-key-20130909
+Public-Lines: 4
+AAAAB3NzaC1yc2EAAAABJQAAAIEAhY5eZJrX0a3+rtEZCq0nu2zvAHp16nk93jhi
+p7c9tTDlGm9QEAgqzmuilEeUQ4BssxAvhCFEo/7Qbg4M7PwcA5cFkjXE4gj0YDJM
+ay7l2mb5aIoS/hACgNz54p/w/UgfQC1Vygt6QtvXXAW8Lh/YCN4Zw4ViROUhoYuy
+3K5SBYs=
+Private-Lines: 8
+mqcGPnrv9d1tYkJZSGaCy5REslPZ2xh8m7qAbN+bD1m7iQ77pLxlKyzs82rbRaC9
+KSnKwsbFl7o92NT+9yYKJ7ehXyWyrUXkn9KcPk7MzNVwMuWVDXwvHodGLCyVCLYq
+PNipvg2USHvnCjnnvtMysBRNJiHTMOaf/gSZLyaEuznYo3FEClMPzggY9b2nrxnV
+O1ttk1FJatkRflwFjn3A/R/GpowmBnkDyCkVlTvR+uBAg8iIy1Vzj5zIV9zmzfgx
+DxPot+Y81y+Xe3ohVh2s1FVvLw+KQbYbCQam5j0V/dTQ+oVWjCJBlibD3aVTGK0M
+Jswz8wPwXFo5N0yX/6ZTrshbvTzoO1bg0+HUu581ZSAeqttk9C1RLmWFS8YDm0Hn
+GhDXrjuAvKJ3cjeVJsumgVw45NYGARuzV24TlHUtU+eze8Y/0NsPJXoCfVoYjTjb
+fjlMh9rYbRdyNHXwYTzwbw==
+Private-MAC: f4c50b3da0b73c34e8989411fc48c884c09e20a0


[51/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common utilities code from sshd-core into sshd-common (new artifact)

Posted by lg...@apache.org.
[SSHD-842] Split common utilities code from sshd-core into sshd-common (new artifact)


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

Branch: refs/heads/master
Commit: 10de190e7d3f9189deb76b8d08c72334a1fe2df0
Parents: 1e82547
Author: Goldstein Lyor <ly...@cb4.com>
Authored: Wed Sep 5 18:05:53 2018 +0300
Committer: Lyor Goldstein <ly...@gmail.com>
Committed: Thu Sep 6 19:07:54 2018 +0300

----------------------------------------------------------------------
 assembly/pom.xml                                |    5 +
 pom.xml                                         |    1 +
 sshd-cli/pom.xml                                |    7 +
 .../java/org/apache/sshd/cli/CliSupport.java    |    6 +-
 .../sshd/cli/client/SshClientCliSupport.java    |   16 +-
 .../apache/sshd/cli/client/SshKeyScanMain.java  |    4 +-
 .../sshd/cli/server/SshServerCliSupport.java    |    4 +-
 .../apache/sshd/cli/server/SshServerMain.java   |    5 +-
 .../apache/sshd/cli/server/SshFsMounter.java    |   11 +-
 sshd-common/pom.xml                             |  128 ++
 .../org/apache/sshd/sshd-version.properties     |   23 +
 .../auth/AuthenticationIdentitiesProvider.java  |  125 ++
 .../auth/hostbased/HostKeyIdentityProvider.java |   53 +
 .../auth/password/PasswordIdentityProvider.java |  169 +++
 .../client/auth/pubkey/PublicKeyIdentity.java   |   42 +
 .../hosts/ConfigFileHostEntryResolver.java      |  107 ++
 .../DefaultConfigFileHostEntryResolver.java     |   95 ++
 .../client/config/hosts/HostConfigEntry.java    | 1169 ++++++++++++++++++
 .../config/hosts/HostConfigEntryResolver.java   |   60 +
 .../client/config/hosts/HostPatternValue.java   |   97 ++
 .../client/config/hosts/HostPatternsHolder.java |  343 +++++
 .../client/config/hosts/KnownHostDigest.java    |   66 +
 .../client/config/hosts/KnownHostEntry.java     |  276 +++++
 .../client/config/hosts/KnownHostHashValue.java |  170 +++
 .../keys/BuiltinClientIdentitiesWatcher.java    |  106 ++
 .../config/keys/ClientIdentitiesWatcher.java    |  139 +++
 .../sshd/client/config/keys/ClientIdentity.java |  268 ++++
 .../config/keys/ClientIdentityFileWatcher.java  |  141 +++
 .../config/keys/ClientIdentityLoader.java       |   95 ++
 .../config/keys/ClientIdentityProvider.java     |   42 +
 .../keys/DefaultClientIdentitiesWatcher.java    |   66 +
 .../org/apache/sshd/common/AttributeStore.java  |   97 ++
 .../org/apache/sshd/common/BuiltinFactory.java  |   40 +
 .../java/org/apache/sshd/common/Closeable.java  |  126 ++
 .../java/org/apache/sshd/common/Factory.java    |   41 +
 .../org/apache/sshd/common/NamedFactory.java    |   66 +
 .../org/apache/sshd/common/NamedResource.java   |  104 ++
 .../org/apache/sshd/common/OptionalFeature.java |   92 ++
 .../apache/sshd/common/PropertyResolver.java    |  124 ++
 .../sshd/common/PropertyResolverUtils.java      |  482 ++++++++
 .../apache/sshd/common/RuntimeSshException.java |   48 +
 .../org/apache/sshd/common/SshConstants.java    |  245 ++++
 .../org/apache/sshd/common/SshException.java    |   72 ++
 .../apache/sshd/common/SyspropsMapWrapper.java  |  209 ++++
 .../sshd/common/auth/MutableUserHolder.java     |   27 +
 .../apache/sshd/common/auth/UsernameHolder.java |   32 +
 .../apache/sshd/common/cipher/BaseCipher.java   |  113 ++
 .../sshd/common/cipher/BaseRC4Cipher.java       |   54 +
 .../sshd/common/cipher/BuiltinCiphers.java      |  348 ++++++
 .../org/apache/sshd/common/cipher/Cipher.java   |   89 ++
 .../sshd/common/cipher/CipherFactory.java       |   31 +
 .../sshd/common/cipher/CipherInformation.java   |   45 +
 .../apache/sshd/common/cipher/CipherNone.java   |   63 +
 .../org/apache/sshd/common/cipher/ECCurves.java |  580 +++++++++
 .../org/apache/sshd/common/cipher/package.html  |   26 +
 .../common/compression/BaseCompression.java     |   48 +
 .../common/compression/BuiltinCompressions.java |  239 ++++
 .../sshd/common/compression/Compression.java    |   71 ++
 .../compression/CompressionDelayedZlib.java     |   40 +
 .../common/compression/CompressionFactory.java  |   31 +
 .../compression/CompressionInformation.java     |   42 +
 .../common/compression/CompressionNone.java     |   76 ++
 .../common/compression/CompressionZlib.java     |   85 ++
 .../apache/sshd/common/compression/package.html |   25 +
 .../common/config/CompressionConfigValue.java   |   94 ++
 .../common/config/ConfigFileReaderSupport.java  |  230 ++++
 .../common/config/FactoriesListParseResult.java |   51 +
 .../sshd/common/config/ListParseResult.java     |   66 +
 .../sshd/common/config/LogLevelValue.java       |   56 +
 .../config/NamedFactoriesListParseResult.java   |   47 +
 .../config/NamedResourceListParseResult.java    |   57 +
 .../sshd/common/config/SyslogFacilityValue.java |   51 +
 .../sshd/common/config/TimeValueConfig.java     |  180 +++
 .../sshd/common/config/VersionProperties.java   |   98 ++
 .../common/config/keys/AuthorizedKeyEntry.java  |  480 +++++++
 .../common/config/keys/BuiltinIdentities.java   |  212 ++++
 .../config/keys/FilePasswordProvider.java       |   45 +
 .../sshd/common/config/keys/Identity.java       |   42 +
 .../config/keys/IdentityResourceLoader.java     |   49 +
 .../sshd/common/config/keys/IdentityUtils.java  |  159 +++
 .../common/config/keys/KeyEntryResolver.java    |  190 +++
 .../sshd/common/config/keys/KeyRandomArt.java   |  310 +++++
 .../sshd/common/config/keys/KeyUtils.java       |  937 ++++++++++++++
 .../config/keys/PrivateKeyEntryDecoder.java     |  142 +++
 .../config/keys/PrivateKeyEntryResolver.java    |   70 ++
 .../sshd/common/config/keys/PublicKeyEntry.java |  286 +++++
 .../config/keys/PublicKeyEntryDecoder.java      |  114 ++
 .../config/keys/PublicKeyEntryResolver.java     |   70 ++
 .../impl/AbstractIdentityResourceLoader.java    |   62 +
 .../keys/impl/AbstractKeyEntryResolver.java     |   59 +
 .../impl/AbstractPrivateKeyEntryDecoder.java    |   40 +
 .../impl/AbstractPublicKeyEntryDecoder.java     |   41 +
 .../keys/impl/DSSPublicKeyEntryDecoder.java     |  119 ++
 .../keys/impl/ECDSAPublicKeyEntryDecoder.java   |  178 +++
 .../config/keys/impl/RSAPublicKeyDecoder.java   |  117 ++
 .../keys/loader/AESPrivateKeyObfuscator.java    |  110 ++
 .../loader/AbstractKeyPairResourceParser.java   |  181 +++
 .../loader/AbstractPrivateKeyObfuscator.java    |  191 +++
 .../keys/loader/DESPrivateKeyObfuscator.java    |   76 ++
 .../keys/loader/KeyPairResourceLoader.java      |  129 ++
 .../keys/loader/KeyPairResourceParser.java      |  192 +++
 .../loader/PrivateKeyEncryptionContext.java     |  270 ++++
 .../keys/loader/PrivateKeyObfuscator.java       |   64 +
 .../OpenSSHDSSPrivateKeyEntryDecoder.java       |  139 +++
 .../OpenSSHECDSAPrivateKeyEntryDecoder.java     |  164 +++
 .../openssh/OpenSSHKeyPairResourceParser.java   |  358 ++++++
 .../loader/openssh/OpenSSHParserContext.java    |   83 ++
 .../openssh/OpenSSHRSAPrivateKeyDecoder.java    |  135 ++
 .../pem/AbstractPEMResourceKeyPairParser.java   |  167 +++
 .../loader/pem/DSSPEMResourceKeyPairParser.java |  126 ++
 .../pem/ECDSAPEMResourceKeyPairParser.java      |  220 ++++
 .../loader/pem/KeyPairPEMResourceParser.java    |   37 +
 .../keys/loader/pem/PEMResourceParserUtils.java |  109 ++
 .../pem/PKCS8PEMResourceKeyPairParser.java      |  156 +++
 .../loader/pem/RSAPEMResourceKeyPairParser.java |  142 +++
 .../apache/sshd/common/digest/BaseDigest.java   |  156 +++
 .../sshd/common/digest/BuiltinDigests.java      |  166 +++
 .../org/apache/sshd/common/digest/Digest.java   |   36 +
 .../sshd/common/digest/DigestFactory.java       |   32 +
 .../sshd/common/digest/DigestInformation.java   |   36 +
 .../apache/sshd/common/digest/DigestUtils.java  |  228 ++++
 .../org/apache/sshd/common/digest/package.html  |   25 +
 .../sshd/common/future/AbstractSshFuture.java   |  194 +++
 .../apache/sshd/common/future/CloseFuture.java  |   40 +
 .../sshd/common/future/DefaultCloseFuture.java  |   52 +
 .../sshd/common/future/DefaultSshFuture.java    |  236 ++++
 .../future/DefaultVerifiableSshFuture.java      |   30 +
 .../apache/sshd/common/future/SshFuture.java    |   48 +
 .../sshd/common/future/SshFutureListener.java   |   42 +
 .../sshd/common/future/VerifiableFuture.java    |   68 +
 .../sshd/common/future/WaitableFuture.java      |  118 ++
 .../keyprovider/AbstractKeyPairProvider.java    |   32 +
 .../AbstractResourceKeyPairProvider.java        |  234 ++++
 .../ClassLoadableResourceKeyPairProvider.java   |  113 ++
 .../common/keyprovider/FileKeyPairProvider.java |   92 ++
 .../common/keyprovider/KeyIdentityProvider.java |  170 +++
 .../common/keyprovider/KeyPairProvider.java     |  181 +++
 .../keyprovider/KeyPairProviderHolder.java      |   35 +
 .../keyprovider/MappedKeyPairProvider.java      |   97 ++
 .../org/apache/sshd/common/mac/BaseMac.java     |  111 ++
 .../org/apache/sshd/common/mac/BuiltinMacs.java |  273 ++++
 .../java/org/apache/sshd/common/mac/Mac.java    |   52 +
 .../org/apache/sshd/common/mac/MacFactory.java  |   31 +
 .../apache/sshd/common/mac/MacInformation.java  |   41 +
 .../org/apache/sshd/common/mac/package.html     |   25 +
 .../sshd/common/random/AbstractRandom.java      |   34 +
 .../common/random/AbstractRandomFactory.java    |   43 +
 .../apache/sshd/common/random/JceRandom.java    |   60 +
 .../sshd/common/random/JceRandomFactory.java    |   42 +
 .../org/apache/sshd/common/random/Random.java   |   56 +
 .../sshd/common/random/RandomFactory.java       |   31 +
 .../common/random/SingletonRandomFactory.java   |   70 ++
 .../org/apache/sshd/common/random/package.html  |   25 +
 .../common/signature/AbstractSignature.java     |  149 +++
 .../common/signature/BuiltinSignatures.java     |  305 +++++
 .../apache/sshd/common/signature/Signature.java |   88 ++
 .../sshd/common/signature/SignatureDSA.java     |  142 +++
 .../sshd/common/signature/SignatureECDSA.java   |  144 +++
 .../signature/SignatureFactoriesManager.java    |   94 ++
 .../sshd/common/signature/SignatureFactory.java |   31 +
 .../sshd/common/signature/SignatureRSA.java     |   89 ++
 .../apache/sshd/common/signature/package.html   |   25 +
 .../sshd/common/util/EventListenerUtils.java    |  212 ++++
 .../apache/sshd/common/util/EventNotifier.java  |   35 +
 .../apache/sshd/common/util/GenericUtils.java   |  915 ++++++++++++++
 .../sshd/common/util/IgnoringEmptyMap.java      |  128 ++
 .../sshd/common/util/Int2IntFunction.java       |   66 +
 .../org/apache/sshd/common/util/Invoker.java    |  116 ++
 .../apache/sshd/common/util/MapEntryUtils.java  |   51 +
 .../apache/sshd/common/util/NumberUtils.java    |  310 +++++
 .../apache/sshd/common/util/ObjectBuilder.java  |   38 +
 .../org/apache/sshd/common/util/OsUtils.java    |  257 ++++
 .../org/apache/sshd/common/util/Readable.java   |   53 +
 .../sshd/common/util/ReflectionUtils.java       |   53 +
 .../apache/sshd/common/util/SelectorUtils.java  |  805 ++++++++++++
 .../sshd/common/util/SshdEventListener.java     |   44 +
 .../apache/sshd/common/util/ValidateUtils.java  |  216 ++++
 .../apache/sshd/common/util/VersionInfo.java    |  137 ++
 .../apache/sshd/common/util/buffer/Buffer.java  |  798 ++++++++++++
 .../common/util/buffer/BufferException.java     |   30 +
 .../sshd/common/util/buffer/BufferUtils.java    |  604 +++++++++
 .../common/util/buffer/ByteArrayBuffer.java     |  253 ++++
 .../keys/AbstractBufferPublicKeyParser.java     |   88 ++
 .../util/buffer/keys/BufferPublicKeyParser.java |  111 ++
 .../buffer/keys/DSSBufferPublicKeyParser.java   |   52 +
 .../buffer/keys/ECBufferPublicKeyParser.java    |   81 ++
 .../keys/ED25519BufferPublicKeyParser.java      |   47 +
 .../buffer/keys/RSABufferPublicKeyParser.java   |   49 +
 .../util/closeable/AbstractCloseable.java       |  162 +++
 .../util/closeable/AbstractInnerCloseable.java  |   48 +
 .../sshd/common/util/closeable/Builder.java     |  115 ++
 .../common/util/closeable/FuturesCloseable.java |   76 ++
 .../common/util/closeable/IoBaseCloseable.java  |   35 +
 .../util/closeable/ParallelCloseable.java       |   73 ++
 .../util/closeable/SequentialCloseable.java     |   71 ++
 .../common/util/closeable/SimpleCloseable.java  |   71 ++
 .../util/io/CloseableEmptyInputStream.java      |   96 ++
 .../sshd/common/util/io/DirectoryScanner.java   |  380 ++++++
 .../sshd/common/util/io/EmptyInputStream.java   |   66 +
 .../sshd/common/util/io/FileInfoExtractor.java  |   53 +
 .../common/util/io/InputStreamWithChannel.java  |   32 +
 .../org/apache/sshd/common/util/io/IoUtils.java |  556 +++++++++
 .../sshd/common/util/io/LimitInputStream.java   |  113 ++
 .../util/io/LoggingFilterOutputStream.java      |   67 +
 .../common/util/io/ModifiableFileWatcher.java   |  258 ++++
 .../sshd/common/util/io/NoCloseInputStream.java |   47 +
 .../common/util/io/NoCloseOutputStream.java     |   47 +
 .../sshd/common/util/io/NoCloseReader.java      |   46 +
 .../sshd/common/util/io/NoCloseWriter.java      |   46 +
 .../sshd/common/util/io/NullInputStream.java    |   90 ++
 .../sshd/common/util/io/NullOutputStream.java   |   72 ++
 .../common/util/io/OutputStreamWithChannel.java |   32 +
 .../sshd/common/util/io/der/ASN1Class.java      |   93 ++
 .../sshd/common/util/io/der/ASN1Object.java     |  338 +++++
 .../sshd/common/util/io/der/ASN1Type.java       |  118 ++
 .../sshd/common/util/io/der/DERParser.java      |  151 +++
 .../sshd/common/util/io/der/DERWriter.java      |  172 +++
 .../common/util/io/functors/IOFunction.java     |   86 ++
 .../util/logging/AbstractLoggingBean.java       |   72 ++
 .../sshd/common/util/logging/LoggingUtils.java  |  549 ++++++++
 .../sshd/common/util/logging/SimplifiedLog.java |   55 +
 .../sshd/common/util/net/NetworkConnector.java  |   90 ++
 .../sshd/common/util/net/SshdSocketAddress.java |  639 ++++++++++
 .../AbstractSecurityProviderRegistrar.java      |  129 ++
 .../util/security/SecurityEntityFactory.java    |  194 +++
 .../util/security/SecurityProviderChoice.java   |  130 ++
 .../security/SecurityProviderRegistrar.java     |  337 +++++
 .../common/util/security/SecurityUtils.java     |  759 ++++++++++++
 .../BouncyCastleGeneratorHostKeyProvider.java   |   48 +
 .../BouncyCastleKeyPairResourceParser.java      |  130 ++
 .../bouncycastle/BouncyCastleRandom.java        |   88 ++
 .../bouncycastle/BouncyCastleRandomFactory.java |   45 +
 .../BouncyCastleSecurityProviderRegistrar.java  |  128 ++
 .../security/eddsa/Ed25519PublicKeyDecoder.java |   97 ++
 .../eddsa/EdDSASecurityProviderRegistrar.java   |  104 ++
 .../eddsa/EdDSASecurityProviderUtils.java       |  201 +++
 .../OpenSSHEd25519PrivateKeyEntryDecoder.java   |  172 +++
 .../util/security/eddsa/SignatureEd25519.java   |   49 +
 .../util/threads/CloseableExecutorService.java  |   28 +
 .../util/threads/ExecutorServiceCarrier.java    |   31 +
 .../common/util/threads/NoCloseExecutor.java    |  160 +++
 .../util/threads/SshThreadPoolExecutor.java     |  138 +++
 .../common/util/threads/SshdThreadFactory.java  |   78 ++
 .../sshd/common/util/threads/ThreadUtils.java   |  185 +++
 .../AbstractGeneratorHostKeyProvider.java       |  293 +++++
 .../SimpleGeneratorHostKeyProvider.java         |   67 +
 .../password/PasswordIdentityProviderTest.java  |   72 ++
 .../hosts/ConfigFileHostEntryResolverTest.java  |  139 +++
 .../config/hosts/HostConfigEntryTest.java       |  325 +++++
 .../config/hosts/KnownHostHashValueTest.java    |   79 ++
 .../BuiltinClientIdentitiesWatcherTest.java     |  161 +++
 .../keys/ClientIdentityFileWatcherTest.java     |  121 ++
 .../client/config/keys/ClientIdentityTest.java  |  100 ++
 .../apache/sshd/common/SshConstantsTest.java    |   75 ++
 .../sshd/common/VersionPropertiesTest.java      |   45 +
 .../sshd/common/cipher/AES192CTRTest.java       |   41 +
 .../sshd/common/cipher/AES256CBCTest.java       |   41 +
 .../sshd/common/cipher/ARCFOUR128Test.java      |   39 +
 .../sshd/common/cipher/ARCFOUR256Test.java      |   41 +
 .../sshd/common/cipher/BaseCipherTest.java      |   95 ++
 .../apache/sshd/common/cipher/ECCurvesTest.java |  107 ++
 .../compression/BuiltinCompressionsTest.java    |  171 +++
 .../sshd/common/config/TimeValueConfigTest.java |   57 +
 ...AuthorizedKeyEntryLoginOptionsParseTest.java |  124 ++
 .../config/keys/BuiltinIdentitiesTest.java      |  116 ++
 .../common/config/keys/KeyRandomArtTest.java    |  117 ++
 .../common/config/keys/KeyUtilsCloneTest.java   |  120 ++
 .../KeyUtilsFingerprintCaseSensitivityTest.java |   96 ++
 .../keys/KeyUtilsFingerprintGenerationTest.java |  159 +++
 .../sshd/common/config/keys/KeyUtilsTest.java   |  157 +++
 .../common/config/keys/PublicKeyEntryTest.java  |   62 +
 .../loader/AESPrivateKeyObfuscatorTest.java     |   79 ++
 .../OpenSSHKeyPairResourceParserTest.java       |  108 ++
 .../pem/PKCS8PEMResourceKeyPairParserTest.java  |  107 ++
 .../sshd/common/digest/BuiltinDigestsTest.java  |   65 +
 .../common/future/DefaultSshFutureTest.java     |  137 ++
 .../common/keyprovider/KeyPairProviderTest.java |   79 ++
 .../apache/sshd/common/mac/BuiltinMacsTest.java |  166 +++
 .../apache/sshd/common/mac/MacVectorsTest.java  |  311 +++++
 .../sshd/common/random/RandomFactoryTest.java   |   83 ++
 .../common/signature/BuiltinSignaturesTest.java |  149 +++
 .../sshd/common/signature/SignatureDSATest.java |  109 ++
 .../sshd/common/signature/SignatureRSATest.java |  119 ++
 .../common/signature/SignaturesDevelopment.java |   81 ++
 .../common/util/EventListenerUtilsTest.java     |  149 +++
 .../sshd/common/util/GenericUtilsTest.java      |  173 +++
 .../sshd/common/util/Int2IntFunctionTest.java   |  154 +++
 .../sshd/common/util/NumberUtilsTest.java       |   77 ++
 .../apache/sshd/common/util/OsUtilsTest.java    |  135 ++
 .../sshd/common/util/SelectorUtilsTest.java     |  147 +++
 .../sshd/common/util/ThreadUtilsTest.java       |   72 ++
 .../sshd/common/util/ValidateUtilsTest.java     |   44 +
 .../sshd/common/util/VersionInfoTest.java       |   52 +
 .../sshd/common/util/buffer/BufferTest.java     |  103 ++
 .../common/util/buffer/BufferUtilsTest.java     |   73 ++
 .../util/closeable/CloseableUtilsTest.java      |  163 +++
 .../common/util/io/EmptyInputStreamTest.java    |  120 ++
 .../apache/sshd/common/util/io/IoUtilsTest.java |   61 +
 .../common/util/io/LimitInputStreamTest.java    |  118 ++
 .../util/io/ModifiableFileWatcherTest.java      |   82 ++
 .../common/util/io/NoCloseInputStreamTest.java  |   89 ++
 .../common/util/io/NoCloseOutputStreamTest.java |   69 ++
 .../sshd/common/util/io/NoCloseReaderTest.java  |   95 ++
 .../sshd/common/util/io/NoCloseWriterTest.java  |   73 ++
 .../common/util/io/NullInputStreamTest.java     |  118 ++
 .../common/util/io/NullOutputStreamTest.java    |   80 ++
 .../sshd/common/util/io/der/ASN1ClassTest.java  |   67 +
 .../sshd/common/util/io/der/ASN1TypeTest.java   |   67 +
 .../sshd/common/util/io/der/DERParserTest.java  |   61 +
 .../sshd/common/util/io/der/DERWriterTest.java  |   64 +
 .../util/net/SshdSocketIpv6AddressTest.java     |   88 ++
 ...SecurityProviderRegistrarCipherNameTest.java |   75 ++
 .../SecurityProviderRegistrarTestSupport.java   |   62 +
 .../common/util/security/SecurityUtilsTest.java |  231 ++++
 .../util/security/eddsa/EDDSAProviderTest.java  |  126 ++
 .../util/security/eddsa/Ed25519VectorsTest.java |  238 ++++
 .../EdDSASecurityProviderRegistrarTest.java     |   85 ++
 .../AbstractGeneratorHostKeyProviderTest.java   |   83 ++
 .../PEMGeneratorHostKeyProviderTest.java        |  141 +++
 .../SimpleGeneratorHostKeyProviderTest.java     |  133 ++
 .../sshd/util/test/CommonTestSupportUtils.java  |  620 ++++++++++
 .../test/JUnit4ClassRunnerWithParameters.java   |   48 +
 .../JUnit4ClassRunnerWithParametersFactory.java |   58 +
 .../test/JUnit4SingleInstanceClassRunner.java   |   54 +
 .../apache/sshd/util/test/JUnitTestSupport.java |  572 +++++++++
 .../org/apache/sshd/util/test/NoIoTestCase.java |   30 +
 .../test/OutputCountTrackingOutputStream.java   |   56 +
 .../apache/sshd/util/test/TeeOutputStream.java  |   64 +
 sshd-common/src/test/resources/log4j.properties |   38 +
 .../testReadGlobalHostsConfigEntries.config.txt |   22 +
 .../testReadMultipleHostPatterns.config.txt     |    5 +
 .../testReadSimpleHostsConfigEntries.config.txt |    8 +
 .../org/apache/sshd/client/config/keys/id_dsa   |   12 +
 .../org/apache/sshd/client/config/keys/id_ecdsa |    5 +
 .../org/apache/sshd/client/config/keys/id_rsa   |   27 +
 ...OpenSSHKeyPairResourceParserTest-DSA-KeyPair |   21 +
 ...SSHKeyPairResourceParserTest-DSA-KeyPair.pub |    1 +
 ...enSSHKeyPairResourceParserTest-ECDSA-KeyPair |   12 +
 ...HKeyPairResourceParserTest-ECDSA-KeyPair.pub |    1 +
 ...SSHKeyPairResourceParserTest-ED25519-KeyPair |    7 +
 ...eyPairResourceParserTest-ED25519-KeyPair.pub |    1 +
 ...OpenSSHKeyPairResourceParserTest-RSA-KeyPair |   49 +
 ...SSHKeyPairResourceParserTest-RSA-KeyPair.pub |    1 +
 .../EDDSAProviderTest-EDDSA-OpenSSH-KeyPair     |    7 +
 .../EDDSAProviderTest-EDDSA-OpenSSH-KeyPair.pub |    1 +
 .../util/security/SecurityUtilsTest-DSA-KeyPair |   12 +
 .../security/SecurityUtilsTest-DSA-KeyPair.pub  |    1 +
 .../security/SecurityUtilsTest-EC-256-KeyPair   |    5 +
 .../SecurityUtilsTest-EC-256-KeyPair.pub        |    1 +
 .../security/SecurityUtilsTest-EC-384-KeyPair   |    6 +
 .../SecurityUtilsTest-EC-384-KeyPair.pub        |    1 +
 .../security/SecurityUtilsTest-EC-521-KeyPair   |    7 +
 .../SecurityUtilsTest-EC-521-KeyPair.pub        |    1 +
 .../util/security/SecurityUtilsTest-RSA-KeyPair |   27 +
 .../security/SecurityUtilsTest-RSA-KeyPair.pub  |    1 +
 .../super-secret-passphrase-RSA-AES-128-key     |   30 +
 .../super-secret-passphrase-RSA-AES-128-key.pub |    1 +
 .../super-secret-passphrase-RSA-AES-192-key     |   30 +
 .../super-secret-passphrase-RSA-AES-192-key.pub |    1 +
 .../super-secret-passphrase-RSA-AES-256-key     |   30 +
 .../super-secret-passphrase-RSA-AES-256-key.pub |    1 +
 .../super-secret-passphrase-RSA-DES-EDE3-key    |   30 +
 ...super-secret-passphrase-RSA-DES-EDE3-key.pub |    1 +
 sshd-contrib/pom.xml                            |    9 +-
 ...SimpleAccessControlScpEventListenerTest.java |    8 +-
 ...impleAccessControlSftpEventListenerTest.java |   12 +-
 sshd-core/pom.xml                               |   19 +-
 .../org/apache/sshd/sshd-version.properties     |   23 -
 .../java/org/apache/sshd/client/SshClient.java  |   60 +
 .../auth/AuthenticationIdentitiesProvider.java  |  125 --
 .../auth/hostbased/HostKeyIdentityProvider.java |   53 -
 .../keyboard/UserAuthKeyboardInteractive.java   |    3 +-
 .../auth/password/PasswordIdentityProvider.java |  183 ---
 .../client/auth/password/UserAuthPassword.java  |    2 +-
 .../client/auth/pubkey/PublicKeyIdentity.java   |   42 -
 .../auth/pubkey/UserAuthPublicKeyIterator.java  |    2 +-
 .../config/SshClientConfigFileReader.java       |    3 +-
 .../hosts/ConfigFileHostEntryResolver.java      |  107 --
 .../DefaultConfigFileHostEntryResolver.java     |   95 --
 .../client/config/hosts/HostConfigEntry.java    | 1169 ------------------
 .../config/hosts/HostConfigEntryResolver.java   |   60 -
 .../client/config/hosts/HostPatternValue.java   |   97 --
 .../client/config/hosts/HostPatternsHolder.java |  343 -----
 .../client/config/hosts/KnownHostDigest.java    |   66 -
 .../client/config/hosts/KnownHostEntry.java     |  276 -----
 .../client/config/hosts/KnownHostHashValue.java |  170 ---
 .../keys/BuiltinClientIdentitiesWatcher.java    |  106 --
 .../config/keys/ClientIdentitiesWatcher.java    |  139 ---
 .../sshd/client/config/keys/ClientIdentity.java |  325 -----
 .../config/keys/ClientIdentityFileWatcher.java  |  141 ---
 .../config/keys/ClientIdentityLoader.java       |   95 --
 .../config/keys/ClientIdentityProvider.java     |   42 -
 .../keys/DefaultClientIdentitiesWatcher.java    |   66 -
 .../KnownHostsServerKeyVerifier.java            |    6 +-
 .../sshd/client/session/ClientSession.java      |   54 +
 .../client/simple/SimpleClientConfigurator.java |    4 +-
 .../org/apache/sshd/common/AttributeStore.java  |  152 ---
 .../org/apache/sshd/common/BuiltinFactory.java  |   40 -
 .../java/org/apache/sshd/common/Closeable.java  |  126 --
 .../java/org/apache/sshd/common/Factory.java    |   41 -
 .../org/apache/sshd/common/FactoryManager.java  |   16 +
 .../org/apache/sshd/common/NamedFactory.java    |   66 -
 .../org/apache/sshd/common/NamedResource.java   |  104 --
 .../org/apache/sshd/common/OptionalFeature.java |   92 --
 .../apache/sshd/common/PropertyResolver.java    |  124 --
 .../sshd/common/PropertyResolverUtils.java      |  482 --------
 .../apache/sshd/common/RuntimeSshException.java |   48 -
 .../org/apache/sshd/common/SshConstants.java    |  245 ----
 .../org/apache/sshd/common/SshException.java    |   72 --
 .../apache/sshd/common/SyspropsMapWrapper.java  |  209 ----
 .../sshd/common/auth/MutableUserHolder.java     |   27 -
 .../apache/sshd/common/auth/UsernameHolder.java |   32 -
 .../sshd/common/channel/AbstractChannel.java    |    6 -
 .../org/apache/sshd/common/channel/Channel.java |   26 +
 .../apache/sshd/common/cipher/BaseCipher.java   |  113 --
 .../sshd/common/cipher/BaseRC4Cipher.java       |   54 -
 .../sshd/common/cipher/BuiltinCiphers.java      |  348 ------
 .../org/apache/sshd/common/cipher/Cipher.java   |   89 --
 .../sshd/common/cipher/CipherFactory.java       |   31 -
 .../sshd/common/cipher/CipherInformation.java   |   45 -
 .../apache/sshd/common/cipher/CipherNone.java   |   63 -
 .../org/apache/sshd/common/cipher/ECCurves.java |  580 ---------
 .../org/apache/sshd/common/cipher/package.html  |   26 -
 .../common/compression/BaseCompression.java     |   48 -
 .../common/compression/BuiltinCompressions.java |  239 ----
 .../sshd/common/compression/Compression.java    |   71 --
 .../compression/CompressionDelayedZlib.java     |   40 -
 .../common/compression/CompressionFactory.java  |   31 -
 .../compression/CompressionInformation.java     |   42 -
 .../common/compression/CompressionNone.java     |   76 --
 .../common/compression/CompressionZlib.java     |   85 --
 .../apache/sshd/common/compression/package.html |   25 -
 .../common/config/CompressionConfigValue.java   |   94 --
 .../common/config/FactoriesListParseResult.java |   51 -
 .../sshd/common/config/ListParseResult.java     |   66 -
 .../sshd/common/config/LogLevelValue.java       |   56 -
 .../config/NamedFactoriesListParseResult.java   |   47 -
 .../config/NamedResourceListParseResult.java    |   57 -
 .../sshd/common/config/SshConfigFileReader.java |  268 +---
 .../sshd/common/config/SyslogFacilityValue.java |   51 -
 .../sshd/common/config/TimeValueConfig.java     |  180 ---
 .../sshd/common/config/VersionProperties.java   |   98 --
 .../common/config/keys/AuthorizedKeyEntry.java  |  492 --------
 .../common/config/keys/BuiltinIdentities.java   |  212 ----
 .../config/keys/FilePasswordProvider.java       |   45 -
 .../sshd/common/config/keys/Identity.java       |   42 -
 .../config/keys/IdentityResourceLoader.java     |   49 -
 .../sshd/common/config/keys/IdentityUtils.java  |  159 ---
 .../common/config/keys/KeyEntryResolver.java    |  190 ---
 .../sshd/common/config/keys/KeyRandomArt.java   |  310 -----
 .../sshd/common/config/keys/KeyUtils.java       |  937 --------------
 .../config/keys/PrivateKeyEntryDecoder.java     |  142 ---
 .../config/keys/PrivateKeyEntryResolver.java    |   70 --
 .../sshd/common/config/keys/PublicKeyEntry.java |  286 -----
 .../config/keys/PublicKeyEntryDecoder.java      |  114 --
 .../config/keys/PublicKeyEntryResolver.java     |   70 --
 .../impl/AbstractIdentityResourceLoader.java    |   62 -
 .../keys/impl/AbstractKeyEntryResolver.java     |   59 -
 .../impl/AbstractPrivateKeyEntryDecoder.java    |   40 -
 .../impl/AbstractPublicKeyEntryDecoder.java     |   41 -
 .../keys/impl/DSSPublicKeyEntryDecoder.java     |  119 --
 .../keys/impl/ECDSAPublicKeyEntryDecoder.java   |  178 ---
 .../config/keys/impl/RSAPublicKeyDecoder.java   |  117 --
 .../keys/loader/AESPrivateKeyObfuscator.java    |  110 --
 .../loader/AbstractKeyPairResourceParser.java   |  181 ---
 .../loader/AbstractPrivateKeyObfuscator.java    |  191 ---
 .../keys/loader/DESPrivateKeyObfuscator.java    |   76 --
 .../keys/loader/KeyPairResourceLoader.java      |  129 --
 .../keys/loader/KeyPairResourceParser.java      |  192 ---
 .../loader/PrivateKeyEncryptionContext.java     |  270 ----
 .../keys/loader/PrivateKeyObfuscator.java       |   64 -
 .../OpenSSHDSSPrivateKeyEntryDecoder.java       |  139 ---
 .../OpenSSHECDSAPrivateKeyEntryDecoder.java     |  164 ---
 .../openssh/OpenSSHKeyPairResourceParser.java   |  358 ------
 .../loader/openssh/OpenSSHParserContext.java    |   83 --
 .../openssh/OpenSSHRSAPrivateKeyDecoder.java    |  135 --
 .../pem/AbstractPEMResourceKeyPairParser.java   |  167 ---
 .../loader/pem/DSSPEMResourceKeyPairParser.java |  126 --
 .../pem/ECDSAPEMResourceKeyPairParser.java      |  220 ----
 .../loader/pem/KeyPairPEMResourceParser.java    |   37 -
 .../keys/loader/pem/PEMResourceParserUtils.java |  109 --
 .../pem/PKCS8PEMResourceKeyPairParser.java      |  156 ---
 .../loader/pem/RSAPEMResourceKeyPairParser.java |  142 ---
 .../apache/sshd/common/digest/BaseDigest.java   |  156 ---
 .../sshd/common/digest/BuiltinDigests.java      |  166 ---
 .../org/apache/sshd/common/digest/Digest.java   |   36 -
 .../sshd/common/digest/DigestFactory.java       |   32 -
 .../sshd/common/digest/DigestInformation.java   |   36 -
 .../apache/sshd/common/digest/DigestUtils.java  |  228 ----
 .../org/apache/sshd/common/digest/package.html  |   25 -
 .../sshd/common/future/AbstractSshFuture.java   |  194 ---
 .../apache/sshd/common/future/CloseFuture.java  |   40 -
 .../sshd/common/future/DefaultCloseFuture.java  |   52 -
 .../sshd/common/future/DefaultSshFuture.java    |  236 ----
 .../future/DefaultVerifiableSshFuture.java      |   30 -
 .../apache/sshd/common/future/SshFuture.java    |   48 -
 .../sshd/common/future/SshFutureListener.java   |   42 -
 .../sshd/common/future/VerifiableFuture.java    |   68 -
 .../sshd/common/future/WaitableFuture.java      |  118 --
 .../common/helpers/AbstractFactoryManager.java  |    6 -
 .../keyprovider/AbstractKeyPairProvider.java    |   32 -
 .../AbstractResourceKeyPairProvider.java        |  234 ----
 .../ClassLoadableResourceKeyPairProvider.java   |  113 --
 .../common/keyprovider/FileKeyPairProvider.java |   92 --
 .../common/keyprovider/KeyIdentityProvider.java |  204 ---
 .../common/keyprovider/KeyPairProvider.java     |  181 ---
 .../keyprovider/KeyPairProviderHolder.java      |   35 -
 .../keyprovider/MappedKeyPairProvider.java      |   97 --
 .../org/apache/sshd/common/mac/BaseMac.java     |  111 --
 .../org/apache/sshd/common/mac/BuiltinMacs.java |  273 ----
 .../java/org/apache/sshd/common/mac/Mac.java    |   52 -
 .../org/apache/sshd/common/mac/MacFactory.java  |   31 -
 .../apache/sshd/common/mac/MacInformation.java  |   41 -
 .../org/apache/sshd/common/mac/package.html     |   25 -
 .../sshd/common/random/AbstractRandom.java      |   34 -
 .../common/random/AbstractRandomFactory.java    |   43 -
 .../apache/sshd/common/random/JceRandom.java    |   60 -
 .../sshd/common/random/JceRandomFactory.java    |   42 -
 .../org/apache/sshd/common/random/Random.java   |   56 -
 .../sshd/common/random/RandomFactory.java       |   31 -
 .../common/random/SingletonRandomFactory.java   |   70 --
 .../org/apache/sshd/common/random/package.html  |   25 -
 .../org/apache/sshd/common/session/Session.java |   27 +
 .../common/session/helpers/AbstractSession.java |    6 -
 .../common/signature/AbstractSignature.java     |  149 ---
 .../common/signature/BuiltinSignatures.java     |  305 -----
 .../apache/sshd/common/signature/Signature.java |   88 --
 .../sshd/common/signature/SignatureDSA.java     |  142 ---
 .../sshd/common/signature/SignatureECDSA.java   |  144 ---
 .../signature/SignatureFactoriesManager.java    |   94 --
 .../sshd/common/signature/SignatureFactory.java |   31 -
 .../sshd/common/signature/SignatureRSA.java     |   89 --
 .../apache/sshd/common/signature/package.html   |   25 -
 .../sshd/common/util/EventListenerUtils.java    |  212 ----
 .../apache/sshd/common/util/EventNotifier.java  |   35 -
 .../apache/sshd/common/util/GenericUtils.java   |  915 --------------
 .../sshd/common/util/IgnoringEmptyMap.java      |  128 --
 .../sshd/common/util/Int2IntFunction.java       |   66 -
 .../org/apache/sshd/common/util/Invoker.java    |  116 --
 .../apache/sshd/common/util/MapEntryUtils.java  |   51 -
 .../apache/sshd/common/util/NumberUtils.java    |  313 -----
 .../apache/sshd/common/util/ObjectBuilder.java  |   38 -
 .../org/apache/sshd/common/util/OsUtils.java    |  257 ----
 .../org/apache/sshd/common/util/Readable.java   |   53 -
 .../sshd/common/util/ReflectionUtils.java       |   53 -
 .../apache/sshd/common/util/SelectorUtils.java  |  805 ------------
 .../sshd/common/util/SshdEventListener.java     |   44 -
 .../apache/sshd/common/util/ValidateUtils.java  |  216 ----
 .../apache/sshd/common/util/VersionInfo.java    |  137 --
 .../apache/sshd/common/util/buffer/Buffer.java  |  798 ------------
 .../common/util/buffer/BufferException.java     |   30 -
 .../sshd/common/util/buffer/BufferUtils.java    |  604 ---------
 .../common/util/buffer/ByteArrayBuffer.java     |  253 ----
 .../keys/AbstractBufferPublicKeyParser.java     |   88 --
 .../util/buffer/keys/BufferPublicKeyParser.java |  111 --
 .../buffer/keys/DSSBufferPublicKeyParser.java   |   52 -
 .../buffer/keys/ECBufferPublicKeyParser.java    |   81 --
 .../keys/ED25519BufferPublicKeyParser.java      |   47 -
 .../buffer/keys/RSABufferPublicKeyParser.java   |   49 -
 .../util/closeable/AbstractCloseable.java       |  162 ---
 .../util/closeable/AbstractInnerCloseable.java  |   48 -
 .../sshd/common/util/closeable/Builder.java     |  115 --
 .../common/util/closeable/FuturesCloseable.java |   76 --
 .../common/util/closeable/IoBaseCloseable.java  |   35 -
 .../util/closeable/ParallelCloseable.java       |   73 --
 .../util/closeable/SequentialCloseable.java     |   71 --
 .../common/util/closeable/SimpleCloseable.java  |   71 --
 .../util/io/CloseableEmptyInputStream.java      |   96 --
 .../sshd/common/util/io/DirectoryScanner.java   |  380 ------
 .../sshd/common/util/io/EmptyInputStream.java   |   66 -
 .../sshd/common/util/io/FileInfoExtractor.java  |   53 -
 .../common/util/io/InputStreamWithChannel.java  |   32 -
 .../org/apache/sshd/common/util/io/IoUtils.java |  556 ---------
 .../sshd/common/util/io/LimitInputStream.java   |  113 --
 .../util/io/LoggingFilterOutputStream.java      |   67 -
 .../common/util/io/ModifiableFileWatcher.java   |  258 ----
 .../sshd/common/util/io/NoCloseInputStream.java |   47 -
 .../common/util/io/NoCloseOutputStream.java     |   47 -
 .../sshd/common/util/io/NoCloseReader.java      |   46 -
 .../sshd/common/util/io/NoCloseWriter.java      |   46 -
 .../sshd/common/util/io/NullInputStream.java    |   90 --
 .../sshd/common/util/io/NullOutputStream.java   |   72 --
 .../common/util/io/OutputStreamWithChannel.java |   32 -
 .../sshd/common/util/io/der/ASN1Class.java      |   93 --
 .../sshd/common/util/io/der/ASN1Object.java     |  338 -----
 .../sshd/common/util/io/der/ASN1Type.java       |  118 --
 .../sshd/common/util/io/der/DERParser.java      |  151 ---
 .../sshd/common/util/io/der/DERWriter.java      |  172 ---
 .../common/util/io/functors/IOFunction.java     |   86 --
 .../util/logging/AbstractLoggingBean.java       |   72 --
 .../sshd/common/util/logging/LoggingUtils.java  |  549 --------
 .../sshd/common/util/logging/SimplifiedLog.java |   55 -
 .../sshd/common/util/net/NetworkConnector.java  |   90 --
 .../sshd/common/util/net/SshdSocketAddress.java |  639 ----------
 .../AbstractSecurityProviderRegistrar.java      |  129 --
 .../util/security/SecurityEntityFactory.java    |  194 ---
 .../util/security/SecurityProviderChoice.java   |  130 --
 .../security/SecurityProviderRegistrar.java     |  337 -----
 .../common/util/security/SecurityUtils.java     |  759 ------------
 .../BouncyCastleGeneratorHostKeyProvider.java   |   48 -
 .../BouncyCastleKeyPairResourceParser.java      |  130 --
 .../bouncycastle/BouncyCastleRandom.java        |   88 --
 .../bouncycastle/BouncyCastleRandomFactory.java |   45 -
 .../BouncyCastleSecurityProviderRegistrar.java  |  128 --
 .../security/eddsa/Ed25519PublicKeyDecoder.java |   97 --
 .../eddsa/EdDSASecurityProviderRegistrar.java   |  104 --
 .../eddsa/EdDSASecurityProviderUtils.java       |  201 ---
 .../OpenSSHEd25519PrivateKeyEntryDecoder.java   |  172 ---
 .../util/security/eddsa/SignatureEd25519.java   |   49 -
 .../util/threads/CloseableExecutorService.java  |   28 -
 .../util/threads/ExecutorServiceCarrier.java    |   31 -
 .../common/util/threads/NoCloseExecutor.java    |  160 ---
 .../util/threads/SshThreadPoolExecutor.java     |  138 ---
 .../common/util/threads/SshdThreadFactory.java  |   78 --
 .../sshd/common/util/threads/ThreadUtils.java   |  185 ---
 .../auth/pubkey/PublickeyAuthenticator.java     |   17 +
 .../config/SshServerConfigFileReader.java       |   22 +-
 .../keys/AuthorizedKeysAuthenticator.java       |    4 +-
 .../AbstractGeneratorHostKeyProvider.java       |  293 -----
 .../SimpleGeneratorHostKeyProvider.java         |   67 -
 .../java/org/apache/sshd/KeepAliveTest.java     |    6 +-
 .../java/org/apache/sshd/agent/AgentTest.java   |    4 +-
 .../sshd/client/ClientSessionListenerTest.java  |    6 +-
 .../auth/PasswordIdentityProviderTest.java      |   73 --
 .../sshd/client/channel/ChannelExecTest.java    |    6 +-
 .../hosts/ConfigFileHostEntryResolverTest.java  |  139 ---
 .../hosts/HostConfigEntryResolverTest.java      |   11 +-
 .../config/hosts/HostConfigEntryTest.java       |  325 -----
 .../config/hosts/KnownHostHashValueTest.java    |   79 --
 .../BuiltinClientIdentitiesWatcherTest.java     |  161 ---
 .../keys/ClientIdentityFileWatcherTest.java     |  121 --
 .../client/config/keys/ClientIdentityTest.java  |  100 --
 .../org/apache/sshd/client/kex/KexTest.java     |    6 +-
 .../KnownHostsServerKeyVerifierTest.java        |    6 +-
 .../sshd/client/session/ClientSessionTest.java  |    6 +-
 .../client/simple/SimpleSessionClientTest.java  |    6 +-
 .../apache/sshd/common/AttributeStoreTest.java  |   24 +-
 .../sshd/common/PropertyResolverUtilsTest.java  |   24 +-
 .../apache/sshd/common/SshConstantsTest.java    |   75 --
 .../sshd/common/VersionPropertiesTest.java      |   45 -
 .../sshd/common/auth/AuthenticationTest.java    |   18 +-
 .../sshd/common/cipher/AES192CTRTest.java       |   41 -
 .../sshd/common/cipher/AES256CBCTest.java       |   41 -
 .../sshd/common/cipher/ARCFOUR128Test.java      |   39 -
 .../sshd/common/cipher/ARCFOUR256Test.java      |   41 -
 .../sshd/common/cipher/BaseCipherTest.java      |   95 --
 .../apache/sshd/common/cipher/CipherTest.java   |   25 +-
 .../apache/sshd/common/cipher/ECCurvesTest.java |  107 --
 .../compression/BuiltinCompressionsTest.java    |  171 ---
 .../common/compression/CompressionTest.java     |    7 +-
 .../common/config/SshConfigFileReaderTest.java  |   39 +-
 .../sshd/common/config/TimeValueConfigTest.java |   57 -
 ...AuthorizedKeyEntryLoginOptionsParseTest.java |  124 --
 .../config/keys/AuthorizedKeyEntryTest.java     |    6 +-
 .../config/keys/BuiltinIdentitiesTest.java      |  116 --
 .../common/config/keys/KeyRandomArtTest.java    |  117 --
 .../common/config/keys/KeyUtilsCloneTest.java   |  120 --
 .../KeyUtilsFingerprintCaseSensitivityTest.java |   96 --
 .../keys/KeyUtilsFingerprintGenerationTest.java |  159 ---
 .../sshd/common/config/keys/KeyUtilsTest.java   |  157 ---
 .../common/config/keys/PublicKeyEntryTest.java  |   62 -
 .../loader/AESPrivateKeyObfuscatorTest.java     |   79 --
 .../OpenSSHKeyPairResourceParserTest.java       |  108 --
 .../pem/PKCS8PEMResourceKeyPairParserTest.java  |  107 --
 .../sshd/common/digest/BuiltinDigestsTest.java  |   65 -
 .../file/root/RootedFileSystemProviderTest.java |    4 +-
 .../common/forward/PortForwardingLoadTest.java  |    6 +-
 .../sshd/common/forward/PortForwardingTest.java |   14 +-
 .../common/future/DefaultSshFutureTest.java     |  137 --
 .../common/keyprovider/KeyPairProviderTest.java |   79 --
 .../apache/sshd/common/mac/BuiltinMacsTest.java |  166 ---
 .../org/apache/sshd/common/mac/MacTest.java     |    7 +-
 .../apache/sshd/common/mac/MacVectorsTest.java  |  311 -----
 .../sshd/common/random/RandomFactoryTest.java   |   83 --
 .../session/helpers/AbstractSessionTest.java    |    2 +-
 .../common/signature/BuiltinSignaturesTest.java |  149 ---
 .../sshd/common/signature/SignatureDSATest.java |  109 --
 .../signature/SignatureFactoriesTest.java       |    6 +-
 .../sshd/common/signature/SignatureRSATest.java |  119 --
 .../common/signature/SignaturesDevelopment.java |   79 --
 .../common/util/EventListenerUtilsTest.java     |  149 ---
 .../sshd/common/util/GenericUtilsTest.java      |  173 ---
 .../sshd/common/util/Int2IntFunctionTest.java   |  154 ---
 .../sshd/common/util/NumberUtilsTest.java       |   77 --
 .../apache/sshd/common/util/OsUtilsTest.java    |  135 --
 .../sshd/common/util/SecurityUtilsTest.java     |  232 ----
 .../sshd/common/util/SelectorUtilsTest.java     |  147 ---
 .../sshd/common/util/ThreadUtilsTest.java       |   72 --
 .../sshd/common/util/ValidateUtilsTest.java     |   44 -
 .../sshd/common/util/VersionInfoTest.java       |   52 -
 .../sshd/common/util/buffer/BufferTest.java     |  103 --
 .../common/util/buffer/BufferUtilsTest.java     |   73 --
 .../util/closeable/CloseableUtilsTest.java      |  163 ---
 .../common/util/io/EmptyInputStreamTest.java    |  120 --
 .../apache/sshd/common/util/io/IoUtilsTest.java |   61 -
 .../common/util/io/LimitInputStreamTest.java    |  118 --
 .../util/io/ModifiableFileWatcherTest.java      |   82 --
 .../common/util/io/NoCloseInputStreamTest.java  |   89 --
 .../common/util/io/NoCloseOutputStreamTest.java |   69 --
 .../sshd/common/util/io/NoCloseReaderTest.java  |   95 --
 .../sshd/common/util/io/NoCloseWriterTest.java  |   73 --
 .../common/util/io/NullInputStreamTest.java     |  118 --
 .../common/util/io/NullOutputStreamTest.java    |   80 --
 .../sshd/common/util/io/der/ASN1ClassTest.java  |   67 -
 .../sshd/common/util/io/der/ASN1TypeTest.java   |   67 -
 .../sshd/common/util/io/der/DERParserTest.java  |   61 -
 .../sshd/common/util/io/der/DERWriterTest.java  |   64 -
 .../util/net/SshdSocketIpv6AddressTest.java     |   88 --
 ...SecurityProviderRegistrarCipherNameTest.java |   75 --
 .../SecurityProviderRegistrarTestSupport.java   |   62 -
 .../util/security/eddsa/EDDSAProviderTest.java  |  126 --
 .../util/security/eddsa/Ed25519VectorsTest.java |  238 ----
 .../EdDSASecurityProviderRegistrarTest.java     |   85 --
 .../sshd/server/ServerSessionListenerTest.java  |    6 +-
 .../server/auth/WelcomeBannerPhaseTest.java     |    6 +-
 .../sshd/server/auth/WelcomeBannerTest.java     |    6 +-
 .../AbstractGeneratorHostKeyProviderTest.java   |   83 --
 .../PEMGeneratorHostKeyProviderTest.java        |  141 ---
 .../SimpleGeneratorHostKeyProviderTest.java     |  133 --
 .../apache/sshd/util/test/BaseTestSupport.java  |  543 +-------
 .../sshd/util/test/CoreTestSupportUtils.java    |   63 +
 .../test/JUnit4ClassRunnerWithParameters.java   |   48 -
 .../JUnit4ClassRunnerWithParametersFactory.java |   58 -
 .../test/JUnit4SingleInstanceClassRunner.java   |   54 -
 .../org/apache/sshd/util/test/NoIoTestCase.java |   30 -
 .../test/OutputCountTrackingOutputStream.java   |   56 -
 .../apache/sshd/util/test/TeeOutputStream.java  |   64 -
 .../java/org/apache/sshd/util/test/Utils.java   |  652 ----------
 .../testReadGlobalHostsConfigEntries.config.txt |   22 -
 .../testReadMultipleHostPatterns.config.txt     |    5 -
 .../testReadSimpleHostsConfigEntries.config.txt |    8 -
 .../org/apache/sshd/client/config/keys/id_dsa   |   12 -
 .../org/apache/sshd/client/config/keys/id_ecdsa |    5 -
 .../apache/sshd/client/config/keys/id_rsa_key   |   27 -
 ...OpenSSHKeyPairResourceParserTest-DSA-KeyPair |   21 -
 ...SSHKeyPairResourceParserTest-DSA-KeyPair.pub |    1 -
 ...enSSHKeyPairResourceParserTest-ECDSA-KeyPair |   12 -
 ...HKeyPairResourceParserTest-ECDSA-KeyPair.pub |    1 -
 ...SSHKeyPairResourceParserTest-ED25519-KeyPair |    7 -
 ...eyPairResourceParserTest-ED25519-KeyPair.pub |    1 -
 ...OpenSSHKeyPairResourceParserTest-RSA-KeyPair |   49 -
 ...SSHKeyPairResourceParserTest-RSA-KeyPair.pub |    1 -
 .../EDDSAProviderTest-EDDSA-OpenSSH-KeyPair     |    7 -
 .../EDDSAProviderTest-EDDSA-OpenSSH-KeyPair.pub |    1 -
 .../common/util/SecurityUtilsTest-DSA-KeyPair   |   12 -
 .../util/SecurityUtilsTest-DSA-KeyPair.pub      |    1 -
 .../util/SecurityUtilsTest-EC-256-KeyPair       |    5 -
 .../util/SecurityUtilsTest-EC-256-KeyPair.pub   |    1 -
 .../util/SecurityUtilsTest-EC-384-KeyPair       |    6 -
 .../util/SecurityUtilsTest-EC-384-KeyPair.pub   |    1 -
 .../util/SecurityUtilsTest-EC-521-KeyPair       |    7 -
 .../util/SecurityUtilsTest-EC-521-KeyPair.pub   |    1 -
 .../common/util/SecurityUtilsTest-RSA-KeyPair   |   27 -
 .../util/SecurityUtilsTest-RSA-KeyPair.pub      |    1 -
 .../super-secret-passphrase-RSA-AES-128-key     |   30 -
 .../super-secret-passphrase-RSA-AES-128-key.pub |    1 -
 .../super-secret-passphrase-RSA-AES-192-key     |   30 -
 .../super-secret-passphrase-RSA-AES-192-key.pub |    1 -
 .../super-secret-passphrase-RSA-AES-256-key     |   30 -
 .../super-secret-passphrase-RSA-AES-256-key.pub |    1 -
 .../super-secret-passphrase-RSA-DES-EDE3-key    |   30 -
 ...super-secret-passphrase-RSA-DES-EDE3-key.pub |    1 -
 sshd-git/pom.xml                                |    7 +
 .../sshd/git/pack/GitPackCommandTest.java       |    6 +-
 .../apache/sshd/git/pgm/GitPgmCommandTest.java  |    4 +-
 sshd-ldap/pom.xml                               |    7 +
 .../sshd/server/auth/BaseAuthenticatorTest.java |   17 +-
 sshd-mina/pom.xml                               |    7 +
 sshd-netty/pom.xml                              |    7 +
 sshd-scp/pom.xml                                |    9 +-
 .../org/apache/sshd/client/scp/ScpTest.java     |  154 +--
 .../sshd/client/scp/SimpleScpClientTest.java    |   20 +-
 sshd-sftp/pom.xml                               |    9 +-
 .../subsystem/sftp/SftpFileSystemProvider.java  |    8 +-
 .../sftp/AbstractSftpClientTestSupport.java     |    6 +-
 .../sftp/DefaultCloseableHandleTest.java        |    4 +-
 .../subsystem/sftp/SftpFileSystemTest.java      |   30 +-
 .../sshd/client/subsystem/sftp/SftpTest.java    |   78 +-
 .../subsystem/sftp/SftpVersionSelectorTest.java |    4 +-
 .../client/subsystem/sftp/SftpVersionsTest.java |   24 +-
 .../subsystem/sftp/SimpleSftpClientTest.java    |    8 +-
 .../helpers/AbstractCheckFileExtensionTest.java |    8 +-
 .../helpers/AbstractMD5HashExtensionTest.java   |    8 +-
 .../helpers/CopyDataExtensionImplTest.java      |    8 +-
 .../helpers/CopyFileExtensionImplTest.java      |   11 +-
 .../SpaceAvailableExtensionImplTest.java        |    6 +-
 .../openssh/helpers/OpenSSHExtensionsTest.java  |   10 +-
 .../subsystem/sftp/SftpConstantsTest.java       |    4 +-
 .../sftp/SftpUniversalOwnerAndGroupTest.java    |    4 +-
 .../sftp/SftpSubsystemFactoryTest.java          |    4 +-
 sshd-spring-sftp/pom.xml                        |    7 +
 .../sftp/ApacheSshdSftpSessionFactory.java      |    4 +-
 .../sftp/ApacheSshdSftpSessionFactoryTest.java  |   21 +-
 793 files changed, 44627 insertions(+), 44210 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/assembly/pom.xml
----------------------------------------------------------------------
diff --git a/assembly/pom.xml b/assembly/pom.xml
index 6efb67a..93011a8 100644
--- a/assembly/pom.xml
+++ b/assembly/pom.xml
@@ -38,6 +38,11 @@
     <dependencies>
         <dependency>
             <groupId>org.apache.sshd</groupId>
+            <artifactId>sshd-common</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sshd</groupId>
             <artifactId>sshd-core</artifactId>
             <version>${project.version}</version>
         </dependency>

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 34d245f..a684d78 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1135,6 +1135,7 @@
     </reporting>
 
     <modules>
+        <module>sshd-common</module>
         <module>sshd-core</module>
         <module>sshd-mina</module>
         <module>sshd-netty</module>

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-cli/pom.xml
----------------------------------------------------------------------
diff --git a/sshd-cli/pom.xml b/sshd-cli/pom.xml
index 3fdfb7a..3babf6b 100644
--- a/sshd-cli/pom.xml
+++ b/sshd-cli/pom.xml
@@ -61,6 +61,13 @@
             <type>test-jar</type>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.apache.sshd</groupId>
+            <artifactId>sshd-common</artifactId>
+            <version>${project.version}</version>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
             <!-- For the I/O factories -->
         <dependency>
             <groupId>org.apache.sshd</groupId>

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-cli/src/main/java/org/apache/sshd/cli/CliSupport.java
----------------------------------------------------------------------
diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/CliSupport.java b/sshd-cli/src/main/java/org/apache/sshd/cli/CliSupport.java
index 27c6d96..025c209 100644
--- a/sshd-cli/src/main/java/org/apache/sshd/cli/CliSupport.java
+++ b/sshd-cli/src/main/java/org/apache/sshd/cli/CliSupport.java
@@ -24,8 +24,8 @@ import java.net.SocketAddress;
 import java.util.Map;
 import java.util.Objects;
 
+import org.apache.sshd.common.config.ConfigFileReaderSupport;
 import org.apache.sshd.common.config.LogLevelValue;
-import org.apache.sshd.common.config.SshConfigFileReader;
 import org.apache.sshd.common.helpers.AbstractFactoryManager;
 import org.apache.sshd.common.io.BuiltinIoServiceFactoryFactories;
 import org.apache.sshd.common.io.IoAcceptor;
@@ -105,14 +105,14 @@ public abstract class CliSupport {
 
         manager.setIoServiceFactoryFactory(factory.create());
 
-        String levelValue = (options == null) ? null : Objects.toString(options.get(SshConfigFileReader.LOG_LEVEL_CONFIG_PROP), null);
+        String levelValue = (options == null) ? null : Objects.toString(options.get(ConfigFileReaderSupport.LOG_LEVEL_CONFIG_PROP), null);
         if (GenericUtils.isEmpty(levelValue)) {
             return manager;
         }
 
         LogLevelValue level = LogLevelValue.fromName(levelValue);
         if (level == null) {
-            throw new IllegalArgumentException("Unknown " + SshConfigFileReader.LOG_LEVEL_CONFIG_PROP + " option value: " + levelValue);
+            throw new IllegalArgumentException("Unknown " + ConfigFileReaderSupport.LOG_LEVEL_CONFIG_PROP + " option value: " + levelValue);
         }
 
         if ((level != LogLevelValue.FATAL) && (level != LogLevelValue.ERROR) && (level != LogLevelValue.INFO)) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientCliSupport.java
----------------------------------------------------------------------
diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientCliSupport.java b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientCliSupport.java
index 0d7d2f5..2d129f6 100644
--- a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientCliSupport.java
+++ b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientCliSupport.java
@@ -65,7 +65,7 @@ import org.apache.sshd.common.cipher.Cipher;
 import org.apache.sshd.common.compression.BuiltinCompressions;
 import org.apache.sshd.common.compression.Compression;
 import org.apache.sshd.common.config.CompressionConfigValue;
-import org.apache.sshd.common.config.SshConfigFileReader;
+import org.apache.sshd.common.config.ConfigFileReaderSupport;
 import org.apache.sshd.common.config.keys.BuiltinIdentities;
 import org.apache.sshd.common.config.keys.KeyUtils;
 import org.apache.sshd.common.config.keys.PublicKeyEntry;
@@ -236,7 +236,7 @@ public abstract class SshClientCliSupport extends CliSupport {
             }
 
             if (port <= 0) {
-                port = SshConfigFileReader.DEFAULT_PORT;
+                port = ConfigFileReaderSupport.DEFAULT_PORT;
             }
 
             // TODO use a configurable wait time
@@ -424,7 +424,7 @@ public abstract class SshClientCliSupport extends CliSupport {
         }
 
         String strictValue = Objects.toString(options.remove(KnownHostsServerKeyVerifier.STRICT_CHECKING_OPTION), "true");
-        if (!SshConfigFileReader.parseBooleanValue(strictValue)) {
+        if (!ConfigFileReaderSupport.parseBooleanValue(strictValue)) {
             return current;
         }
 
@@ -506,7 +506,7 @@ public abstract class SshClientCliSupport extends CliSupport {
     }
 
     public static List<NamedFactory<Compression>> setupCompressions(PropertyResolver options, PrintStream stderr) {
-        String argVal = PropertyResolverUtils.getString(options, SshConfigFileReader.COMPRESSION_PROP);
+        String argVal = PropertyResolverUtils.getString(options, ConfigFileReaderSupport.COMPRESSION_PROP);
         if (GenericUtils.isEmpty(argVal)) {
             return Collections.emptyList();
         }
@@ -543,10 +543,10 @@ public abstract class SshClientCliSupport extends CliSupport {
     }
 
     public static List<NamedFactory<Mac>> setupMacs(PropertyResolver options, PrintStream stderr) {
-        String argVal = PropertyResolverUtils.getString(options, SshConfigFileReader.MACS_CONFIG_PROP);
+        String argVal = PropertyResolverUtils.getString(options, ConfigFileReaderSupport.MACS_CONFIG_PROP);
         return GenericUtils.isEmpty(argVal)
              ? Collections.emptyList()
-             : setupMacs(SshConfigFileReader.MACS_CONFIG_PROP, argVal, null, stderr);
+             : setupMacs(ConfigFileReaderSupport.MACS_CONFIG_PROP, argVal, null, stderr);
     }
 
     public static List<NamedFactory<Mac>> setupMacs(String argName, String argVal, List<NamedFactory<Mac>> current, PrintStream stderr) {
@@ -571,10 +571,10 @@ public abstract class SshClientCliSupport extends CliSupport {
     }
 
     public static List<NamedFactory<Cipher>> setupCiphers(PropertyResolver options, PrintStream stderr) {
-        String argVal = PropertyResolverUtils.getString(options, SshConfigFileReader.CIPHERS_CONFIG_PROP);
+        String argVal = PropertyResolverUtils.getString(options, ConfigFileReaderSupport.CIPHERS_CONFIG_PROP);
         return GenericUtils.isEmpty(argVal)
              ? Collections.emptyList()
-             : setupCiphers(SshConfigFileReader.CIPHERS_CONFIG_PROP, argVal, null, stderr);
+             : setupCiphers(ConfigFileReaderSupport.CIPHERS_CONFIG_PROP, argVal, null, stderr);
     }
 
     // returns null - e.g., re-specified or no supported cipher found

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshKeyScanMain.java
----------------------------------------------------------------------
diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshKeyScanMain.java b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshKeyScanMain.java
index b5350f0..adc120f 100644
--- a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshKeyScanMain.java
+++ b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshKeyScanMain.java
@@ -63,7 +63,7 @@ import org.apache.sshd.client.keyverifier.ServerKeyVerifier;
 import org.apache.sshd.client.session.ClientSession;
 import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.cipher.ECCurves;
-import org.apache.sshd.common.config.SshConfigFileReader;
+import org.apache.sshd.common.config.ConfigFileReaderSupport;
 import org.apache.sshd.common.config.keys.BuiltinIdentities;
 import org.apache.sshd.common.config.keys.KeyUtils;
 import org.apache.sshd.common.config.keys.PublicKeyEntry;
@@ -711,7 +711,7 @@ public class SshKeyScanMain implements Channel, Callable<Void>, ServerKeyVerifie
     public static <S extends SshKeyScanMain> S initializeScanner(S scanner, Collection<String> hosts) throws IOException {
         setInputStream(scanner, hosts);
         if (scanner.getPort() <= 0) {
-            scanner.setPort(SshConfigFileReader.DEFAULT_PORT);
+            scanner.setPort(ConfigFileReaderSupport.DEFAULT_PORT);
         }
 
         if (scanner.getTimeout() <= 0L) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerCliSupport.java
----------------------------------------------------------------------
diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerCliSupport.java b/sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerCliSupport.java
index 8b392bc..6cdffc8 100644
--- a/sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerCliSupport.java
+++ b/sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerCliSupport.java
@@ -40,7 +40,7 @@ import org.apache.sshd.cli.CliSupport;
 import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.PropertyResolver;
 import org.apache.sshd.common.PropertyResolverUtils;
-import org.apache.sshd.common.config.SshConfigFileReader;
+import org.apache.sshd.common.config.ConfigFileReaderSupport;
 import org.apache.sshd.common.config.keys.BuiltinIdentities;
 import org.apache.sshd.common.config.keys.KeyUtils;
 import org.apache.sshd.common.keyprovider.KeyPairProvider;
@@ -166,7 +166,7 @@ public abstract class SshServerCliSupport extends CliSupport {
             return subsystems;
         }
 
-        String nameList = (options == null) ? null : options.getString(SshConfigFileReader.SUBSYSTEM_CONFIG_PROP);
+        String nameList = (options == null) ? null : options.getString(ConfigFileReaderSupport.SUBSYSTEM_CONFIG_PROP);
         if ("none".equalsIgnoreCase(nameList)) {
             return Collections.emptyList();
         }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerMain.java
----------------------------------------------------------------------
diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerMain.java b/sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerMain.java
index 0740d65..92bd182 100644
--- a/sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerMain.java
+++ b/sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerMain.java
@@ -30,6 +30,7 @@ import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.PropertyResolver;
 import org.apache.sshd.common.PropertyResolverUtils;
+import org.apache.sshd.common.config.ConfigFileReaderSupport;
 import org.apache.sshd.common.config.SshConfigFileReader;
 import org.apache.sshd.common.keyprovider.KeyPairProvider;
 import org.apache.sshd.common.util.GenericUtils;
@@ -138,7 +139,7 @@ public class SshServerMain extends SshServerCliSupport {
                         keyFiles = new LinkedList<>();
                     }
                     keyFiles.add(optValue);
-                } else if (SshConfigFileReader.PORT_CONFIG_PROP.equals(optName)) {
+                } else if (ConfigFileReaderSupport.PORT_CONFIG_PROP.equals(optName)) {
                     port = Integer.parseInt(optValue);
                 } else {
                     options.put(optName, optValue);
@@ -166,7 +167,7 @@ public class SshServerMain extends SshServerCliSupport {
         setupServerBanner(sshd, resolver);
         sshd.setPort(port);
 
-        String macsOverride = resolver.getString(SshConfigFileReader.MACS_CONFIG_PROP);
+        String macsOverride = resolver.getString(ConfigFileReaderSupport.MACS_CONFIG_PROP);
         if (GenericUtils.isNotEmpty(macsOverride)) {
             SshConfigFileReader.configureMacs(sshd, macsOverride, true, true);
         }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-cli/src/test/java/org/apache/sshd/cli/server/SshFsMounter.java
----------------------------------------------------------------------
diff --git a/sshd-cli/src/test/java/org/apache/sshd/cli/server/SshFsMounter.java b/sshd-cli/src/test/java/org/apache/sshd/cli/server/SshFsMounter.java
index 51596b1..37cf8bc 100644
--- a/sshd-cli/src/test/java/org/apache/sshd/cli/server/SshFsMounter.java
+++ b/sshd-cli/src/test/java/org/apache/sshd/cli/server/SshFsMounter.java
@@ -35,7 +35,7 @@ import java.util.concurrent.Future;
 
 import org.apache.sshd.common.PropertyResolver;
 import org.apache.sshd.common.PropertyResolverUtils;
-import org.apache.sshd.common.config.SshConfigFileReader;
+import org.apache.sshd.common.config.ConfigFileReaderSupport;
 import org.apache.sshd.common.io.BuiltinIoServiceFactoryFactories;
 import org.apache.sshd.common.io.IoServiceFactory;
 import org.apache.sshd.common.util.GenericUtils;
@@ -56,7 +56,8 @@ import org.apache.sshd.server.scp.ScpCommandFactory;
 import org.apache.sshd.server.session.ServerSession;
 import org.apache.sshd.server.shell.ShellFactory;
 import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory;
-import org.apache.sshd.util.test.Utils;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
+import org.apache.sshd.util.test.CoreTestSupportUtils;
 
 /**
  * A basic implementation to allow remote mounting of the local file system via SFTP
@@ -249,7 +250,7 @@ public final class SshFsMounter extends SshServerCliSupport {
     //////////////////////////////////////////////////////////////////////////
 
     public static void main(String[] args) throws Exception {
-        int port = SshConfigFileReader.DEFAULT_PORT;
+        int port = ConfigFileReaderSupport.DEFAULT_PORT;
         boolean error = false;
         Map<String, Object> options = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
         int numArgs = GenericUtils.length(args);
@@ -296,7 +297,7 @@ public final class SshFsMounter extends SshServerCliSupport {
         }
 
         SshServer sshd = error ? null : setupIoServiceFactory(
-            Utils.setupTestServer(SshFsMounter.class), options, System.out, System.err, args);
+            CoreTestSupportUtils.setupTestServer(SshFsMounter.class), options, System.out, System.err, args);
         if (sshd == null) {
             error = true;
         }
@@ -309,7 +310,7 @@ public final class SshFsMounter extends SshServerCliSupport {
         Map<String, Object> props = sshd.getProperties();
         props.putAll(options);
         PropertyResolver resolver = PropertyResolverUtils.toPropertyResolver(options);
-        File targetFolder = Objects.requireNonNull(Utils.detectTargetFolder(MounterCommandFactory.class), "Failed to detect target folder");
+        File targetFolder = Objects.requireNonNull(CommonTestSupportUtils.detectTargetFolder(MounterCommandFactory.class), "Failed to detect target folder");
         if (SecurityUtils.isBouncyCastleRegistered()) {
             sshd.setKeyPairProvider(SecurityUtils.createGeneratorHostKeyProvider(new File(targetFolder, "key.pem").toPath()));
         } else {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/pom.xml
----------------------------------------------------------------------
diff --git a/sshd-common/pom.xml b/sshd-common/pom.xml
new file mode 100644
index 0000000..f893b19
--- /dev/null
+++ b/sshd-common/pom.xml
@@ -0,0 +1,128 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+
+    <!--
+
+        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.
+    -->
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.sshd</groupId>
+        <artifactId>sshd</artifactId>
+        <version>2.0.1-SNAPSHOT</version>
+        <relativePath>..</relativePath>
+    </parent>
+
+    <artifactId>sshd-common</artifactId>
+    <name>Apache Mina SSHD :: Common support utilities</name>
+    <packaging>jar</packaging>
+    <inceptionYear>2018</inceptionYear>
+
+    <properties>
+        <projectRoot>${project.basedir}/..</projectRoot>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.bouncycastle</groupId>
+            <artifactId>bcpg-jdk15on</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.bouncycastle</groupId>
+            <artifactId>bcpkix-jdk15on</artifactId>
+            <optional>true</optional>
+        </dependency>
+
+            <!-- For ed25519 support -->
+        <dependency>
+            <groupId>net.i2p.crypto</groupId>
+            <artifactId>eddsa</artifactId>
+            <optional>true</optional>
+        </dependency>
+
+            <!-- test dependencies -->
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>jcl-over-slf4j</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-log4j12</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.servicemix.bundles</groupId>
+            <artifactId>org.apache.servicemix.bundles.not-yet-commons-ssl</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <resources>
+            <resource>
+                <directory>src/main/filtered-resources</directory>
+                <filtering>true</filtering>
+            </resource>
+        </resources>
+        <plugins>
+            <!-- publish the test-jar since it contains some classes used by other
+                artifacts tests -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>test-jar</goal>
+                        </goals>
+                        <configuration>
+                            <includes>
+                                <include>org/apache/sshd/util/test/**/*</include>
+                            </includes>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <redirectTestOutputToFile>true</redirectTestOutputToFile>
+                    <reportsDirectory>${project.build.directory}/surefire-reports-common</reportsDirectory>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/filtered-resources/org/apache/sshd/sshd-version.properties
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/filtered-resources/org/apache/sshd/sshd-version.properties b/sshd-common/src/main/filtered-resources/org/apache/sshd/sshd-version.properties
new file mode 100644
index 0000000..2c32c28
--- /dev/null
+++ b/sshd-common/src/main/filtered-resources/org/apache/sshd/sshd-version.properties
@@ -0,0 +1,23 @@
+##
+## 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.
+##
+
+groupId=${pom.groupId}
+artifactId=${pom.artifactId}
+version=${pom.version}
+sshd-version=${pom.artifactId}-${pom.version}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/client/auth/AuthenticationIdentitiesProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/auth/AuthenticationIdentitiesProvider.java b/sshd-common/src/main/java/org/apache/sshd/client/auth/AuthenticationIdentitiesProvider.java
new file mode 100644
index 0000000..cfd1f85
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/client/auth/AuthenticationIdentitiesProvider.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.client.auth;
+
+import java.security.KeyPair;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.sshd.client.auth.password.PasswordIdentityProvider;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.keyprovider.KeyIdentityProvider;
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface AuthenticationIdentitiesProvider extends KeyIdentityProvider, PasswordIdentityProvider {
+
+    /**
+     * Compares 2 password identities - returns zero ONLY if <U>both</U> compared
+     * objects are {@link String}s and equal to each other
+     */
+    Comparator<Object> PASSWORD_IDENTITY_COMPARATOR = (o1, o2) -> {
+        if (!(o1 instanceof String) || !(o2 instanceof String)) {
+            return -1;
+        } else {
+            return ((String) o1).compareTo((String) o2);
+        }
+    };
+
+    /**
+     * Compares 2 {@link KeyPair} identities - returns zero ONLY if <U>both</U> compared
+     * objects are {@link KeyPair}s and equal to each other
+     */
+    Comparator<Object> KEYPAIR_IDENTITY_COMPARATOR = (o1, o2) -> {
+        if ((!(o1 instanceof KeyPair)) || (!(o2 instanceof KeyPair))) {
+            return -1;
+        } else if (KeyUtils.compareKeyPairs((KeyPair) o1, (KeyPair) o2)) {
+            return 0;
+        } else {
+            return 1;
+        }
+    };
+
+    /**
+     * @return All the currently available identities - passwords, keys, etc...
+     */
+    Iterable<?> loadIdentities();
+
+    static int findIdentityIndex(List<?> identities, Comparator<? super Object> comp, Object target) {
+        for (int index = 0; index < identities.size(); index++) {
+            Object value = identities.get(index);
+            if (comp.compare(value, target) == 0) {
+                return index;
+            }
+        }
+
+        return -1;
+    }
+
+    /**
+     * @param identities The {@link Iterable} identities - OK if {@code null}/empty
+     * @return An {@link AuthenticationIdentitiesProvider} wrapping the identities
+     */
+    static AuthenticationIdentitiesProvider wrapIdentities(Iterable<?> identities) {
+        return new AuthenticationIdentitiesProvider() {
+            @Override
+            public Iterable<KeyPair> loadKeys() {
+                return selectIdentities(KeyPair.class);
+            }
+
+            @Override
+            public Iterable<String> loadPasswords() {
+                return selectIdentities(String.class);
+            }
+
+            @Override
+            public Iterable<?> loadIdentities() {
+                return selectIdentities(Object.class);
+            }
+
+            // NOTE: returns a NEW Collection on every call so that the original
+            //      identities remain unchanged
+            private <T> Collection<T> selectIdentities(Class<T> type) {
+                Collection<T> matches = null;
+                for (Iterator<?> iter = GenericUtils.iteratorOf(identities); iter.hasNext();) {
+                    Object o = iter.next();
+                    Class<?> t = o.getClass();
+                    if (!type.isAssignableFrom(t)) {
+                        continue;
+                    }
+
+                    if (matches == null) {
+                        matches = new LinkedList<>();
+                    }
+
+                    matches.add(type.cast(o));
+                }
+
+                return (matches == null) ? Collections.<T>emptyList() : matches;
+            }
+        };
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/client/auth/hostbased/HostKeyIdentityProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/auth/hostbased/HostKeyIdentityProvider.java b/sshd-common/src/main/java/org/apache/sshd/client/auth/hostbased/HostKeyIdentityProvider.java
new file mode 100644
index 0000000..81c26ca
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/client/auth/hostbased/HostKeyIdentityProvider.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.client.auth.hostbased;
+
+import java.security.KeyPair;
+import java.security.cert.X509Certificate;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FunctionalInterface
+public interface HostKeyIdentityProvider {
+    /**
+     * @return The host keys as a {@link java.util.Map.Entry} of key + certificates (which can be {@code null}/empty)
+     */
+    Iterable<? extends Map.Entry<KeyPair, List<X509Certificate>>> loadHostKeys();
+
+    static Iterator<? extends Map.Entry<KeyPair, List<X509Certificate>>> iteratorOf(HostKeyIdentityProvider provider) {
+        return GenericUtils.iteratorOf((provider == null) ? null : provider.loadHostKeys());
+    }
+
+    static HostKeyIdentityProvider wrap(KeyPair... pairs) {
+        return wrap(GenericUtils.asList(pairs));
+    }
+
+    static HostKeyIdentityProvider wrap(Iterable<? extends KeyPair> pairs) {
+        return () -> GenericUtils.wrapIterable(pairs, kp -> new SimpleImmutableEntry<>(kp, Collections.<X509Certificate>emptyList()));
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/client/auth/password/PasswordIdentityProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/auth/password/PasswordIdentityProvider.java b/sshd-common/src/main/java/org/apache/sshd/client/auth/password/PasswordIdentityProvider.java
new file mode 100644
index 0000000..f075bf8
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/client/auth/password/PasswordIdentityProvider.java
@@ -0,0 +1,169 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.client.auth.password;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FunctionalInterface
+public interface PasswordIdentityProvider {
+
+    /**
+     * An &quot;empty&quot implementation of {@link PasswordIdentityProvider} that returns
+     * and empty group of passwords
+     */
+    PasswordIdentityProvider EMPTY_PASSWORDS_PROVIDER = new PasswordIdentityProvider() {
+        @Override
+        public Iterable<String> loadPasswords() {
+            return Collections.emptyList();
+        }
+
+        @Override
+        public String toString() {
+            return "EMPTY";
+        }
+    };
+
+    /**
+     * Invokes {@link PasswordIdentityProvider#loadPasswords()} and returns the result.
+     * Ignores {@code null} providers (i.e., returns an empty iterable instance)
+     */
+    Function<PasswordIdentityProvider, Iterable<String>> LOADER = p ->
+            (p == null) ? Collections.emptyList() : p.loadPasswords();
+
+    /**
+     * @return The currently available passwords - ignored if {@code null}
+     */
+    Iterable<String> loadPasswords();
+
+    /**
+     * Creates a &quot;unified&quot; {@link Iterator} of passwords out of 2 possible
+     * {@link PasswordIdentityProvider}
+     *
+     * @param identities The registered passwords
+     * @param passwords Extra available passwords
+     * @return The wrapping iterator
+     * @see #resolvePasswordIdentityProvider(PasswordIdentityProvider, PasswordIdentityProvider)
+     */
+    static Iterator<String> iteratorOf(PasswordIdentityProvider identities, PasswordIdentityProvider passwords) {
+        return iteratorOf(resolvePasswordIdentityProvider(identities, passwords));
+    }
+
+    /**
+     * Resolves a non-{@code null} iterator of the available passwords
+     *
+     * @param provider The {@link PasswordIdentityProvider} - ignored if {@code null} (i.e.,
+     * return an empty iterator)
+     * @return A non-{@code null} iterator - which may be empty if no provider or no passwords
+     */
+    static Iterator<String> iteratorOf(PasswordIdentityProvider provider) {
+        return GenericUtils.iteratorOf((provider == null) ? null : provider.loadPasswords());
+    }
+
+    /**
+     * <P>Creates a &quot;unified&quot; {@link PasswordIdentityProvider} out of 2 possible ones
+     * as follows:</P></BR>
+     * <UL>
+     *      <LI>If both are {@code null} then return {@code null}.</LI>
+     *      <LI>If either one is {@code null} then use the non-{@code null} one.</LI>
+     *      <LI>If both are the same instance then use it.</U>
+     *      <LI>Otherwise, returns a wrapper that groups both providers.</LI>
+     * </UL>
+     * @param identities The registered passwords
+     * @param passwords The extra available passwords
+     * @return The resolved provider
+     * @see #multiProvider(PasswordIdentityProvider...)
+     */
+    static PasswordIdentityProvider resolvePasswordIdentityProvider(
+            PasswordIdentityProvider identities, PasswordIdentityProvider passwords) {
+        if ((passwords == null) || (identities == passwords)) {
+            return identities;
+        } else if (identities == null) {
+            return passwords;
+        } else {
+            return multiProvider(identities, passwords);
+        }
+    }
+
+    /**
+     * Wraps a group of {@link PasswordIdentityProvider} into a single one
+     *
+     * @param providers The providers - ignored if {@code null}/empty (i.e., returns
+     * {@link #EMPTY_PASSWORDS_PROVIDER}
+     * @return The wrapping provider
+     * @see #multiProvider(Collection)
+     */
+    static PasswordIdentityProvider multiProvider(PasswordIdentityProvider... providers) {
+        return multiProvider(GenericUtils.asList(providers));
+    }
+
+    /**
+     * Wraps a group of {@link PasswordIdentityProvider} into a single one
+     *
+     * @param providers The providers - ignored if {@code null}/empty (i.e., returns
+     * {@link #EMPTY_PASSWORDS_PROVIDER}
+     * @return The wrapping provider
+     */
+    static PasswordIdentityProvider multiProvider(Collection<? extends PasswordIdentityProvider> providers) {
+        return GenericUtils.isEmpty(providers) ? EMPTY_PASSWORDS_PROVIDER : wrapPasswords(iterableOf(providers));
+    }
+
+    /**
+     * Wraps a group of {@link PasswordIdentityProvider} into an {@link Iterable} of their combined passwords
+     *
+     * @param providers The providers - ignored if {@code null}/empty (i.e., returns an empty iterable instance)
+     * @return The wrapping iterable
+     */
+    static Iterable<String> iterableOf(Collection<? extends PasswordIdentityProvider> providers) {
+        Iterable<Supplier<Iterable<String>>> passwordSuppliers =
+                GenericUtils.<PasswordIdentityProvider, Supplier<Iterable<String>>>wrapIterable(providers, p -> p::loadPasswords);
+        return GenericUtils.multiIterableSuppliers(passwordSuppliers);
+    }
+
+    /**
+     * Wraps a group of passwords into a {@link PasswordIdentityProvider}
+     *
+     * @param passwords The passwords - ignored if {@code null}/empty
+     * (i.e., returns {@link #EMPTY_PASSWORDS_PROVIDER})
+     * @return The provider wrapper
+     */
+    static PasswordIdentityProvider wrapPasswords(String... passwords) {
+        return wrapPasswords(GenericUtils.asList(passwords));
+    }
+
+    /**
+     * Wraps a group of passwords into a {@link PasswordIdentityProvider}
+     *
+     * @param passwords The passwords {@link Iterable} - ignored if {@code null}
+     * (i.e., returns {@link #EMPTY_PASSWORDS_PROVIDER})
+     * @return The provider wrapper
+     */
+    static PasswordIdentityProvider wrapPasswords(Iterable<String> passwords) {
+        return (passwords == null) ? EMPTY_PASSWORDS_PROVIDER : () -> passwords;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/client/auth/pubkey/PublicKeyIdentity.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/auth/pubkey/PublicKeyIdentity.java b/sshd-common/src/main/java/org/apache/sshd/client/auth/pubkey/PublicKeyIdentity.java
new file mode 100644
index 0000000..51444ae
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/client/auth/pubkey/PublicKeyIdentity.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.client.auth.pubkey;
+
+import java.security.PublicKey;
+
+/**
+ * Represents a public key identity
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface PublicKeyIdentity {
+    /**
+     * @return The {@link PublicKey} identity value
+     */
+    PublicKey getPublicKey();
+
+    /**
+     * Proves the public key identity by signing the given data
+     *
+     * @param data Data to sign
+     * @return Signed data - using the identity
+     * @throws Exception If failed to sign the data
+     */
+    byte[] sign(byte[] data) throws Exception;
+}
\ No newline at end of file


[13/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common utilities code from sshd-core into sshd-common (new artifact)

Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/io/der/ASN1Object.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/io/der/ASN1Object.java b/sshd-core/src/main/java/org/apache/sshd/common/util/io/der/ASN1Object.java
deleted file mode 100644
index 1d32a40..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/io/der/ASN1Object.java
+++ /dev/null
@@ -1,338 +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.io.der;
-
-import java.io.EOFException;
-import java.io.IOException;
-import java.io.Serializable;
-import java.io.StreamCorruptedException;
-import java.math.BigInteger;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.NumberUtils;
-import org.apache.sshd.common.util.buffer.BufferUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class ASN1Object implements Serializable, Cloneable {
-    // Constructed Flag
-    public static final byte CONSTRUCTED = 0x20;
-
-    private static final long serialVersionUID = 4687581744706127265L;
-
-    private ASN1Class objClass;
-    private ASN1Type objType;
-    private boolean constructed;
-    private int length;
-    private byte[] value;
-
-    public ASN1Object() {
-        super();
-    }
-
-    /*
-     * <P>The first byte in DER encoding is made of following fields</P>
-     * <pre>
-     *-------------------------------------------------
-     *|Bit 8|Bit 7|Bit 6|Bit 5|Bit 4|Bit 3|Bit 2|Bit 1|
-     *-------------------------------------------------
-     *|  Class    | CF  |        Type                 |
-     *-------------------------------------------------
-     * </pre>
-     */
-    public ASN1Object(byte tag, int len, byte... data) {
-        this(ASN1Class.fromDERValue(tag), ASN1Type.fromDERValue(tag), (tag & CONSTRUCTED) == CONSTRUCTED, len, data);
-    }
-
-    public ASN1Object(ASN1Class c, ASN1Type t, boolean ctored, int len, byte... data) {
-        objClass = c;
-        objType = t;
-        constructed = ctored;
-        length = len;
-        value = data;
-    }
-
-    public ASN1Class getObjClass() {
-        return objClass;
-    }
-
-    public void setObjClass(ASN1Class c) {
-        objClass = c;
-    }
-
-    public ASN1Type getObjType() {
-        return objType;
-    }
-
-    public void setObjType(ASN1Type y) {
-        objType = y;
-    }
-
-    public boolean isConstructed() {
-        return constructed;
-    }
-
-    public void setConstructed(boolean c) {
-        constructed = c;
-    }
-
-    public int getLength() {
-        return length;
-    }
-
-    public void setLength(int l) {
-        length = l;
-    }
-
-    public byte[] getValue() {
-        return value;
-    }
-
-    // if length is less than value.length then returns copy of it
-    public byte[] getPureValueBytes() {
-        byte[] bytes = getValue();
-        int available = getLength();
-        int numBytes = NumberUtils.length(bytes);
-        if (numBytes == available) {
-            return bytes;
-        }
-
-        if (available == 0) {
-            return GenericUtils.EMPTY_BYTE_ARRAY;
-        }
-
-        byte[] pure = new byte[available];
-        System.arraycopy(bytes, 0, pure, 0, available);
-        return pure;
-    }
-
-    public void setValue(byte[] v) {
-        value = v;
-    }
-
-    public DERParser createParser() {
-        return new DERParser(getValue(), 0, getLength());
-    }
-
-    public Object asObject() throws IOException {
-        ASN1Type type = getObjType();
-        if (type == null) {
-            throw new IOException("No type set");
-        }
-
-        switch (type) {
-            case INTEGER:
-                return asInteger();
-
-            case NUMERIC_STRING:
-            case PRINTABLE_STRING:
-            case VIDEOTEX_STRING:
-            case IA5_STRING:
-            case GRAPHIC_STRING:
-            case ISO646_STRING:
-            case GENERAL_STRING:
-            case BMP_STRING:
-            case UTF8_STRING:
-                return asString();
-
-            case OBJECT_IDENTIFIER:
-                return asOID();
-
-            case SEQUENCE   :
-                return getValue();
-
-            default:
-                throw new IOException("Invalid DER: unsupported type: " + type);
-        }
-    }
-
-    /**
-     * Get the value as {@link BigInteger}
-     * @return BigInteger
-     * @throws IOException if type not an {@link ASN1Type#INTEGER}
-     */
-    public BigInteger asInteger() throws IOException {
-        ASN1Type typeValue = getObjType();
-        if (ASN1Type.INTEGER.equals(typeValue)) {
-            return toInteger();
-        } else {
-            throw new IOException("Invalid DER: object is not integer: " + typeValue);
-        }
-    }
-
-    // does not check if this is an integer
-    public BigInteger toInteger() {
-        return new BigInteger(getPureValueBytes());
-    }
-
-    /**
-     * Get value as string. Most strings are treated as Latin-1.
-     * @return Java string
-     * @throws IOException if
-     */
-    public String asString() throws IOException {
-        ASN1Type type = getObjType();
-        if (type == null) {
-            throw new IOException("No type set");
-        }
-
-        final String encoding;
-        switch (type) {
-            // Not all are Latin-1 but it's the closest thing
-            case NUMERIC_STRING:
-            case PRINTABLE_STRING:
-            case VIDEOTEX_STRING:
-            case IA5_STRING:
-            case GRAPHIC_STRING:
-            case ISO646_STRING:
-            case GENERAL_STRING:
-                encoding = "ISO-8859-1";
-                break;
-
-            case BMP_STRING:
-                encoding = "UTF-16BE";
-                break;
-
-            case UTF8_STRING:
-                encoding = "UTF-8";
-                break;
-
-            case UNIVERSAL_STRING:
-                throw new IOException("Invalid DER: can't handle UCS-4 string");
-
-            default:
-                throw new IOException("Invalid DER: object is not a string: " + type);
-        }
-
-        return new String(getValue(), 0, getLength(), encoding);
-    }
-
-    public List<Integer> asOID() throws IOException {
-        ASN1Type typeValue = getObjType();
-        if (ASN1Type.OBJECT_IDENTIFIER.equals(typeValue)) {
-            return toOID();
-        } else {
-            throw new StreamCorruptedException("Invalid DER: object is not an OID: " + typeValue);
-        }
-    }
-
-    // Does not check that type is OID
-    public List<Integer> toOID() throws IOException {
-        int vLen = getLength();
-        if (vLen <= 0) {
-            throw new EOFException("Not enough data for an OID");
-        }
-
-        List<Integer> oid = new ArrayList<>(vLen + 1);
-        byte[] bytes = getValue();
-        int val1 = bytes[0] & 0xFF;
-        oid.add(Integer.valueOf(val1 / 40));
-        oid.add(Integer.valueOf(val1 % 40));
-
-        for (int curPos = 1; curPos < vLen; curPos++) {
-            int v = bytes[curPos] & 0xFF;
-            if (v <= 0x7F) {    // short form
-                oid.add(Integer.valueOf(v));
-                continue;
-            }
-
-            long curVal = v & 0x7F;
-            curPos++;
-
-            for (int subLen = 1;; subLen++, curPos++) {
-                if (curPos >= vLen) {
-                    throw new EOFException("Incomplete OID value");
-                }
-
-                if (subLen > 5) {   // 32 bit values can span at most 5 octets
-                    throw new StreamCorruptedException("OID component encoding beyond 5 bytes");
-                }
-
-                v = bytes[curPos] & 0xFF;
-                curVal = ((curVal << 7) & 0xFFFFFFFF80L) | (v & 0x7FL);
-                if (curVal  > Integer.MAX_VALUE) {
-                    throw new StreamCorruptedException("OID value exceeds 32 bits: " + curVal);
-                }
-
-                if (v <= 0x7F) {    // found last octet ?
-                    break;
-                }
-            }
-
-            oid.add(Integer.valueOf((int) (curVal & 0x7FFFFFFFL)));
-        }
-
-        return oid;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(getObjClass(), getObjType())
-             + Boolean.hashCode(isConstructed())
-             + getLength()
-             + NumberUtils.hashCode(getValue(), 0, getLength());
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (obj == null) {
-            return false;
-        }
-        if (this == obj) {
-            return true;
-        }
-        if (getClass() != obj.getClass()) {
-            return false;
-        }
-
-        ASN1Object other = (ASN1Object) obj;
-        return Objects.equals(this.getObjClass(), other.getObjClass())
-            && Objects.equals(this.getObjType(), other.getObjType())
-            && (this.isConstructed() == other.isConstructed())
-            && (this.getLength() == other.getLength())
-            && (NumberUtils.diffOffset(this.getValue(), 0, other.getValue(), 0, this.getLength()) < 0);
-    }
-
-    @Override
-    public ASN1Object clone() {
-        try {
-            ASN1Object cpy = getClass().cast(super.clone());
-            byte[] data = cpy.getValue();
-            if (data != null) {
-                cpy.setValue(data.clone());
-            }
-            return cpy;
-        } catch (CloneNotSupportedException e) {
-            throw new IllegalStateException("Unexpected clone failure: " + e.getMessage(), e);
-        }
-    }
-
-    @Override
-    public String toString() {
-        return Objects.toString(getObjClass())
-             + "/" + getObjType()
-             + "/" + isConstructed()
-             + "[" + getLength() + "]"
-             + ": " + BufferUtils.toHex(getValue(), 0, getLength(), ':');
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/io/der/ASN1Type.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/io/der/ASN1Type.java b/sshd-core/src/main/java/org/apache/sshd/common/util/io/der/ASN1Type.java
deleted file mode 100644
index 3dd13ca..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/io/der/ASN1Type.java
+++ /dev/null
@@ -1,118 +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.io.der;
-
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.Set;
-
-import org.apache.sshd.common.util.GenericUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public enum ASN1Type {
-    ANY((byte) 0x00),
-    BOOLEAN((byte) 0x01),
-    INTEGER((byte) 0x02),
-    BIT_STRING((byte) 0x03),
-    OCTET_STRING((byte) 0x04),
-    NULL((byte) 0x05),
-    OBJECT_IDENTIFIER((byte) 0x06),
-    REAL((byte) 0x09),
-    ENUMERATED((byte) 0x0a),
-    RELATIVE_OID((byte) 0x0d),
-    SEQUENCE((byte) 0x10),
-    SET((byte) 0x11),
-    NUMERIC_STRING((byte) 0x12),
-    PRINTABLE_STRING((byte) 0x13),
-    T61_STRING((byte) 0x14),
-    VIDEOTEX_STRING((byte) 0x15),
-    IA5_STRING((byte) 0x16),
-    GRAPHIC_STRING((byte) 0x19),
-    ISO646_STRING((byte) 0x1A),
-    GENERAL_STRING((byte) 0x1B),
-    UTF8_STRING((byte) 0x0C),
-    UNIVERSAL_STRING((byte) 0x1C),
-    BMP_STRING((byte) 0x1E),
-    UTC_TIME((byte) 0x17),
-    GENERALIZED_TIME((byte) 0x18);
-
-    public static final Set<ASN1Type> VALUES =
-            Collections.unmodifiableSet(EnumSet.allOf(ASN1Type.class));
-
-    private final byte typeValue;
-
-    ASN1Type(byte typeVal) {
-        typeValue = typeVal;
-    }
-
-    public byte getTypeValue() {
-        return typeValue;
-    }
-
-    public static ASN1Type fromName(String s) {
-        if (GenericUtils.isEmpty(s)) {
-            return null;
-        }
-
-        for (ASN1Type t : VALUES) {
-            if (s.equalsIgnoreCase(t.name())) {
-                return t;
-            }
-        }
-
-        return null;
-    }
-
-    /**
-     * <P>The first byte in DER encoding is made of following fields</P>
-     * <pre>
-     *-------------------------------------------------
-     *|Bit 8|Bit 7|Bit 6|Bit 5|Bit 4|Bit 3|Bit 2|Bit 1|
-     *-------------------------------------------------
-     *|  Class    | CF  |        Type                 |
-     *-------------------------------------------------
-     * </pre>
-     * @param value The original DER encoded byte
-     * @return The {@link ASN1Type} value - {@code null} if no match found
-     * @see #fromTypeValue(int)
-     */
-    public static ASN1Type fromDERValue(int value) {
-        return fromTypeValue(value & 0x1F);
-    }
-
-    /**
-     * @param value The &quot;pure&quot; type value - with no extra bits set
-     * @return The {@link ASN1Type} value - {@code null} if no match found
-     */
-    public static ASN1Type fromTypeValue(int value) {
-        if ((value < 0) || (value > 0x1F)) {    // only 5 bits are used
-            return null;
-        }
-
-        for (ASN1Type t : VALUES) {
-            if (t.getTypeValue() == value) {
-                return t;
-            }
-        }
-
-        return null;
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/io/der/DERParser.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/io/der/DERParser.java b/sshd-core/src/main/java/org/apache/sshd/common/util/io/der/DERParser.java
deleted file mode 100644
index 5f37bd7..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/io/der/DERParser.java
+++ /dev/null
@@ -1,151 +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.io.der;
-
-import java.io.ByteArrayInputStream;
-import java.io.FilterInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.StreamCorruptedException;
-import java.math.BigInteger;
-import java.util.Arrays;
-
-import org.apache.sshd.common.util.NumberUtils;
-import org.apache.sshd.common.util.buffer.BufferUtils;
-
-/**
- * A bare minimum DER parser - just enough to be able to decode
- * signatures and private keys
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class DERParser extends FilterInputStream {
-    /**
-     * Maximum size of data allowed by {@link #readLength()} - it is a bit
-     * arbitrary since one can encode 32-bit length data, but it is good
-     * enough for the keys
-     */
-    public static final int MAX_DER_VALUE_LENGTH = 2 * Short.MAX_VALUE;
-
-    private final byte[] lenBytes = new byte[Integer.BYTES];
-
-    public DERParser(byte... bytes) {
-        this(bytes, 0, NumberUtils.length(bytes));
-    }
-
-    public DERParser(byte[] bytes, int offset, int len) {
-        this(new ByteArrayInputStream(bytes, offset, len));
-    }
-
-    public DERParser(InputStream s) {
-        super(s);
-    }
-
-    /**
-     * Decode the length of the field. Can only support length
-     * encoding up to 4 octets. In BER/DER encoding, length can
-     * be encoded in 2 forms:
-     * <ul>
-     * <li><p>
-     * Short form - One octet. Bit 8 has value "0" and bits 7-1
-     * give the length.
-     * </p></li>
-     *
-     * <li><p>
-     * Long form - Two to 127 octets (only 4 is supported here).
-     * Bit 8 of first octet has value "1" and bits 7-1 give the
-     * number of additional length octets. Second and following
-     * octets give the length, base 256, most significant digit
-     * first.
-     * </p></li>
-     * </ul>
-     *
-     * @return The length as integer
-     * @throws IOException If invalid format found
-     */
-    public int readLength() throws IOException {
-        int i = read();
-        if (i == -1) {
-            throw new StreamCorruptedException("Invalid DER: length missing");
-        }
-
-        // A single byte short length
-        if ((i & ~0x7F) == 0) {
-            return i;
-        }
-
-        int num = i & 0x7F;
-        // TODO We can't handle length longer than 4 bytes
-        if ((i >= 0xFF) || (num > lenBytes.length)) {
-            throw new StreamCorruptedException("Invalid DER: length field too big: " + i);
-        }
-
-        // place the read bytes last so that the 1st ones are zeroes as big endian
-        Arrays.fill(lenBytes, (byte) 0);
-        int n = read(lenBytes, 4 - num, num);
-        if (n < num) {
-            throw new StreamCorruptedException("Invalid DER: length data too short: expected=" + num + ", actual=" + n);
-        }
-
-        long len = BufferUtils.getUInt(lenBytes);
-        if (len < 0x7FL) {   // according to standard: "the shortest possible length encoding must be used"
-            throw new StreamCorruptedException("Invalid DER: length not in shortest form: " + len);
-        }
-
-        if (len > MAX_DER_VALUE_LENGTH) {
-            throw new StreamCorruptedException("Invalid DER: data length too big: " + len + " (max=" + MAX_DER_VALUE_LENGTH + ")");
-        }
-
-        // we know the cast is safe since it is less than MAX_DER_VALUE_LENGTH which is ~64K
-        return (int) len;
-    }
-
-    public ASN1Object readObject() throws IOException {
-        int tag = read();
-        if (tag == -1) {
-            return null;
-        }
-
-        int length = readLength();
-        byte[] value = new byte[length];
-        int n = read(value);
-        if (n < length) {
-            throw new StreamCorruptedException("Invalid DER: stream too short, missing value: read " + n + " out of required " + length);
-        }
-
-        return new ASN1Object((byte) tag, length, value);
-    }
-
-    public BigInteger readBigInteger() throws IOException {
-        int type = read();
-        if (type != 0x02) {
-            throw new StreamCorruptedException("Invalid DER: data type is not an INTEGER: 0x" + Integer.toHexString(type));
-        }
-
-        int len = readLength();
-        byte[] value = new byte[len];
-        int n = read(value);
-        if (n < len) {
-            throw new StreamCorruptedException("Invalid DER: stream too short, missing value: read " + n + " out of required " + len);
-        }
-
-        return new BigInteger(value);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/io/der/DERWriter.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/io/der/DERWriter.java b/sshd-core/src/main/java/org/apache/sshd/common/util/io/der/DERWriter.java
deleted file mode 100644
index bc603ee..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/io/der/DERWriter.java
+++ /dev/null
@@ -1,172 +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.io.der;
-
-import java.io.ByteArrayOutputStream;
-import java.io.FilterOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.StreamCorruptedException;
-import java.math.BigInteger;
-import java.util.Objects;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import org.apache.sshd.common.util.NumberUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.buffer.BufferUtils;
-import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
-
-/**
- * A bare-minimum DER encoder - just enough so we can encoder signatures
- * and keys data
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class DERWriter extends FilterOutputStream {
-    private final byte[] lenBytes = new byte[Integer.BYTES];
-
-    public DERWriter() {
-        this(ByteArrayBuffer.DEFAULT_SIZE);
-    }
-
-    public DERWriter(int initialSize) {
-        this(new ByteArrayOutputStream(initialSize));
-    }
-
-    public DERWriter(OutputStream stream) {
-        super(Objects.requireNonNull(stream, "No output stream"));
-    }
-
-    public DERWriter startSequence() {
-        ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        AtomicBoolean dataWritten = new AtomicBoolean(false);
-        @SuppressWarnings("resource")
-        DERWriter encloser = this;
-        return new DERWriter(baos) {
-            @Override
-            public void close() throws IOException {
-                baos.close();
-
-                if (!dataWritten.getAndSet(true)) { // detect repeated calls and write this only once
-                    encloser.writeObject(new ASN1Object(ASN1Class.UNIVERSAL, ASN1Type.SEQUENCE, false, baos.size(), baos.toByteArray()));
-                }
-            }
-        };
-    }
-
-    public void writeBigInteger(BigInteger value) throws IOException {
-        writeBigInteger(Objects.requireNonNull(value, "No value").toByteArray());
-    }
-
-    /**
-     * The integer is always considered to be positive, so if the first byte is < 0,
-     * we pad with a zero to make it positive
-     *
-     * @param bytes {@link BigInteger} bytes
-     * @throws IOException If failed to write the bytes
-     */
-    public void writeBigInteger(byte... bytes) throws IOException {
-        writeBigInteger(bytes, 0, NumberUtils.length(bytes));
-    }
-
-    /**
-     * The integer is always considered to be positive, so if the first byte is < 0,
-     * we pad with a zero to make it positive
-     *
-     * @param bytes {@link BigInteger} bytes
-     * @param off Offset in bytes data
-     * @param len Number of bytes to write
-     * @throws IOException If failed to write the bytes
-     */
-    public void writeBigInteger(byte[] bytes, int off, int len) throws IOException {
-        // Strip leading zeroes
-        while (len > 1 && bytes[off] == 0 && isPositive(bytes[off + 1])) {
-            off++;
-            len--;
-        }
-        // indicate it is an INTEGER
-        write(0x02);
-        // Pad with a zero if needed
-        if (isPositive(bytes[off])) {
-            writeLength(len);
-        } else {
-            writeLength(len + 1);
-            write(0);
-        }
-        // Write data
-        write(bytes, off, len);
-    }
-
-    private boolean isPositive(byte b) {
-        return (b & 0x80) == 0;
-    }
-
-    public void writeObject(ASN1Object obj) throws IOException {
-        Objects.requireNonNull(obj, "No ASN.1 object");
-
-        ASN1Type type = obj.getObjType();
-        byte typeValue = type.getTypeValue();
-        ASN1Class clazz = obj.getObjClass();
-        byte classValue = clazz.getClassValue();
-        byte tagValue = (byte) (((classValue << 6) & 0xC0) | (typeValue & 0x1F));
-        writeObject(tagValue, obj.getLength(), obj.getValue());
-    }
-
-    public void writeObject(byte tag, int len, byte... data) throws IOException {
-        write(tag & 0xFF);
-        writeLength(len);
-        write(data, 0, len);
-    }
-
-    public void writeLength(int len) throws IOException {
-        ValidateUtils.checkTrue(len >= 0, "Invalid length: %d", len);
-
-        // short form - MSBit is zero
-        if (len <= 127) {
-            write(len);
-            return;
-        }
-
-        BufferUtils.putUInt(len, lenBytes);
-
-        int nonZeroPos = 0;
-        for (; nonZeroPos < lenBytes.length; nonZeroPos++) {
-            if (lenBytes[nonZeroPos] != 0) {
-                break;
-            }
-        }
-
-        if (nonZeroPos >= lenBytes.length) {
-            throw new StreamCorruptedException("All zeroes length representation for len=" + len);
-        }
-
-        int bytesLen = lenBytes.length - nonZeroPos;
-        write(0x80 | bytesLen); // indicate number of octets
-        write(lenBytes, nonZeroPos, bytesLen);
-    }
-
-    public byte[] toByteArray() throws IOException {
-        if (this.out instanceof ByteArrayOutputStream) {
-            return ((ByteArrayOutputStream) this.out).toByteArray();
-        } else {
-            throw new IOException("The underlying stream is not a byte[] stream");
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/io/functors/IOFunction.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/io/functors/IOFunction.java b/sshd-core/src/main/java/org/apache/sshd/common/util/io/functors/IOFunction.java
deleted file mode 100644
index a55ac2e..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/io/functors/IOFunction.java
+++ /dev/null
@@ -1,86 +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.io.functors;
-
-import java.io.IOException;
-import java.util.Objects;
-
-/**
- * Invokes some I/O function on the input returning some output
- * and potentially throwing an {@link IOException} in the process
- *
- * @param <T> Type of input
- * @param <R> Type of output
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FunctionalInterface
-public interface IOFunction<T, R> {
-    R apply(T t) throws IOException;
-
-    /**
-     * Returns a composed function that first applies the {@code before}
-     * function to its input, and then applies this function to the result.
-     * If evaluation of either function throws an exception, it is relayed to
-     * the caller of the composed function.
-     *
-     * @param <V> the type of input to the {@code before} function, and to the
-     *           composed function
-     * @param before the function to apply before this function is applied
-     * @return a composed function that first applies the {@code before}
-     * function and then applies this function
-     * @throws NullPointerException if before is null
-     *
-     * @see #andThen(IOFunction)
-     */
-    default <V> IOFunction<V, R> compose(IOFunction<? super V, ? extends T> before) {
-        Objects.requireNonNull(before, "No composing function provided");
-        return (V v) -> apply(before.apply(v));
-    }
-
-    /**
-     * Returns a composed function that first applies this function to
-     * its input, and then applies the {@code after} function to the result.
-     * If evaluation of either function throws an exception, it is relayed to
-     * the caller of the composed function.
-     *
-     * @param <V> the type of output of the {@code after} function, and of the
-     *           composed function
-     * @param after the function to apply after this function is applied
-     * @return a composed function that first applies this function and then
-     * applies the {@code after} function
-     * @throws NullPointerException if after is null
-     *
-     * @see #compose(IOFunction)
-     */
-    default <V> IOFunction<T, V> andThen(IOFunction<? super R, ? extends V> after) {
-        Objects.requireNonNull(after, "No composing function provided");
-        return (T t) -> after.apply(apply(t));
-    }
-
-    /**
-     * Returns a function that always returns its input argument.
-     *
-     * @param <T> the type of the input and output objects to the function
-     * @return a function that always returns its input argument
-     */
-    static <T> IOFunction<T, T> identity() {
-        return t -> t;
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/logging/AbstractLoggingBean.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/logging/AbstractLoggingBean.java b/sshd-core/src/main/java/org/apache/sshd/common/util/logging/AbstractLoggingBean.java
deleted file mode 100644
index 27fef2f..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/logging/AbstractLoggingBean.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.common.util.logging;
-
-import java.util.concurrent.atomic.AtomicReference;
-
-import org.apache.sshd.common.util.GenericUtils;
-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;
-    private final AtomicReference<SimplifiedLog> simplifiedLog = new AtomicReference<>();
-
-    /**
-     * Default constructor - creates a logger using the full class name
-     */
-    protected AbstractLoggingBean() {
-        this("");
-    }
-
-    /**
-     * 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 - ignored if {@code null}
-     * or empty
-     */
-    protected AbstractLoggingBean(String discriminator) {
-        String name = getClass().getName();
-        if (GenericUtils.length(discriminator) > 0) {
-            name += "[" + discriminator + "]";
-        }
-        log = LoggerFactory.getLogger(name);
-    }
-
-    protected SimplifiedLog getSimplifiedLogger() {
-        SimplifiedLog logger;
-        synchronized (simplifiedLog) {
-            logger = simplifiedLog.get();
-            if (logger == null) {
-                logger = LoggingUtils.wrap(log);
-            }
-        }
-
-        return logger;
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/logging/LoggingUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/logging/LoggingUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/logging/LoggingUtils.java
deleted file mode 100644
index 674ed1a..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/logging/LoggingUtils.java
+++ /dev/null
@@ -1,549 +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.logging;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-import java.util.Map;
-import java.util.NavigableMap;
-import java.util.Objects;
-import java.util.TreeMap;
-import java.util.function.Consumer;
-import java.util.function.Predicate;
-import java.util.logging.Level;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.NumberUtils;
-import org.apache.sshd.common.util.ReflectionUtils;
-import org.slf4j.Logger;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public final class LoggingUtils {
-
-    private LoggingUtils() {
-        throw new UnsupportedOperationException("No instance");
-    }
-
-    /**
-     * Scans using reflection API for all fields that are {@code public static final}
-     * that start with the given common prefix (case <U>sensitive</U>) and are of type
-     * {@link Number}.
-     *
-     * @param clazz The {@link Class} to query
-     * @param commonPrefix The expected common prefix
-     * @return A {@link NavigableMap} of all the matching fields, where key=the field's {@link Integer}
-     * value and mapping=the field's name
-     * @see #generateMnemonicMap(Class, Predicate)
-     */
-    public static NavigableMap<Integer, String> generateMnemonicMap(Class<?> clazz, final String commonPrefix) {
-        return generateMnemonicMap(clazz, f -> {
-            String name = f.getName();
-            return name.startsWith(commonPrefix);
-        });
-    }
-
-    /**
-     * Scans using reflection API for all <U>numeric {@code public static final}</U> fields
-     * that are also accepted by the predicate. Any field that is not such or fail to retrieve
-     * its value, or has a duplicate value is <U>silently</U> skipped.
-     *
-     * @param clazz The {@link Class} to query
-     * @param acceptor The {@link Predicate} used to decide whether to process the {@link Field}
-     * (besides being a {@link Number} and {@code public static final}).
-     * @return A {@link NavigableMap} of all the matching fields, where key=the field's {@link Integer}
-     * value and mapping=the field's name
-     * @see #getMnemonicFields(Class, Predicate)
-     */
-    public static NavigableMap<Integer, String> generateMnemonicMap(Class<?> clazz, Predicate<? super Field> acceptor) {
-        Collection<Field> fields = getMnemonicFields(clazz, acceptor);
-        if (GenericUtils.isEmpty(fields)) {
-            return Collections.emptyNavigableMap();
-        }
-
-        NavigableMap<Integer, String> result = new TreeMap<>(Comparator.naturalOrder());
-        for (Field f : fields) {
-            String name = f.getName();
-            try {
-                Number value = (Number) f.get(null);
-                String prev = result.put(NumberUtils.toInteger(value), name);
-                if (prev != null) {
-                    //noinspection UnnecessaryContinue
-                    continue;   // debug breakpoint
-                }
-            } catch (Exception e) {
-                //noinspection UnnecessaryContinue
-                continue;   // debug breakpoint
-            }
-        }
-
-        return result;
-    }
-
-    /**
-     * Scans using reflection API for all <U>numeric {@code public static final}</U> fields
-     * that have a common prefix and whose value is used by several of the other
-     * matching fields
-     *
-     * @param clazz The {@link Class} to query
-     * @param commonPrefix The expected common prefix
-     * @return A {@link Map} of all the mnemonic fields names whose value is the same as other
-     * fields in this map. The key is the field's name and value is its associated opcode.
-     * @see #getAmbiguousMenmonics(Class, Predicate)
-     */
-    public static Map<String, Integer> getAmbiguousMenmonics(Class<?> clazz, String commonPrefix) {
-        return getAmbiguousMenmonics(clazz, f -> {
-            String name = f.getName();
-            return name.startsWith(commonPrefix);
-        });
-    }
-
-    /**
-     * Scans using reflection API for all <U>numeric {@code public static final}</U> fields
-     * that are also accepted by the predicate and whose value is used by several of the other
-     * matching fields
-     *
-     * @param clazz The {@link Class} to query
-     * @param acceptor The {@link Predicate} used to decide whether to process the {@link Field}
-     * (besides being a {@link Number} and {@code public static final}).
-     * @return A {@link Map} of all the mnemonic fields names whose value is the same as other
-     * fields in this map. The key is the field's name and value is its associated opcode.
-     * @see #getMnemonicFields(Class, Predicate)
-     */
-    public static Map<String, Integer> getAmbiguousMenmonics(Class<?> clazz, Predicate<? super Field> acceptor) {
-        Collection<Field> fields = getMnemonicFields(clazz, acceptor);
-        if (GenericUtils.isEmpty(fields)) {
-            return Collections.emptyMap();
-        }
-
-        Map<String, Integer> result = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
-        Map<Integer, List<String>> opcodesMap = new TreeMap<>(Comparator.naturalOrder());
-        for (Field f : fields) {
-            String name = f.getName();
-            try {
-                Number value = (Number) f.get(null);
-                Integer key = NumberUtils.toInteger(value);
-                List<String> nameList = opcodesMap.get(key);
-                if (nameList == null) {
-                    nameList = new ArrayList<>();
-                    opcodesMap.put(key, nameList);
-                }
-                nameList.add(name);
-
-                int numOpcodes = nameList.size();
-                if (numOpcodes > 1) {
-                    result.put(name, key);
-                    if (numOpcodes == 2) {  // add the 1st name as well
-                        result.put(nameList.get(0), key);
-                    }
-                }
-            } catch (Exception e) {
-                continue;   // debug breakpoint
-            }
-        }
-
-        return result;
-    }
-
-    /**
-     * Scans using reflection API for all <U>numeric {@code public static final}</U> fields
-     * that are also accepted by the predicate.
-     *
-     * @param clazz The {@link Class} to query
-     * @param acceptor The {@link Predicate} used to decide whether to process the {@link Field}
-     * (besides being a {@link Number} and {@code public static final}).
-     * @return A {@link Collection} of all the fields that have satisfied all conditions
-     */
-    public static Collection<Field> getMnemonicFields(Class<?> clazz, Predicate<? super Field> acceptor) {
-        return ReflectionUtils.getMatchingFields(clazz, f -> {
-            int mods = f.getModifiers();
-            if ((!Modifier.isPublic(mods)) || (!Modifier.isStatic(mods)) || (!Modifier.isFinal(mods))) {
-                return false;
-            }
-
-            Class<?> type = f.getType();
-            if (!NumberUtils.isNumericClass(type)) {
-                return false;
-            }
-
-            return acceptor.test(f);
-        });
-    }
-
-    /**
-     * Verifies if the given level is above the required threshold for logging.
-     *
-     * @param level     The {@link Level} to evaluate
-     * @param threshold The threshold {@link Level}
-     * @return {@code true} if the evaluated level is above the required
-     * threshold.
-     * <P>
-     * <B>Note(s):</B>
-     * </P>
-     * <UL>
-     * <LI><P>
-     * If either argument is {@code null} then result is {@code false}.
-     * </P></LI>
-     *
-     * <LI><P>
-     * If the evaluated level is {@link Level#OFF} then result is {@code false}
-     * regardless of the threshold.
-     * </P></LI>
-     *
-     * <LI><P>
-     * If the threshold is {@link Level#ALL} and the evaluated level is
-     * <U>not</U> {@link Level#OFF} the result is {@code true}.
-     * </P></LI>
-     *
-     * <LI><P>
-     * Otherwise, the evaluated level {@link Level#intValue()} must be
-     * greater or equal to the threshold.
-     * </P></LI>
-     * </UL>
-     */
-    public static boolean isLoggable(Level level, Level threshold) {
-        if ((level == null) || (threshold == null)) {
-            return false;
-        } else if (Level.OFF.equals(level) || Level.OFF.equals(threshold)) {
-            return false;
-        } else if (Level.ALL.equals(threshold)) {
-            return true;
-        } else {
-            return level.intValue() >= threshold.intValue();
-        }
-    }
-
-    public static SimplifiedLog wrap(final Logger logger) {
-        if (logger == null) {
-            return SimplifiedLog.EMPTY;
-        } else {
-            return new SimplifiedLog() {
-                @Override
-                public void log(Level level, Object message, Throwable t) {
-                    if (isEnabled(level)) {
-                        logMessage(logger, level, message, t);
-                    }
-
-                }
-
-                @Override
-                public boolean isEnabled(Level level) {
-                    return isLoggable(logger, level);
-                }
-            };
-        }
-    }
-
-    // NOTE: assume that level enabled has been checked !!!
-    public static void logMessage(Logger logger, Level level, Object message, Throwable t) {
-        if ((logger == null) || (level == null) || Level.OFF.equals(level)) {
-            return;
-        } else if (Level.SEVERE.equals(level)) {
-            logger.error(Objects.toString(message), t);
-        } else if (Level.WARNING.equals(level)) {
-            logger.warn(Objects.toString(message), t);
-        } else if (Level.INFO.equals(level) || Level.ALL.equals(level)) {
-            logger.info(Objects.toString(message), t);
-        } else if (Level.CONFIG.equals(level) || Level.FINE.equals(level)) {
-            logger.debug(Objects.toString(message), t);
-        } else {
-            logger.trace(Objects.toString(message), t);
-        }
-    }
-
-    /**
-     * @param logger The {@link Logger} instance - ignored if {@code null}
-     * @param level  The validate log {@link Level} - ignored if {@code null}
-     * @return <P>{@code true} if the level is enabled for the logger. The
-     * mapping of the level to the logger is as follows:</P>
-     * <UL>
-     * <LI>{@link Level#OFF} always returns {@code false}</LI>
-     * <LI>{@link Level#SEVERE} returns {@link Logger#isErrorEnabled()}</LI>
-     * <LI>{@link Level#WARNING} returns {@link Logger#isWarnEnabled()}</LI>
-     * <LI>{@link Level#INFO} and {@link Level#ALL} returns {@link Logger#isInfoEnabled()}</LI>
-     * <LI>{@link Level#CONFIG} and {@link Level#FINE} returns {@link Logger#isDebugEnabled()}</LI>
-     * <LI>All other levels return {@link Logger#isTraceEnabled()}</LI>
-     * </UL>
-     */
-    public static boolean isLoggable(Logger logger, Level level) {
-        if ((logger == null) || (level == null) || Level.OFF.equals(level)) {
-            return false;
-        } else if (Level.SEVERE.equals(level)) {
-            return logger.isErrorEnabled();
-        } else if (Level.WARNING.equals(level)) {
-            return logger.isWarnEnabled();
-        } else if (Level.INFO.equals(level) || Level.ALL.equals(level)) {
-            return logger.isInfoEnabled();
-        } else if (Level.CONFIG.equals(level) || Level.FINE.equals(level)) {
-            return logger.isDebugEnabled();
-        } else {
-            return logger.isTraceEnabled();
-        }
-    }
-
-    /**
-     * @param <T> Generic message type consumer
-     * @param logger The {@link Logger} instance to use
-     * @param level The log {@link Level} mapped as follows:</BR>
-     *
-     * <UL>
-     *     <LI>{@link Level#OFF} - {@link #nologClosure(Logger)}</LI>
-     *     <LI>{@link Level#SEVERE} - {@link #errorClosure(Logger)}</LI>
-     *     <LI>{@link Level#WARNING} - {@link #warnClosure(Logger)}</LI>
-     *     <LI>{@link Level#INFO}/{@link Level#ALL} - {@link #infoClosure(Logger)}</LI>
-     *     <LI>{@link Level#CONFIG}/{@link Level#FINE} - {@link #debugClosure(Logger)}</LI>
-     *     <LI>All others - {@link #traceClosure(Logger)}</LI>
-     * </UL>
-     * @return A consumer whose {@link Consumer#accept(Object)} method logs the
-     * {@link String#valueOf(Object)} value of its argument if the specific level is enabled
-     */
-    public static <T> Consumer<T> loggingClosure(Logger logger, Level level) {
-        return loggingClosure(logger, level, null);
-    }
-
-    public static <T> Consumer<T> loggingClosure(Logger logger, Level level, Throwable t) {
-        Objects.requireNonNull(level, "No level provided");
-
-        if (Level.OFF.equals(level)) {
-            return nologClosure(logger);
-        } else if (Level.SEVERE.equals(level)) {
-            return errorClosure(logger, t);
-        } else if (Level.WARNING.equals(level)) {
-            return warnClosure(logger, t);
-        } else if (Level.INFO.equals(level) || Level.ALL.equals(level)) {
-            return infoClosure(logger, t);
-        } else if (Level.CONFIG.equals(level) || Level.FINE.equals(level)) {
-            return debugClosure(logger, t);
-        } else {
-            return traceClosure(logger, t);
-        }
-    }
-
-    /**
-     * @param <T> Generic message type consumer
-     * @param logger The {@link Logger} instance to use
-     * @return A consumer whose {@link Consumer#accept(Object)} method logs nothing when invoked
-     */
-    public static <T> Consumer<T> nologClosure(Logger logger) {
-        Objects.requireNonNull(logger, "No logger provided");
-        return t -> { /* do nothing */ };
-    }
-
-    /**
-     * @param <T> Generic message type consumer
-     * @param logger The {@link Logger} instance to use
-     * @return A consumer whose {@link Consumer#accept(Object)} method logs the
-     * {@link String#valueOf(Object)} value of its argument if {@link Logger#isErrorEnabled()}
-     */
-    public static <T> Consumer<T> errorClosure(Logger logger) {
-        return errorClosure(logger, null);
-    }
-
-    /**
-     * @param <T> Generic message type consumer
-     * @param logger The {@link Logger} instance to use
-     * @param thrown A {@link Throwable} to attach to the message - ignored if {@code null}
-     * @return A consumer whose {@link Consumer#accept(Object)} method logs the
-     * {@link String#valueOf(Object)} value of its argument if {@link Logger#isErrorEnabled()}
-     */
-    public static <T> Consumer<T> errorClosure(Logger logger, Throwable thrown) {
-        Objects.requireNonNull(logger, "No logger provided");
-        return new Consumer<T>() {
-            @Override
-            public void accept(T input) {
-                if (logger.isErrorEnabled()) {
-                    String msg = String.valueOf(input);
-                    if (thrown == null) {
-                        logger.error(msg);
-                    } else {
-                        logger.error(msg, thrown);
-                    }
-                }
-            }
-
-            @Override
-            public String toString() {
-                return "ERROR";
-            }
-        };
-    }
-
-    /**
-     * @param <T> Generic message type consumer
-     * @param logger The {@link Logger} instance to use
-     * @return A consumer whose {@link Consumer#accept(Object)} method logs the
-     * {@link String#valueOf(Object)} value of its argument if {@link Logger#isWarnEnabled()}
-     */
-    public static <T> Consumer<T> warnClosure(Logger logger) {
-        return warnClosure(logger, null);
-    }
-
-    /**
-     * @param <T> Generic message type consumer
-     * @param logger The {@link Logger} instance to use
-     * @param thrown A {@link Throwable} to attach to the message - ignored if {@code null}
-     * @return A consumer whose {@link Consumer#accept(Object)} method logs the {@link String#valueOf(Object)}
-     * value of its argument if {@link Logger#isWarnEnabled()}
-     */
-    public static <T> Consumer<T> warnClosure(Logger logger, Throwable thrown) {
-        Objects.requireNonNull(logger, "No logger provided");
-        return new Consumer<T>() {
-            @Override
-            public void accept(T input) {
-                if (logger.isWarnEnabled()) {
-                    String msg = String.valueOf(input);
-                    if (thrown == null) {
-                        logger.warn(msg);
-                    } else {
-                        logger.warn(msg, thrown);
-                    }
-                }
-            }
-
-            @Override
-            public String toString() {
-                return "WARN";
-            }
-        };
-    }
-
-    /**
-     * @param <T> Generic message type consumer
-     * @param logger The {@link Logger} instance to use
-     * @return A consumer whose {@link Consumer#accept(Object)} method logs the {@link String#valueOf(Object)}
-     * value of its argument if {@link Logger#isInfoEnabled()}
-     */
-    public static <T> Consumer<T> infoClosure(Logger logger) {
-        return infoClosure(logger, null);
-    }
-
-    /**
-     * @param <T> Generic message type consumer
-     * @param logger The {@link Logger} instance to use
-     * @param thrown A {@link Throwable} to attach to the message - ignored if {@code null}
-     * @return A consumer whose {@link Consumer#accept(Object)} method logs the
-     * {@link String#valueOf(Object)} value of its argument if {@link Logger#isInfoEnabled()}
-     */
-    public static <T> Consumer<T> infoClosure(Logger logger, Throwable thrown) {
-        Objects.requireNonNull(logger, "No logger provided");
-        return new Consumer<T>() {
-            @Override
-            public void accept(T input) {
-                if (logger.isInfoEnabled()) {
-                    String msg = String.valueOf(input);
-                    if (thrown == null) {
-                        logger.info(msg);
-                    } else {
-                        logger.info(msg, thrown);
-                    }
-                }
-            }
-
-            @Override
-            public String toString() {
-                return "INFO";
-            }
-        };
-    }
-
-    /**
-     * @param <T> Generic message type consumer
-     * @param logger The {@link Logger} instance to use
-     * @return A consumer whose {@link Consumer#accept(Object)} method logs the
-     * {@link String#valueOf(Object)} value of its argument if {@link Logger#isDebugEnabled()}
-     */
-    public static <T> Consumer<T> debugClosure(Logger logger) {
-        return debugClosure(logger, null);
-    }
-
-    /**
-     * @param <T> Generic message type consumer
-     * @param logger The {@link Logger} instance to use
-     * @param thrown A {@link Throwable} to attach to the message - ignored if {@code null}
-     * @return A consumer whose {@link Consumer#accept(Object)} method logs the
-     * {@link String#valueOf(Object)} value of its argument if {@link Logger#isDebugEnabled()}
-     */
-    public static <T> Consumer<T> debugClosure(Logger logger, Throwable thrown) {
-        Objects.requireNonNull(logger, "No logger provided");
-        return new Consumer<T>() {
-            @Override
-            public void accept(T input) {
-                if (logger.isDebugEnabled()) {
-                    String msg = String.valueOf(input);
-                    if (thrown == null) {
-                        logger.debug(msg);
-                    } else {
-                        logger.debug(msg, thrown);
-                    }
-                }
-            }
-
-            @Override
-            public String toString() {
-                return "DEBUG";
-            }
-        };
-    }
-
-    /**
-     * @param <T> Generic message type consumer
-     * @param logger The {@link Logger} instance to use
-     * @return A consumer whose {@link Consumer#accept(Object)} method logs the
-     * {@link String#valueOf(Object)} value of its argument if {@link Logger#isTraceEnabled()}
-     */
-    public static <T> Consumer<T> traceClosure(Logger logger) {
-        return traceClosure(logger, null);
-    }
-
-    /**
-     * @param <T> Generic message type consumer
-     * @param logger The {@link Logger} instance to use
-     * @param thrown A {@link Throwable} to attach to the message - ignored if {@code null}
-     * @return A consumer whose {@link Consumer#accept(Object)} method logs the
-     * {@link String#valueOf(Object)} value of its argument if {@link Logger#isTraceEnabled()}
-     */
-    public static <T> Consumer<T> traceClosure(Logger logger, Throwable thrown) {
-        Objects.requireNonNull(logger, "No logger provided");
-        return new Consumer<T>() {
-            @Override
-            public void accept(T input) {
-                if (logger.isTraceEnabled()) {
-                    String msg = String.valueOf(input);
-                    if (thrown == null) {
-                        logger.trace(msg);
-                    } else {
-                        logger.trace(msg, thrown);
-                    }
-                }
-            }
-
-            @Override
-            public String toString() {
-                return "TRACE";
-            }
-        };
-    }
-}

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

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/net/NetworkConnector.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/net/NetworkConnector.java b/sshd-core/src/main/java/org/apache/sshd/common/util/net/NetworkConnector.java
deleted file mode 100644
index df7683a..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/net/NetworkConnector.java
+++ /dev/null
@@ -1,90 +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.net;
-
-import java.util.concurrent.TimeUnit;
-
-import org.apache.sshd.common.util.logging.AbstractLoggingBean;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class NetworkConnector extends AbstractLoggingBean {
-    public static final String DEFAULT_HOST = SshdSocketAddress.LOCALHOST_IPV4;
-    public static final long DEFAULT_CONNECT_TIMEOUT = TimeUnit.SECONDS.toMillis(5L);
-    public static final long DEFAULT_READ_TIMEOUT = TimeUnit.SECONDS.toMillis(15L);
-
-    private String protocol;
-    private String host = DEFAULT_HOST;
-    private int port;
-    private long connectTimeout = DEFAULT_CONNECT_TIMEOUT;
-    private long readTimeout = DEFAULT_READ_TIMEOUT;
-
-    public NetworkConnector() {
-        super();
-    }
-
-    public String getProtocol() {
-        return protocol;
-    }
-
-    public void setProtocol(String protocol) {
-        this.protocol = protocol;
-    }
-
-    public String getHost() {
-        return host;
-    }
-
-    public void setHost(String host) {
-        this.host = host;
-    }
-
-    public int getPort() {
-        return port;
-    }
-
-    public void setPort(int port) {
-        this.port = port;
-    }
-
-    public long getConnectTimeout() {
-        return connectTimeout;
-    }
-
-    public void setConnectTimeout(long connectTimeout) {
-        this.connectTimeout = connectTimeout;
-    }
-
-    public long getReadTimeout() {
-        return readTimeout;
-    }
-
-    public void setReadTimeout(long readTimeout) {
-        this.readTimeout = readTimeout;
-    }
-
-    @Override
-    public String toString() {
-        return getProtocol() + "://" + getHost() + ":" + getPort()
-             + ";connect=" + getConnectTimeout()
-             + ";read=" + getReadTimeout();
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/net/SshdSocketAddress.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/net/SshdSocketAddress.java b/sshd-core/src/main/java/org/apache/sshd/common/util/net/SshdSocketAddress.java
deleted file mode 100644
index b465d14..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/net/SshdSocketAddress.java
+++ /dev/null
@@ -1,639 +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.net;
-
-import java.net.Inet4Address;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.NetworkInterface;
-import java.net.SocketAddress;
-import java.net.SocketException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Enumeration;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Objects;
-import java.util.Set;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.NumberUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-
-/**
- * <P>A simple socket address holding the host name and port number. The reason
- * it does not extend {@link InetSocketAddress} is twofold:</P>
- * <OL>
- * <LI><P>
- * The {@link InetSocketAddress} performs a DNS resolution on the
- * provided host name - which we don't want do use until we want to
- * create a connection using this address (thus the {@link #toInetSocketAddress()}
- * call which executes this query
- * </P></LI>
- *
- * <LI><P>
- * If empty host name is provided we replace it with the <I>any</I>
- * address of 0.0.0.0
- * </P></LI>
- * </OL>
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class SshdSocketAddress extends SocketAddress {
-    public static final String LOCALHOST_NAME = "localhost";
-    public static final String LOCALHOST_IPV4 = "127.0.0.1";
-    public static final String IPV4_ANYADDR = "0.0.0.0";
-
-    public static final Set<String> WELL_KNOWN_IPV4_ADDRESSES =
-        Collections.unmodifiableSet(new LinkedHashSet<>(Arrays.asList(LOCALHOST_IPV4, IPV4_ANYADDR)));
-
-    // 10.0.0.0 - 10.255.255.255
-    public static final String PRIVATE_CLASS_A_PREFIX = "10.";
-    // 172.16.0.0 - 172.31.255.255
-    public static final String PRIVATE_CLASS_B_PREFIX = "172.";
-    // 192.168.0.0 - 192.168.255.255
-    public static final String PRIVATE_CLASS_C_PREFIX = "192.168.";
-    // 100.64.0.0 - 100.127.255.255
-    public static final String CARRIER_GRADE_NAT_PREFIX = "100.";
-    // The IPv4 broadcast address
-    public static final String BROADCAST_ADDRESS = "255.255.255.255";
-
-    /** Max. number of hex groups (separated by &quot;:&quot;) in an IPV6 address */
-    public static final int IPV6_MAX_HEX_GROUPS = 8;
-
-    /** Max. hex digits in each IPv6 group */
-    public static final int IPV6_MAX_HEX_DIGITS_PER_GROUP = 4;
-
-    public static final String IPV6_LONG_ANY_ADDRESS = "0:0:0:0:0:0:0:0";
-    public static final String IPV6_SHORT_ANY_ADDRESS = "::";
-
-    public static final String IPV6_LONG_LOCALHOST = "0:0:0:0:0:0:0:1";
-    public static final String IPV6_SHORT_LOCALHOST = "::1";
-
-    public static final Set<String> WELL_KNOWN_IPV6_ADDRESSES =
-        Collections.unmodifiableSet(new LinkedHashSet<>(Arrays.asList(
-                IPV6_LONG_LOCALHOST, IPV6_SHORT_LOCALHOST,
-                IPV6_LONG_ANY_ADDRESS, IPV6_SHORT_ANY_ADDRESS)));
-
-    /**
-     * A dummy placeholder that can be used instead of {@code null}s
-     */
-    public static final SshdSocketAddress LOCALHOST_ADDRESS = new SshdSocketAddress(LOCALHOST_IPV4, 0);
-
-    /**
-     * Compares {@link InetAddress}-es according to their {@link InetAddress#getHostAddress()}
-     * value case <U>insensitive</U>
-     *
-     * @see #toAddressString(InetAddress)
-     */
-    public static final Comparator<InetAddress> BY_HOST_ADDRESS = (a1, a2) -> {
-        String n1 = GenericUtils.trimToEmpty(toAddressString(a1));
-        String n2 = GenericUtils.trimToEmpty(toAddressString(a2));
-        return String.CASE_INSENSITIVE_ORDER.compare(n1, n2);
-    };
-
-    /**
-     * Compares {@link SocketAddress}-es according to their host case <U>insensitive</U>
-     * and if equals, then according to their port value (if any)
-     *
-     * @see #toAddressString(SocketAddress)
-     * @see #toAddressPort(SocketAddress)
-     */
-    public static final Comparator<SocketAddress> BY_HOST_AND_PORT = (a1, a2) -> {
-        String n1 = GenericUtils.trimToEmpty(toAddressString(a1));
-        String n2 = GenericUtils.trimToEmpty(toAddressString(a2));
-        int nRes = String.CASE_INSENSITIVE_ORDER.compare(n1, n2);
-        if (nRes != 0) {
-            return nRes;
-        }
-
-        int p1 = toAddressPort(a1);
-        int p2 = toAddressPort(a2);
-        nRes = Integer.compare(p1, p2);
-        if (nRes != 0) {
-            return nRes;
-        }
-
-        return 0;
-    };
-
-    private static final long serialVersionUID = 6461645947151952729L;
-
-    private final String hostName;
-    private final int port;
-
-    public SshdSocketAddress(int port) {
-        this(IPV4_ANYADDR, port);
-    }
-
-    public SshdSocketAddress(InetSocketAddress addr) {
-        Objects.requireNonNull(addr, "No address provided");
-
-        String host = addr.getHostString();
-        hostName = GenericUtils.isEmpty(host) ? IPV4_ANYADDR : host;
-        port = addr.getPort();
-        ValidateUtils.checkTrue(port >= 0, "Port must be >= 0: %d", port);
-    }
-
-    public SshdSocketAddress(String hostName, int port) {
-        Objects.requireNonNull(hostName, "Host name may not be null");
-        this.hostName = GenericUtils.isEmpty(hostName) ? IPV4_ANYADDR : hostName;
-
-        ValidateUtils.checkTrue(port >= 0, "Port must be >= 0: %d", port);
-        this.port = port;
-    }
-
-    public String getHostName() {
-        return hostName;
-    }
-
-    public int getPort() {
-        return port;
-    }
-
-    public InetSocketAddress toInetSocketAddress() {
-        return new InetSocketAddress(getHostName(), getPort());
-    }
-
-    @Override
-    public String toString() {
-        return getHostName() + ":" + getPort();
-    }
-
-    protected boolean isEquivalent(SshdSocketAddress that) {
-        if (that == null) {
-            return false;
-        } else if (that == this) {
-            return true;
-        } else {
-            return (this.getPort() == that.getPort())
-                && (GenericUtils.safeCompare(this.getHostName(), that.getHostName(), false) == 0);
-        }
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (o == null) {
-            return false;
-        }
-        if (getClass() != o.getClass()) {
-            return false;
-        }
-        return isEquivalent((SshdSocketAddress) o);
-    }
-
-    @Override
-    public int hashCode() {
-        return GenericUtils.hashCode(getHostName(), Boolean.FALSE) + getPort();
-    }
-
-
-    /**
-     * Returns the first external network address assigned to this
-     * machine or null if one is not found.
-     * @return Inet4Address associated with an external interface
-     * DevNote:  We actually return InetAddress here, as Inet4Addresses are final and cannot be mocked.
-     */
-    public static InetAddress getFirstExternalNetwork4Address() {
-        List<? extends InetAddress> addresses = getExternalNetwork4Addresses();
-        return (GenericUtils.size(addresses) > 0) ? addresses.get(0) : null;
-    }
-
-    /**
-     * @return a {@link List} of local network addresses which are not multicast
-     * or localhost sorted according to {@link #BY_HOST_ADDRESS}
-     */
-    public static List<InetAddress> getExternalNetwork4Addresses() {
-        List<InetAddress> addresses = new ArrayList<>();
-        try {
-            for (Enumeration<NetworkInterface> nets = NetworkInterface.getNetworkInterfaces(); (nets != null) && nets.hasMoreElements();) {
-                NetworkInterface netint = nets.nextElement();
-                /* TODO - uncomment when 1.5 compatibility no longer required
-                if (!netint.isUp()) {
-                    continue;    // ignore non-running interfaces
-                }
-                */
-
-                for (Enumeration<InetAddress> inetAddresses = netint.getInetAddresses(); (inetAddresses != null) && inetAddresses.hasMoreElements();) {
-                    InetAddress inetAddress = inetAddresses.nextElement();
-                    if (isValidHostAddress(inetAddress)) {
-                        addresses.add(inetAddress);
-                    }
-                }
-            }
-        } catch (SocketException e) {
-            // swallow
-        }
-
-        if (GenericUtils.size(addresses) > 1) {
-            Collections.sort(addresses, BY_HOST_ADDRESS);
-        }
-
-        return addresses;
-    }
-
-    /**
-     * @param addr The {@link InetAddress} to be verified
-     * @return <P><code>true</code> if the address is:</P></BR>
-     * <UL>
-     *         <LI>Not {@code null}</LI>
-     *         <LI>An {@link Inet4Address}</LI>
-     *         <LI>Not link local</LI>
-     *         <LI>Not a multicast</LI>
-     *         <LI>Not a loopback</LI>
-     * </UL>
-     * @see InetAddress#isLinkLocalAddress()
-     * @see InetAddress#isMulticastAddress()
-     * @see InetAddress#isMulticastAddress()
-     */
-    public static boolean isValidHostAddress(InetAddress addr) {
-        if (addr == null) {
-            return false;
-        }
-
-        if (addr.isLinkLocalAddress()) {
-            return false;
-        }
-
-        if (addr.isMulticastAddress()) {
-            return false;
-        }
-
-        if (!(addr instanceof Inet4Address)) {
-            return false;   // TODO add support for IPv6 - see SSHD-746
-        }
-
-        return !isLoopback(addr);
-
-    }
-
-    /**
-     * @param addr The {@link InetAddress} to be considered
-     * @return <code>true</code> if the address is a loopback one.
-     * <B>Note:</B> if {@link InetAddress#isLoopbackAddress()}
-     * returns <code>false</code> the address <U>string</U> is checked
-     * @see #toAddressString(InetAddress)
-     * @see #isLoopback(String)
-     */
-    public static boolean isLoopback(InetAddress addr) {
-        if (addr == null) {
-            return false;
-        }
-
-        if (addr.isLoopbackAddress()) {
-            return true;
-        }
-
-        String ip = toAddressString(addr);
-        return isLoopback(ip);
-    }
-
-    /**
-     * @param ip IP value to be tested
-     * @return <code>true</code> if the IP is &quot;localhost&quot; or
-     * &quot;127.x.x.x&quot;.
-     */
-    public static boolean isLoopback(String ip) {
-        if (GenericUtils.isEmpty(ip)) {
-            return false;
-        }
-
-        if (LOCALHOST_NAME.equals(ip) || LOCALHOST_IPV4.equals(ip)) {
-            return true;
-        }
-
-        // TODO add support for IPv6 - see SSHD-746
-        String[] values = GenericUtils.split(ip, '.');
-        if (GenericUtils.length(values) != 4) {
-            return false;
-        }
-
-        for (int index = 0; index < values.length; index++) {
-            String val = values[index];
-            if (!isValidIPv4AddressComponent(val)) {
-                return false;
-            }
-
-            if (index == 0) {
-                int number = Integer.parseInt(val);
-                if (number != 127) {
-                    return false;
-                }
-            }
-        }
-
-        return true;
-    }
-
-    public static SshdSocketAddress toSshdSocketAddress(SocketAddress addr) {
-        if (addr == null) {
-            return null;
-        } else if (addr instanceof SshdSocketAddress) {
-            return (SshdSocketAddress) addr;
-        } else if (addr instanceof InetSocketAddress) {
-            InetSocketAddress isockAddress = (InetSocketAddress) addr;
-            return new SshdSocketAddress(isockAddress.getHostName(), isockAddress.getPort());
-        } else {
-            throw new UnsupportedOperationException("Cannot convert " + addr.getClass().getSimpleName()
-                    + "=" + addr + " to " + SshdSocketAddress.class.getSimpleName());
-        }
-    }
-
-    public static String toAddressString(SocketAddress addr) {
-        if (addr == null) {
-            return null;
-        } else if (addr instanceof InetSocketAddress) {
-            return ((InetSocketAddress) addr).getHostString();
-        } else if (addr instanceof SshdSocketAddress) {
-            return ((SshdSocketAddress) addr).getHostName();
-        } else {
-            return addr.toString();
-        }
-    }
-
-    /**
-     * Attempts to resolve the port value
-     *
-     * @param addr The {@link SocketAddress} to examine
-     * @return The associated port value - negative if failed to resolve
-     */
-    public static int toAddressPort(SocketAddress addr) {
-        if (addr instanceof InetSocketAddress) {
-            return ((InetSocketAddress) addr).getPort();
-        } else if (addr instanceof SshdSocketAddress) {
-            return ((SshdSocketAddress) addr).getPort();
-        } else {
-            return -1;
-        }
-    }
-
-    /**
-     * <P>Converts a {@code SocketAddress} into an {@link InetSocketAddress} if possible:</P></BR>
-     * <UL>
-     *      <LI>If already an {@link InetSocketAddress} then cast it as such</LI>
-     *      <LI>If an {@code SshdSocketAddress} then invoke {@link #toInetSocketAddress()}</LI>
-     *      <LI>Otherwise, throw an exception</LI>
-     * </UL>
-     *
-     * @param remoteAddress The {@link SocketAddress} - ignored if {@code null}
-     * @return The {@link InetSocketAddress} instance
-     * @throws ClassCastException if argument is not already an {@code InetSocketAddress}
-     * or a {@code SshdSocketAddress}
-     */
-    public static InetSocketAddress toInetSocketAddress(SocketAddress remoteAddress) {
-        if (remoteAddress == null) {
-            return null;
-        } else if (remoteAddress instanceof InetSocketAddress) {
-            return (InetSocketAddress) remoteAddress;
-        } else if (remoteAddress instanceof SshdSocketAddress) {
-            return ((SshdSocketAddress) remoteAddress).toInetSocketAddress();
-        } else {
-            throw new ClassCastException("Unknown remote address type: " + remoteAddress);
-        }
-    }
-
-    public static String toAddressString(InetAddress addr) {
-        String ip = (addr == null) ? null : addr.toString();
-        if (GenericUtils.isEmpty(ip)) {
-            return null;
-        } else {
-            return ip.replaceAll(".*/", "");
-        }
-    }
-
-    public static boolean isIPv4Address(String addr) {
-        addr = GenericUtils.trimToEmpty(addr);
-        if (GenericUtils.isEmpty(addr)) {
-            return false;
-        }
-
-        if (WELL_KNOWN_IPV4_ADDRESSES.contains(addr)) {
-            return true;
-        }
-
-        String[] comps = GenericUtils.split(addr, '.');
-        if (GenericUtils.length(comps) != 4) {
-            return false;
-        }
-
-        for (String c : comps) {
-            if (!isValidIPv4AddressComponent(c)) {
-                return false;
-            }
-        }
-
-        return true;
-    }
-
-    /**
-     * Checks if the address is one of the allocated private blocks
-     * @param addr The address string
-     * @return {@code true} if this is one of the allocated private
-     * blocks. <B>Note:</B> it assumes that the address string is
-     * indeed an IPv4 address
-     * @see #isIPv4Address(String)
-     * @see #PRIVATE_CLASS_A_PREFIX
-     * @see #PRIVATE_CLASS_B_PREFIX
-     * @see #PRIVATE_CLASS_C_PREFIX
-     * @see <A HREF="http://en.wikipedia.org/wiki/Private_network#Private_IPv4_address_spaces">Wiki page</A>
-     */
-    public static boolean isPrivateIPv4Address(String addr) {
-        if (GenericUtils.isEmpty(addr)) {
-            return false;
-        }
-
-        if (addr.startsWith(PRIVATE_CLASS_A_PREFIX) || addr.startsWith(PRIVATE_CLASS_C_PREFIX)) {
-            return true;
-        }
-
-        // for 172.x.x.x we need further checks
-        if (!addr.startsWith(PRIVATE_CLASS_B_PREFIX)) {
-            return false;
-        }
-
-        int nextCompPos = addr.indexOf('.', PRIVATE_CLASS_B_PREFIX.length());
-        if (nextCompPos <= PRIVATE_CLASS_B_PREFIX.length()) {
-            return false;
-        }
-
-        String value = addr.substring(PRIVATE_CLASS_B_PREFIX.length(), nextCompPos);
-        if (!isValidIPv4AddressComponent(value)) {
-            return false;
-        }
-
-        int v = Integer.parseInt(value);
-        return (v >= 16) && (v <= 31);
-    }
-
-    /**
-     * @param addr The address to be checked
-     * @return {@code true} if the address is in the 100.64.0.0/10 range
-     * @see <A HREF="http://tools.ietf.org/html/rfc6598">RFC6598</A>
-     */
-    public static boolean isCarrierGradeNatIPv4Address(String addr) {
-        if (GenericUtils.isEmpty(addr)) {
-            return false;
-        }
-
-        if (!addr.startsWith(CARRIER_GRADE_NAT_PREFIX)) {
-            return false;
-        }
-
-        int nextCompPos = addr.indexOf('.', CARRIER_GRADE_NAT_PREFIX.length());
-        if (nextCompPos <= CARRIER_GRADE_NAT_PREFIX.length()) {
-            return false;
-        }
-
-        String value = addr.substring(CARRIER_GRADE_NAT_PREFIX.length(), nextCompPos);
-        if (!isValidIPv4AddressComponent(value)) {
-            return false;
-        }
-
-        int v = Integer.parseInt(value);
-        return (v >= 64) && (v <= 127);
-    }
-
-    /**
-     * <P>Checks if the provided argument is a valid IPv4 address component:</P></BR>
-     * <UL>
-     *     <LI>Not {@code null}/empty</LI>
-     *     <LI>Has at most 3 <U>digits</U></LI>
-     *     <LI>Its value is &le; 255</LI>
-     * </UL>
-     * @param c The {@link CharSequence} to be validate
-     * @return {@code true} if valid IPv4 address component
-     */
-    public static boolean isValidIPv4AddressComponent(CharSequence c) {
-        if (GenericUtils.isEmpty(c) || (c.length() > 3)) {
-            return false;
-        }
-
-        char ch = c.charAt(0);
-        if ((ch < '0') || (ch > '9')) {
-            return false;
-        }
-
-        if (!NumberUtils.isIntegerNumber(c)) {
-            return false;
-        }
-
-        int v = Integer.parseInt(c.toString());
-        return (v >= 0) && (v <= 255);
-    }
-
-    // Based on org.apache.commons.validator.routines.InetAddressValidator#isValidInet6Address
-    public static boolean isIPv6Address(String address) {
-        address = GenericUtils.trimToEmpty(address);
-        if (GenericUtils.isEmpty(address)) {
-            return false;
-        }
-
-        if (WELL_KNOWN_IPV6_ADDRESSES.contains(address)) {
-            return true;
-        }
-
-        boolean containsCompressedZeroes = address.contains("::");
-        if (containsCompressedZeroes && (address.indexOf("::") != address.lastIndexOf("::"))) {
-            return false;
-        }
-
-        if (((address.indexOf(':') == 0) && (!address.startsWith("::")))
-                || (address.endsWith(":") && (!address.endsWith("::")))) {
-            return false;
-        }
-
-        String[] splitOctets = GenericUtils.split(address, ':');
-        List<String> octetList = new ArrayList<>(Arrays.asList(splitOctets));
-        if (containsCompressedZeroes) {
-            if (address.endsWith("::")) {
-                // String.split() drops ending empty segments
-                octetList.add("");
-            } else if (address.startsWith("::") && (!octetList.isEmpty())) {
-                octetList.remove(0);
-            }
-        }
-
-        int numOctests = octetList.size();
-        if (numOctests > IPV6_MAX_HEX_GROUPS) {
-            return false;
-        }
-
-        int validOctets = 0;
-        int emptyOctets = 0; // consecutive empty chunks
-        for (int index = 0; index < numOctests; index++) {
-            String octet = octetList.get(index);
-            int pos = octet.indexOf('%');   // is it a zone index
-            if (pos >= 0) {
-                // zone index must come last
-                if (index != (numOctests - 1)) {
-                    return false;
-                }
-
-                octet = (pos > 0) ? octet.substring(0, pos) : "";
-            }
-
-            int octetLength = octet.length();
-            if (octetLength == 0) {
-                emptyOctets++;
-                if (emptyOctets > 1) {
-                    return false;
-                }
-
-                validOctets++;
-                continue;
-            }
-
-            emptyOctets = 0;
-
-            // Is last chunk an IPv4 address?
-            if ((index == (numOctests - 1)) && (octet.indexOf('.') > 0)) {
-                if (!isIPv4Address(octet)) {
-                    return false;
-                }
-                validOctets += 2;
-                continue;
-            }
-
-            if (octetLength > IPV6_MAX_HEX_DIGITS_PER_GROUP) {
-                return false;
-            }
-
-            int octetInt = 0;
-            try {
-                octetInt = Integer.parseInt(octet, 16);
-            } catch (NumberFormatException e) {
-                return false;
-            }
-
-            if ((octetInt < 0) || (octetInt > 0x000ffff)) {
-                return false;
-            }
-
-            validOctets++;
-        }
-
-        if ((validOctets > IPV6_MAX_HEX_GROUPS)
-                || ((validOctets < IPV6_MAX_HEX_GROUPS) && (!containsCompressedZeroes))) {
-            return false;
-        }
-        return true;
-    }
-}


[41/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common utilities code from sshd-core into sshd-common (new artifact)

Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/mac/BaseMac.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/mac/BaseMac.java b/sshd-common/src/main/java/org/apache/sshd/common/mac/BaseMac.java
new file mode 100644
index 0000000..e1681d4
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/mac/BaseMac.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.mac;
+
+import javax.crypto.spec.SecretKeySpec;
+
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * Base class for <code>Mac</code> implementations based on the JCE provider.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class BaseMac implements Mac {
+
+    private final String algorithm;
+    private final int defbsize;
+    private final int bsize;
+    private final byte[] tmp;
+    private javax.crypto.Mac mac;
+    private String s;
+
+    public BaseMac(String algorithm, int bsize, int defbsize) {
+        this.algorithm = algorithm;
+        this.bsize = bsize;
+        this.defbsize = defbsize;
+        this.tmp = new byte[defbsize];
+    }
+
+    @Override
+    public final String getAlgorithm() {
+        return algorithm;
+    }
+
+    @Override
+    public final int getBlockSize() {
+        return bsize;
+    }
+
+    @Override
+    public final int getDefaultBlockSize() {
+        return defbsize;
+    }
+
+    @Override
+    public void init(byte[] key) throws Exception {
+        if (key.length > defbsize) {
+            byte[] tmp = new byte[defbsize];
+            System.arraycopy(key, 0, tmp, 0, defbsize);
+            key = tmp;
+        }
+
+        SecretKeySpec skey = new SecretKeySpec(key, algorithm);
+        mac = SecurityUtils.getMac(algorithm);
+        mac.init(skey);
+    }
+
+    @Override
+    public void updateUInt(long i) {
+        tmp[0] = (byte) (i >>> 24);
+        tmp[1] = (byte) (i >>> 16);
+        tmp[2] = (byte) (i >>> 8);
+        tmp[3] = (byte) i;
+        update(tmp, 0, 4);
+    }
+
+    @Override
+    public void update(byte buf[], int offset, int len) {
+        mac.update(buf, offset, len);
+    }
+
+    @Override
+    public void doFinal(byte[] buf, int offset) throws Exception {
+        int blockSize = getBlockSize();
+        int defaultSize = getDefaultBlockSize();
+        if (blockSize != defaultSize) {
+            mac.doFinal(tmp, 0);
+            System.arraycopy(tmp, 0, buf, offset, blockSize);
+        } else {
+            mac.doFinal(buf, offset);
+        }
+    }
+
+    @Override
+    public String toString() {
+        synchronized (this) {
+            if (s == null) {
+                s = getClass().getSimpleName() + "[" + getAlgorithm() + "] - "
+                    + " block=" + getBlockSize() + "/" + getDefaultBlockSize() + " bytes";
+            }
+        }
+
+        return s;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/mac/BuiltinMacs.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/mac/BuiltinMacs.java b/sshd-common/src/main/java/org/apache/sshd/common/mac/BuiltinMacs.java
new file mode 100644
index 0000000..5a4528d
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/mac/BuiltinMacs.java
@@ -0,0 +1,273 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.mac;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Map;
+import java.util.NavigableSet;
+import java.util.Objects;
+import java.util.Set;
+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.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * Provides easy access to the currently implemented macs
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public enum BuiltinMacs implements MacFactory {
+    hmacmd5(Constants.HMAC_MD5, "HmacMD5", 16, 16),
+    hmacmd596(Constants.HMAC_MD5_96, "HmacMD5", 12, 16),
+    hmacsha1(Constants.HMAC_SHA1, "HmacSHA1", 20, 20),
+    hmacsha196(Constants.HMAC_SHA1_96, "HmacSHA1", 12, 20),
+    /** See <A HREF="https://tools.ietf.org/html/rfc6668">RFC 6668</A> */
+    hmacsha256(Constants.HMAC_SHA2_256, "HmacSHA256", 32, 32),
+    /** See <A HREF="https://tools.ietf.org/html/rfc6668">RFC 6668</A> */
+    hmacsha512(Constants.HMAC_SHA2_512, "HmacSHA512", 64, 64);
+
+    public static final Set<BuiltinMacs> VALUES =
+            Collections.unmodifiableSet(EnumSet.allOf(BuiltinMacs.class));
+
+    private static final Map<String, MacFactory> EXTENSIONS =
+            new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+
+    private final String factoryName;
+    private final String algorithm;
+    private final int defbsize;
+    private final int bsize;
+
+    BuiltinMacs(String factoryName, String algorithm, int bsize, int defbsize) {
+        this.factoryName = factoryName;
+        this.algorithm = algorithm;
+        this.bsize = bsize;
+        this.defbsize = defbsize;
+    }
+
+    @Override
+    public Mac create() {
+        return new BaseMac(getAlgorithm(), getBlockSize(), getDefaultBlockSize());
+    }
+
+    @Override
+    public final String getName() {
+        return factoryName;
+    }
+
+    @Override
+    public final String getAlgorithm() {
+        return algorithm;
+    }
+
+    @Override
+    public final int getBlockSize() {
+        return bsize;
+    }
+
+    @Override
+    public final int getDefaultBlockSize() {
+        return defbsize;
+    }
+
+    @Override
+    public final boolean isSupported() {
+        return true;
+    }
+
+    @Override
+    public final String toString() {
+        return getName();
+    }
+
+    /**
+     * Registered a {@link NamedFactory} to be available besides the built-in
+     * ones when parsing configuration
+     *
+     * @param extension The factory to register
+     * @throws IllegalArgumentException if factory instance is {@code null},
+     * or overrides a built-in one or overrides another registered factory
+     * with the same name (case <U>insensitive</U>).
+     */
+    public static void registerExtension(MacFactory extension) {
+        String name = Objects.requireNonNull(extension, "No extension provided").getName();
+        ValidateUtils.checkTrue(fromFactoryName(name) == null, "Extension overrides built-in: %s", name);
+
+        synchronized (EXTENSIONS) {
+            ValidateUtils.checkTrue(!EXTENSIONS.containsKey(name), "Extension overrides existing: %s", name);
+            EXTENSIONS.put(name, extension);
+        }
+    }
+
+    /**
+     * @return A {@link NavigableSet} of the currently registered extensions, sorted
+     * according to the factory name (case <U>insensitive</U>)
+     */
+    public static NavigableSet<MacFactory> getRegisteredExtensions() {
+        synchronized (EXTENSIONS) {
+            return GenericUtils.asSortedSet(NamedResource.BY_NAME_COMPARATOR, EXTENSIONS.values());
+        }
+    }
+
+    /**
+     * Unregisters specified extension
+     *
+     * @param name The factory name - ignored if {@code null}/empty
+     * @return The registered extension - {@code null} if not found
+     */
+    public static MacFactory unregisterExtension(String name) {
+        if (GenericUtils.isEmpty(name)) {
+            return null;
+        }
+
+        synchronized (EXTENSIONS) {
+            return EXTENSIONS.remove(name);
+        }
+    }
+
+    /**
+     * @param s The {@link Enum}'s name - ignored if {@code null}/empty
+     * @return The matching {@link org.apache.sshd.common.mac.BuiltinMacs} whose {@link Enum#name()} matches
+     * (case <U>insensitive</U>) the provided argument - {@code null} if no match
+     */
+    public static BuiltinMacs fromString(String s) {
+        if (GenericUtils.isEmpty(s)) {
+            return null;
+        }
+
+        for (BuiltinMacs c : VALUES) {
+            if (s.equalsIgnoreCase(c.name())) {
+                return c;
+            }
+        }
+
+        return 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<Mac> factory) {
+        if (factory == null) {
+            return null;
+        } else {
+            return fromFactoryName(factory.getName());
+        }
+    }
+
+    /**
+     * @param name The factory name - ignored if {@code null}/empty
+     * @return The matching {@link BuiltinMacs} whose factory name matches
+     * (case <U>insensitive</U>) the provided name - {@code null} if no match
+     */
+    public static BuiltinMacs fromFactoryName(String name) {
+        return NamedResource.findByName(name, String.CASE_INSENSITIVE_ORDER, VALUES);
+    }
+
+    /**
+     * @param macs A comma-separated list of MACs' names - ignored
+     *             if {@code null}/empty
+     * @return A {@link ParseResult} containing the successfully parsed
+     * factories and the unknown ones. <B>Note:</B> it is up to caller to
+     * ensure that the lists do not contain duplicates
+     */
+    public static ParseResult parseMacsList(String macs) {
+        return parseMacsList(GenericUtils.split(macs, ','));
+    }
+
+    public static ParseResult parseMacsList(String... macs) {
+        return parseMacsList(GenericUtils.isEmpty((Object[]) macs) ? Collections.emptyList() : Arrays.asList(macs));
+    }
+
+    public static ParseResult parseMacsList(Collection<String> macs) {
+        if (GenericUtils.isEmpty(macs)) {
+            return ParseResult.EMPTY;
+        }
+
+        List<MacFactory> factories = new ArrayList<>(macs.size());
+        List<String> unknown = Collections.emptyList();
+        for (String name : macs) {
+            MacFactory m = resolveFactory(name);
+            if (m != null) {
+                factories.add(m);
+            } else {
+                // replace the (unmodifiable) empty list with a real one
+                if (unknown.isEmpty()) {
+                    unknown = new ArrayList<>();
+                }
+                unknown.add(name);
+            }
+        }
+
+        return new ParseResult(factories, unknown);
+    }
+
+    /**
+     * @param name The factory name
+     * @return The factory or {@code null} if it is neither a built-in one
+     * or a registered extension
+     */
+    public static MacFactory resolveFactory(String name) {
+        if (GenericUtils.isEmpty(name)) {
+            return null;
+        }
+
+        MacFactory m = fromFactoryName(name);
+        if (m != null) {
+            return m;
+        }
+
+        synchronized (EXTENSIONS) {
+            return EXTENSIONS.get(name);
+        }
+    }
+
+    public static final class ParseResult extends NamedFactoriesListParseResult<Mac, MacFactory> {
+        public static final ParseResult EMPTY = new ParseResult(Collections.emptyList(), Collections.emptyList());
+
+        public ParseResult(List<MacFactory> parsed, List<String> unsupported) {
+            super(parsed, unsupported);
+        }
+    }
+
+    public static final class Constants {
+        public static final String HMAC_MD5 = "hmac-md5";
+        public static final String HMAC_MD5_96 = "hmac-md5-96";
+        public static final String HMAC_SHA1 = "hmac-sha1";
+        public static final String HMAC_SHA1_96 = "hmac-sha1-96";
+        public static final String HMAC_SHA2_256 = "hmac-sha2-256";
+        public static final String HMAC_SHA2_512 = "hmac-sha2-512";
+
+        private Constants() {
+            throw new UnsupportedOperationException("No instance allowed");
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/mac/Mac.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/mac/Mac.java b/sshd-common/src/main/java/org/apache/sshd/common/mac/Mac.java
new file mode 100644
index 0000000..4b80447
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/mac/Mac.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.mac;
+
+import org.apache.sshd.common.util.NumberUtils;
+
+/**
+ * Message Authentication Code for use in SSH.
+ * It usually wraps a javax.crypto.Mac class.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface Mac extends MacInformation {
+    void init(byte[] key) throws Exception;
+
+    default void update(byte[] buf) {
+        update(buf, 0, NumberUtils.length(buf));
+    }
+
+    void update(byte[] buf, int start, int len);
+
+    void updateUInt(long foo);
+
+    default byte[] doFinal() throws Exception {
+        int blockSize = getBlockSize();
+        byte[] buf = new byte[blockSize];
+        doFinal(buf);
+        return buf;
+    }
+
+    default void doFinal(byte[] buf) throws Exception {
+        doFinal(buf, 0);
+    }
+
+    void doFinal(byte[] buf, int offset) throws Exception;
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/mac/MacFactory.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/mac/MacFactory.java b/sshd-common/src/main/java/org/apache/sshd/common/mac/MacFactory.java
new file mode 100644
index 0000000..4463600
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/mac/MacFactory.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.mac;
+
+import org.apache.sshd.common.BuiltinFactory;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+// CHECKSTYLE:OFF
+public interface MacFactory extends MacInformation, BuiltinFactory<Mac> {
+    // nothing extra
+}
+//CHECKSTYLE:ON

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/mac/MacInformation.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/mac/MacInformation.java b/sshd-common/src/main/java/org/apache/sshd/common/mac/MacInformation.java
new file mode 100644
index 0000000..583165f
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/mac/MacInformation.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.mac;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface MacInformation {
+    /**
+     * @return MAC algorithm name
+     */
+    String getAlgorithm();
+
+    /**
+     * @return MAC output block size in bytes - may be less than the default
+     * - e.g., MD5-96
+     */
+    int getBlockSize();
+
+    /**
+     * @return The &quot;natural&quot; MAC block size in bytes
+     */
+    int getDefaultBlockSize();
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/mac/package.html
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/mac/package.html b/sshd-common/src/main/java/org/apache/sshd/common/mac/package.html
new file mode 100644
index 0000000..52a90ca
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/mac/package.html
@@ -0,0 +1,25 @@
+<!--
+    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.
+-->
+<html>
+<head>
+</head>
+<body>
+
+<a href="{@docRoot}/org/apache/sshd/common/mac/Mac.html"><code>Mac</code></a> implementations.
+
+</body>
+</html>

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/random/AbstractRandom.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/random/AbstractRandom.java b/sshd-common/src/main/java/org/apache/sshd/common/random/AbstractRandom.java
new file mode 100644
index 0000000..2a1f825
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/random/AbstractRandom.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.random;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractRandom implements Random {
+    protected AbstractRandom() {
+        super();
+    }
+
+    @Override
+    public String toString() {
+        return getName();
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/random/AbstractRandomFactory.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/random/AbstractRandomFactory.java b/sshd-common/src/main/java/org/apache/sshd/common/random/AbstractRandomFactory.java
new file mode 100644
index 0000000..c1d7893
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/random/AbstractRandomFactory.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.random;
+
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractRandomFactory implements RandomFactory {
+    private final String name;
+
+    protected AbstractRandomFactory(String name) {
+        this.name = ValidateUtils.checkNotNullAndNotEmpty(name, "No name");
+    }
+
+    @Override
+    public final String getName() {
+        return name;
+    }
+
+    @Override
+    public String toString() {
+        return getName();
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/random/JceRandom.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/random/JceRandom.java b/sshd-common/src/main/java/org/apache/sshd/common/random/JceRandom.java
new file mode 100644
index 0000000..ba050e6
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/random/JceRandom.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.random;
+
+import java.security.SecureRandom;
+
+/**
+ * A <code>Random</code> implementation using the built-in {@link SecureRandom} PRNG.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class JceRandom extends AbstractRandom {
+    public static final String NAME = "JCE";
+
+    private byte[] tmp = new byte[16];
+    private final SecureRandom random = new SecureRandom();
+
+    public JceRandom() {
+        super();
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    public synchronized void fill(byte[] foo, int start, int len) {
+        if ((start == 0) && (len == foo.length)) {
+            random.nextBytes(foo);
+        } else {
+            if (len > tmp.length) {
+                tmp = new byte[len];
+            }
+            random.nextBytes(tmp);
+            System.arraycopy(tmp, 0, foo, start, len);
+        }
+    }
+
+    @Override
+    public synchronized int random(int n) {
+        return random.nextInt(n);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/random/JceRandomFactory.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/random/JceRandomFactory.java b/sshd-common/src/main/java/org/apache/sshd/common/random/JceRandomFactory.java
new file mode 100644
index 0000000..450a85e
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/random/JceRandomFactory.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.random;
+
+/**
+ * Named factory for the JCE <code>Random</code>
+ */
+public class JceRandomFactory extends AbstractRandomFactory {
+    public static final String NAME = "default";
+    public static final JceRandomFactory INSTANCE = new JceRandomFactory();
+
+    public JceRandomFactory() {
+        super(NAME);
+    }
+
+    @Override
+    public boolean isSupported() {
+        return true;
+    }
+
+    @Override
+    public Random create() {
+        return new JceRandom();
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/random/Random.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/random/Random.java b/sshd-common/src/main/java/org/apache/sshd/common/random/Random.java
new file mode 100644
index 0000000..6e597ef
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/random/Random.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.random;
+
+import org.apache.sshd.common.NamedResource;
+
+/**
+ * A pseudo random number generator.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface Random extends NamedResource {
+    /**
+     * Fill the buffer with random values
+     *
+     * @param bytes The bytes to fill
+     * @see #fill(byte[], int, int)
+     */
+    default void fill(byte[] bytes) {
+        fill(bytes, 0, bytes.length);
+    }
+
+    /**
+     * Fill part of bytes with random values.
+     *
+     * @param bytes byte array to be filled.
+     * @param start index to start filling at.
+     * @param len   length of segment to fill.
+     */
+    void fill(byte[] bytes, int start, int len);
+
+    /**
+     * Returns a pseudo-random uniformly distributed {@code int}
+     * in the half-open range [0, n).
+     *
+     * @param n The range upper limit
+     * @return The randomly selected value in the range
+     */
+    int random(int n);
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/random/RandomFactory.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/random/RandomFactory.java b/sshd-common/src/main/java/org/apache/sshd/common/random/RandomFactory.java
new file mode 100644
index 0000000..dded06f
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/random/RandomFactory.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.random;
+
+import org.apache.sshd.common.BuiltinFactory;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+// CHECKSTYLE:OFF
+public interface RandomFactory extends BuiltinFactory<Random> {
+    // nothing extra
+}
+//CHECKSTYLE:ON

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/random/SingletonRandomFactory.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/random/SingletonRandomFactory.java b/sshd-common/src/main/java/org/apache/sshd/common/random/SingletonRandomFactory.java
new file mode 100644
index 0000000..ce24112
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/random/SingletonRandomFactory.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.random;
+
+import java.util.Objects;
+
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.OptionalFeature;
+
+/**
+ * A random factory wrapper that uses a single random instance.
+ * The underlying random instance has to be thread safe.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class SingletonRandomFactory extends AbstractRandom implements RandomFactory {
+
+    private final NamedFactory<Random> factory;
+    private final Random random;
+
+    public SingletonRandomFactory(NamedFactory<Random> factory) {
+        this.factory = Objects.requireNonNull(factory, "No factory");
+        this.random = Objects.requireNonNull(factory.create(), "No random instance created");
+    }
+
+    @Override
+    public boolean isSupported() {
+        if (factory instanceof OptionalFeature) {
+            return ((OptionalFeature) factory).isSupported();
+        } else {
+            return true;
+        }
+    }
+
+    @Override
+    public void fill(byte[] bytes, int start, int len) {
+        random.fill(bytes, start, len);
+    }
+
+    @Override
+    public int random(int max) {
+        return random.random(max);
+    }
+
+    @Override
+    public String getName() {
+        return factory.getName();
+    }
+
+    @Override
+    public Random create() {
+        return this;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/random/package.html
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/random/package.html b/sshd-common/src/main/java/org/apache/sshd/common/random/package.html
new file mode 100644
index 0000000..0e94d7f
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/random/package.html
@@ -0,0 +1,25 @@
+<!--
+    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.
+-->
+<html>
+<head>
+</head>
+<body>
+
+<a href="{@docRoot}/org/apache/sshd/common/random/Random.html"><code>Random</code></a> implementations.
+
+</body>
+</html>

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/signature/AbstractSignature.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/signature/AbstractSignature.java b/sshd-common/src/main/java/org/apache/sshd/common/signature/AbstractSignature.java
new file mode 100644
index 0000000..ef06d15
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/signature/AbstractSignature.java
@@ -0,0 +1,149 @@
+/*
+ * 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.signature;
+
+import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SignatureException;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.Objects;
+
+import org.apache.sshd.common.util.NumberUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.buffer.BufferUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * Useful base class for {@link Signature} implementation
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractSignature implements Signature {
+    private java.security.Signature signatureInstance;
+    private final String algorithm;
+
+    protected AbstractSignature(String algorithm) {
+        this.algorithm = ValidateUtils.checkNotNullAndNotEmpty(algorithm, "No signature algorithm specified");
+    }
+
+    @Override
+    public final String getAlgorithm() {
+        return algorithm;
+    }
+
+    /**
+     * Initializes the internal signature instance
+     *
+     * @param algo The signature's algorithm
+     * @param forSigning If {@code true} then it is being initialized for signing,
+     * otherwise for verifying a signature
+     * @return The {@link java.security.Signature} instance
+     * @throws GeneralSecurityException if failed to initialize
+     */
+    protected java.security.Signature doInitSignature(String algo, boolean forSigning) throws GeneralSecurityException {
+        return SecurityUtils.getSignature(algo);
+    }
+
+    /**
+     * @return The current {@link java.security.Signature} instance
+     * - {@code null} if not initialized
+     * @see #doInitSignature(String, boolean)
+     */
+    protected java.security.Signature getSignature() {
+        return signatureInstance;
+    }
+
+    @Override
+    public byte[] sign() throws Exception {
+        java.security.Signature signature = Objects.requireNonNull(getSignature(), "Signature not initialized");
+        return signature.sign();
+    }
+
+    @Override
+    public void initVerifier(PublicKey key) throws Exception {
+        String algo = getAlgorithm();
+        signatureInstance = Objects.requireNonNull(doInitSignature(algo, false), "No signature instance create");
+        signatureInstance.initVerify(Objects.requireNonNull(key, "No public key provided"));
+    }
+
+    @Override
+    public void initSigner(PrivateKey key) throws Exception {
+        String algo = getAlgorithm();
+        signatureInstance = Objects.requireNonNull(doInitSignature(algo, true), "No signature instance create");
+        signatureInstance.initSign(Objects.requireNonNull(key, "No private key provided"));
+    }
+
+    @Override
+    public void update(byte[] hash, int off, int len) throws Exception {
+        java.security.Signature signature = Objects.requireNonNull(getSignature(), "Signature not initialized");
+        signature.update(hash, off, len);
+    }
+
+    /**
+     * Makes an attempt to detect if the signature is encoded or pure data
+     *
+     * @param sig The original signature
+     * @return A {@link SimpleImmutableEntry} where first value is the key type and second
+     * value is the data - {@code null} if not encoded
+     */
+    protected SimpleImmutableEntry<String, byte[]> extractEncodedSignature(byte[] sig) {
+        final int dataLen = NumberUtils.length(sig);
+        // if it is encoded then we must have at least 2 UINT32 values
+        if (dataLen < (2 * Integer.BYTES)) {
+            return null;
+        }
+
+        long keyTypeLen = BufferUtils.getUInt(sig, 0, dataLen);
+        // after the key type we MUST have data bytes
+        if (keyTypeLen >= (dataLen - Integer.BYTES)) {
+            return null;
+        }
+
+        int keyTypeStartPos = Integer.BYTES;
+        int keyTypeEndPos = keyTypeStartPos + (int) keyTypeLen;
+        int remainLen = dataLen - keyTypeEndPos;
+        // must have UINT32 with the data bytes length
+        if (remainLen < Integer.BYTES) {
+            return null;
+        }
+
+        long dataBytesLen = BufferUtils.getUInt(sig, keyTypeEndPos, remainLen);
+        // make sure reported number of bytes does not exceed available
+        if (dataBytesLen > (remainLen - Integer.BYTES)) {
+            return null;
+        }
+
+        String keyType = new String(sig, keyTypeStartPos, (int) keyTypeLen, StandardCharsets.UTF_8);
+        byte[] data = new byte[(int) dataBytesLen];
+        System.arraycopy(sig, keyTypeEndPos + Integer.BYTES, data, 0, (int) dataBytesLen);
+        return new SimpleImmutableEntry<>(keyType, data);
+    }
+
+    protected boolean doVerify(byte[] data) throws SignatureException {
+        java.security.Signature signature = Objects.requireNonNull(getSignature(), "Signature not initialized");
+        return signature.verify(data);
+    }
+
+    @Override
+    public String toString() {
+        return getClass().getSimpleName() + "[" + getAlgorithm() + "]";
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/signature/BuiltinSignatures.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/signature/BuiltinSignatures.java b/sshd-common/src/main/java/org/apache/sshd/common/signature/BuiltinSignatures.java
new file mode 100644
index 0000000..7d4e1e2
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/signature/BuiltinSignatures.java
@@ -0,0 +1,305 @@
+/*
+ * 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.signature;
+
+import java.security.spec.ECParameterSpec;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Map;
+import java.util.NavigableSet;
+import java.util.Objects;
+import java.util.Set;
+import java.util.TreeMap;
+
+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.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * Provides easy access to the currently implemented signatures
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public enum BuiltinSignatures implements SignatureFactory {
+    dsa(KeyPairProvider.SSH_DSS) {
+        @Override
+        public Signature create() {
+            return new SignatureDSA();
+        }
+    },
+    rsa(KeyPairProvider.SSH_RSA) {
+        @Override
+        public Signature create() {
+            return new SignatureRSA();
+        }
+    },
+    nistp256(KeyPairProvider.ECDSA_SHA2_NISTP256) {
+        @Override
+        public Signature create() {
+            return new SignatureECDSA.SignatureECDSA256();
+        }
+
+        @Override
+        public boolean isSupported() {
+            return SecurityUtils.isECCSupported();
+        }
+    },
+    nistp384(KeyPairProvider.ECDSA_SHA2_NISTP384) {
+        @Override
+        public Signature create() {
+            return new SignatureECDSA.SignatureECDSA384();
+        }
+
+        @Override
+        public boolean isSupported() {
+            return SecurityUtils.isECCSupported();
+        }
+    },
+    nistp521(KeyPairProvider.ECDSA_SHA2_NISTP521) {
+        @Override
+        public Signature create() {
+            return new SignatureECDSA.SignatureECDSA521();
+        }
+
+        @Override
+        public boolean isSupported() {
+            return SecurityUtils.isECCSupported();
+        }
+    },
+    ed25519(KeyPairProvider.SSH_ED25519) {
+        @Override
+        public Signature create() {
+            return SecurityUtils.getEDDSASigner();
+        }
+
+        @Override
+        public boolean isSupported() {
+            return SecurityUtils.isEDDSACurveSupported();
+        }
+    };
+
+    public static final Set<BuiltinSignatures> VALUES =
+            Collections.unmodifiableSet(EnumSet.allOf(BuiltinSignatures.class));
+
+    private static final Map<String, SignatureFactory> EXTENSIONS =
+            new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+
+    private final String factoryName;
+
+    BuiltinSignatures(String facName) {
+        factoryName = facName;
+    }
+
+    public static Signature getByCurveSize(ECParameterSpec params) {
+        int curveSize = ECCurves.getCurveSize(params);
+        if (curveSize <= 256) {
+            return nistp256.create();
+        } else if (curveSize <= 384) {
+            return nistp384.create();
+        } else {
+            return nistp521.create();
+        }
+    }
+
+    @Override
+    public final String getName() {
+        return factoryName;
+    }
+
+    @Override
+    public final String toString() {
+        return getName();
+    }
+
+    @Override
+    public boolean isSupported() {
+        return true;
+    }
+
+    /**
+     * Registered a {@link NamedFactory} to be available besides the built-in
+     * ones when parsing configuration
+     *
+     * @param extension The factory to register
+     * @throws IllegalArgumentException if factory instance is {@code null},
+     * or overrides a built-in one or overrides another registered factory
+     * with the same name (case <U>insensitive</U>).
+     */
+    public static void registerExtension(SignatureFactory extension) {
+        String name = Objects.requireNonNull(extension, "No extension provided").getName();
+        ValidateUtils.checkTrue(fromFactoryName(name) == null, "Extension overrides built-in: %s", name);
+
+        synchronized (EXTENSIONS) {
+            ValidateUtils.checkTrue(!EXTENSIONS.containsKey(name), "Extension overrides existing: %s", name);
+            EXTENSIONS.put(name, extension);
+        }
+    }
+
+    /**
+     * @return A {@link NavigableSet} of the currently registered extensions, sorted
+     * according to the factory name (case <U>insensitive</U>)
+     */
+    public static NavigableSet<SignatureFactory> getRegisteredExtensions() {
+        synchronized (EXTENSIONS) {
+            return GenericUtils.asSortedSet(NamedResource.BY_NAME_COMPARATOR, EXTENSIONS.values());
+        }
+    }
+
+    /**
+     * Unregisters specified extension
+     *
+     * @param name The factory name - ignored if {@code null}/empty
+     * @return The registered extension - {@code null} if not found
+     */
+    public static SignatureFactory unregisterExtension(String name) {
+        if (GenericUtils.isEmpty(name)) {
+            return null;
+        }
+
+        synchronized (EXTENSIONS) {
+            return EXTENSIONS.remove(name);
+        }
+    }
+
+    /**
+     * @param s The {@link Enum}'s name - ignored if {@code null}/empty
+     * @return The matching {@link org.apache.sshd.common.signature.BuiltinSignatures} whose {@link Enum#name()} matches
+     * (case <U>insensitive</U>) the provided argument - {@code null} if no match
+     */
+    public static BuiltinSignatures fromString(String s) {
+        if (GenericUtils.isEmpty(s)) {
+            return null;
+        }
+
+        for (BuiltinSignatures c : VALUES) {
+            if (s.equalsIgnoreCase(c.name())) {
+                return c;
+            }
+        }
+
+        return 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<Signature> factory) {
+        if (factory == null) {
+            return null;
+        } else {
+            return fromFactoryName(factory.getName());
+        }
+    }
+
+    /**
+     * @param name The factory name - ignored if {@code null}/empty
+     * @return The matching {@link BuiltinSignatures} whose factory name matches
+     * (case <U>insensitive</U>) the provided name - {@code null} if no match
+     */
+    public static BuiltinSignatures fromFactoryName(String name) {
+        return NamedResource.findByName(name, String.CASE_INSENSITIVE_ORDER, VALUES);
+    }
+
+    /**
+     * @param sigs A comma-separated list of signatures' names - ignored
+     *             if {@code null}/empty
+     * @return A {@link ParseResult} of all the {@link NamedFactory} whose
+     * name appears in the string and represent a built-in signature. Any
+     * unknown name is <U>ignored</U>. The order of the returned result
+     * is the same as the original order - bar the unknown signatures.
+     * <B>Note:</B> it is up to caller to ensure that the list does not
+     * contain duplicates
+     */
+    public static ParseResult parseSignatureList(String sigs) {
+        return parseSignatureList(GenericUtils.split(sigs, ','));
+    }
+
+    public static ParseResult parseSignatureList(String... sigs) {
+        return parseSignatureList(GenericUtils.isEmpty((Object[]) sigs) ? Collections.emptyList() : Arrays.asList(sigs));
+    }
+
+    public static ParseResult parseSignatureList(Collection<String> sigs) {
+        if (GenericUtils.isEmpty(sigs)) {
+            return ParseResult.EMPTY;
+        }
+
+        List<SignatureFactory> factories = new ArrayList<>(sigs.size());
+        List<String> unknown = Collections.emptyList();
+        for (String name : sigs) {
+            SignatureFactory s = resolveFactory(name);
+            if (s != null) {
+                factories.add(s);
+            } else {
+                // replace the (unmodifiable) empty list with a real one
+                if (unknown.isEmpty()) {
+                    unknown = new ArrayList<>();
+                }
+                unknown.add(name);
+            }
+        }
+
+        return new ParseResult(factories, unknown);
+    }
+
+    /**
+     * @param name The factory name
+     * @return The factory or {@code null} if it is neither a built-in one
+     * or a registered extension
+     */
+    public static SignatureFactory resolveFactory(String name) {
+        if (GenericUtils.isEmpty(name)) {
+            return null;
+        }
+
+        SignatureFactory s = fromFactoryName(name);
+        if (s != null) {
+            return s;
+        }
+
+        synchronized (EXTENSIONS) {
+            return EXTENSIONS.get(name);
+        }
+    }
+
+    /**
+     * Holds the result of the {@link BuiltinSignatures#parseSignatureList(String)}
+     *
+     * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+     */
+    public static final class ParseResult extends NamedFactoriesListParseResult<Signature, SignatureFactory> {
+        public static final ParseResult EMPTY = new ParseResult(Collections.emptyList(), Collections.emptyList());
+
+        public ParseResult(List<SignatureFactory> parsed, List<String> unsupported) {
+            super(parsed, unsupported);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/signature/Signature.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/signature/Signature.java b/sshd-common/src/main/java/org/apache/sshd/common/signature/Signature.java
new file mode 100644
index 0000000..fd88a1d
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/signature/Signature.java
@@ -0,0 +1,88 @@
+/*
+ * 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.signature;
+
+import java.security.PrivateKey;
+import java.security.PublicKey;
+
+import org.apache.sshd.common.util.NumberUtils;
+
+/**
+ * Signature interface for SSH used to sign or verify packets
+ * Usually wraps a javax.crypto.Signature object
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface Signature {
+    /**
+     * @return The signature algorithm name
+     */
+    String getAlgorithm();
+
+    /**
+     * @param key The {@link PublicKey} to be used for verifying signatures
+     * @throws Exception If failed to initialize
+     */
+    void initVerifier(PublicKey key) throws Exception;
+
+    /**
+     * @param key The {@link PrivateKey} to be used for signing
+     * @throws Exception If failed to initialize
+     */
+    void initSigner(PrivateKey key) throws Exception;
+
+    /**
+     * Update the computed signature with the given data
+     *
+     * @param hash The hash data buffer
+     * @throws Exception If failed to update
+     * @see #update(byte[], int, int)
+     */
+    default void update(byte[] hash) throws Exception {
+        update(hash, 0, NumberUtils.length(hash));
+    }
+
+    /**
+     * Update the computed signature with the given data
+     *
+     * @param hash The hash data buffer
+     * @param off  Offset of hash data in buffer
+     * @param len  Length of hash data
+     * @throws Exception If failed to update
+     */
+    void update(byte[] hash, int off, int len) throws Exception;
+
+    /**
+     * Verify against the given signature
+     *
+     * @param sig The signed data
+     * @return {@code true} if signature is valid
+     * @throws Exception If failed to extract signed data for validation
+     */
+    boolean verify(byte[] sig) throws Exception;
+
+    /**
+     * Compute the signature
+     *
+     * @return The signature value
+     * @throws Exception If failed to calculate the signature
+     */
+    byte[] sign() throws Exception;
+
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureDSA.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureDSA.java b/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureDSA.java
new file mode 100644
index 0000000..1f552bd
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureDSA.java
@@ -0,0 +1,142 @@
+/*
+ * 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.signature;
+
+import java.io.StreamCorruptedException;
+import java.math.BigInteger;
+import java.security.SignatureException;
+import java.util.Map;
+
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.NumberUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.buffer.BufferUtils;
+import org.apache.sshd.common.util.io.der.DERParser;
+import org.apache.sshd.common.util.io.der.DERWriter;
+
+
+/**
+ * DSA <code>Signature</code>
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ * @see <A HREF="https://tools.ietf.org/html/rfc4253#section-6.6">RFC4253 section 6.6</A>
+ */
+public class SignatureDSA extends AbstractSignature {
+    public static final String DEFAULT_ALGORITHM = "SHA1withDSA";
+
+    public static final int DSA_SIGNATURE_LENGTH = 40;
+    // result must be 40 bytes, but length of r and s may not exceed 20 bytes
+    public static final int MAX_SIGNATURE_VALUE_LENGTH = DSA_SIGNATURE_LENGTH / 2;
+
+    public SignatureDSA() {
+        this(DEFAULT_ALGORITHM);
+    }
+
+    protected SignatureDSA(String algorithm) {
+        super(algorithm);
+    }
+
+    @Override
+    public byte[] sign() throws Exception {
+        byte[] sig = super.sign();
+
+        try (DERParser parser = new DERParser(sig)) {
+            int type = parser.read();
+            if (type != 0x30) {
+                throw new StreamCorruptedException("Invalid signature format - not a DER SEQUENCE: 0x" + Integer.toHexString(type));
+            }
+
+            // length of remaining encoding of the 2 integers
+            int remainLen = parser.readLength();
+            /*
+             * There are supposed to be 2 INTEGERs, each encoded with:
+             *
+             *  - one byte representing the fact that it is an INTEGER
+             *  - one byte of the integer encoding length
+             *  - at least one byte of integer data (zero length is not an option)
+             */
+            if (remainLen < (2 * 3)) {
+                throw new StreamCorruptedException("Invalid signature format - not enough encoded data length: " + remainLen);
+            }
+
+            BigInteger r = parser.readBigInteger();
+            BigInteger s = parser.readBigInteger();
+
+            byte[] result = new byte[DSA_SIGNATURE_LENGTH];
+            putBigInteger(r, result, 0);
+            putBigInteger(s, result, MAX_SIGNATURE_VALUE_LENGTH);
+            return result;
+        }
+    }
+
+    public static void putBigInteger(BigInteger value, byte[] result, int offset) {
+        byte[] data = value.toByteArray();
+        boolean maxExceeded = data.length > MAX_SIGNATURE_VALUE_LENGTH;
+        int dstOffset = maxExceeded ? 0 : (MAX_SIGNATURE_VALUE_LENGTH - data.length);
+        System.arraycopy(data, maxExceeded ? 1 : 0,
+                result, offset + dstOffset,
+                Math.min(MAX_SIGNATURE_VALUE_LENGTH, data.length));
+    }
+
+    @Override
+    public boolean verify(byte[] sig) throws Exception {
+        int sigLen = NumberUtils.length(sig);
+        byte[] data = sig;
+
+        if (sigLen != DSA_SIGNATURE_LENGTH) {
+            // probably some encoded data
+            Map.Entry<String, byte[]> encoding = extractEncodedSignature(sig);
+            if (encoding != null) {
+                String keyType = encoding.getKey();
+                ValidateUtils.checkTrue(KeyPairProvider.SSH_DSS.equals(keyType), "Mismatched key type: %s", keyType);
+                data = encoding.getValue();
+                sigLen = NumberUtils.length(data);
+            }
+        }
+
+        if (sigLen != DSA_SIGNATURE_LENGTH) {
+            throw new SignatureException("Bad signature length (" + sigLen + " instead of " + DSA_SIGNATURE_LENGTH + ")"
+                    + " for " + BufferUtils.toHex(':', data));
+        }
+
+        byte[] rEncoding;
+        try (DERWriter w = new DERWriter(MAX_SIGNATURE_VALUE_LENGTH + 4)) {     // in case length > 0x7F
+            w.writeBigInteger(data, 0, MAX_SIGNATURE_VALUE_LENGTH);
+            rEncoding = w.toByteArray();
+        }
+
+        byte[] sEncoding;
+        try (DERWriter w = new DERWriter(MAX_SIGNATURE_VALUE_LENGTH + 4)) {     // in case length > 0x7F
+            w.writeBigInteger(data, MAX_SIGNATURE_VALUE_LENGTH, MAX_SIGNATURE_VALUE_LENGTH);
+            sEncoding = w.toByteArray();
+        }
+
+        int length = rEncoding.length + sEncoding.length;
+        byte[] encoded;
+        try (DERWriter w = new DERWriter(1 + length + 4)) {  // in case length > 0x7F
+            w.write(0x30); // SEQUENCE
+            w.writeLength(length);
+            w.write(rEncoding);
+            w.write(sEncoding);
+            encoded = w.toByteArray();
+        }
+
+        return doVerify(encoded);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureECDSA.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureECDSA.java b/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureECDSA.java
new file mode 100644
index 0000000..56964d3
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureECDSA.java
@@ -0,0 +1,144 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.signature;
+
+import java.io.StreamCorruptedException;
+import java.math.BigInteger;
+import java.util.Map;
+
+import org.apache.sshd.common.cipher.ECCurves;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
+import org.apache.sshd.common.util.io.der.DERParser;
+import org.apache.sshd.common.util.io.der.DERWriter;
+
+/**
+ * Signature algorithm for EC keys using ECDSA.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ * @see <A HREF="http://tools.ietf.org/html/rfc3278#section-8.2">RFC3278 section 8.2</A>
+ */
+public class SignatureECDSA extends AbstractSignature {
+    public static class SignatureECDSA256 extends SignatureECDSA {
+        public static final String DEFAULT_ALGORITHM = "SHA256withECDSA";
+
+        public SignatureECDSA256() {
+            super(DEFAULT_ALGORITHM);
+        }
+    }
+
+    public static class SignatureECDSA384 extends SignatureECDSA {
+        public static final String DEFAULT_ALGORITHM = "SHA384withECDSA";
+
+        public SignatureECDSA384() {
+            super(DEFAULT_ALGORITHM);
+        }
+    }
+
+    public static class SignatureECDSA521 extends SignatureECDSA {
+        public static final String DEFAULT_ALGORITHM = "SHA512withECDSA";
+
+        public SignatureECDSA521() {
+            super(DEFAULT_ALGORITHM);
+        }
+    }
+
+    protected SignatureECDSA(String algo) {
+        super(algo);
+    }
+
+    @Override
+    public byte[] sign() throws Exception {
+        byte[] sig = super.sign();
+
+        try (DERParser parser = new DERParser(sig)) {
+            int type = parser.read();
+            if (type != 0x30) {
+                throw new StreamCorruptedException("Invalid signature format - not a DER SEQUENCE: 0x" + Integer.toHexString(type));
+            }
+
+            // length of remaining encoding of the 2 integers
+            int remainLen = parser.readLength();
+            /*
+             * There are supposed to be 2 INTEGERs, each encoded with:
+             *
+             *  - one byte representing the fact that it is an INTEGER
+             *  - one byte of the integer encoding length
+             *  - at least one byte of integer data (zero length is not an option)
+             */
+            if (remainLen < (2 * 3)) {
+                throw new StreamCorruptedException("Invalid signature format - not enough encoded data length: " + remainLen);
+            }
+
+            BigInteger r = parser.readBigInteger();
+            BigInteger s = parser.readBigInteger();
+            // Write the <r,s> to its own types writer.
+            Buffer rsBuf = new ByteArrayBuffer();
+            rsBuf.putMPInt(r);
+            rsBuf.putMPInt(s);
+
+            return rsBuf.getCompactData();
+        }
+    }
+
+    @Override
+    public boolean verify(byte[] sig) throws Exception {
+        byte[] data = sig;
+        Map.Entry<String, byte[]> encoding = extractEncodedSignature(data);
+        if (encoding != null) {
+            String keyType = encoding.getKey();
+            ECCurves curve = ECCurves.fromKeyType(keyType);
+            ValidateUtils.checkNotNull(curve, "Unknown curve type: %s", keyType);
+            data = encoding.getValue();
+        }
+
+        Buffer rsBuf = new ByteArrayBuffer(data);
+        byte[] rArray = rsBuf.getMPIntAsBytes();
+        byte[] rEncoding;
+        try (DERWriter w = new DERWriter(rArray.length + 4)) {     // in case length > 0x7F
+            w.writeBigInteger(rArray);
+            rEncoding = w.toByteArray();
+        }
+
+        byte[] sArray = rsBuf.getMPIntAsBytes();
+        byte[] sEncoding;
+        try (DERWriter w = new DERWriter(sArray.length + 4)) {     // in case length > 0x7F
+            w.writeBigInteger(sArray);
+            sEncoding = w.toByteArray();
+        }
+
+        int remaining = rsBuf.available();
+        if (remaining != 0) {
+            throw new StreamCorruptedException("Signature had padding - remaining=" + remaining);
+        }
+
+        int length = rEncoding.length + sEncoding.length;
+        byte[] encoded;
+        try (DERWriter w = new DERWriter(1 + length + 4)) {  // in case length > 0x7F
+            w.write(0x30); // SEQUENCE
+            w.writeLength(length);
+            w.write(rEncoding);
+            w.write(sEncoding);
+            encoded = w.toByteArray();
+        }
+
+        return doVerify(encoded);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureFactoriesManager.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureFactoriesManager.java b/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureFactoriesManager.java
new file mode 100644
index 0000000..c9d876a
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureFactoriesManager.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.signature;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+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.ValidateUtils;
+
+/**
+ * Manage the list of named factories for <code>Signature</code>.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface SignatureFactoriesManager {
+    /**
+     * @return The list of named <code>Signature</code> factories
+     */
+    List<NamedFactory<Signature>> getSignatureFactories();
+
+    default String getSignatureFactoriesNameList() {
+        return NamedResource.getNames(getSignatureFactories());
+    }
+
+    default List<String> getSignatureFactoriesNames() {
+        return NamedResource.getNameList(getSignatureFactories());
+    }
+
+    void setSignatureFactories(List<NamedFactory<Signature>> factories);
+
+    default void setSignatureFactoriesNameList(String names) {
+        setSignatureFactoriesNames(GenericUtils.split(names, ','));
+    }
+
+    default void setSignatureFactoriesNames(String... names) {
+        setSignatureFactoriesNames(GenericUtils.isEmpty((Object[]) names) ? Collections.emptyList() : Arrays.asList(names));
+    }
+
+    default void setSignatureFactoriesNames(Collection<String> names) {
+        BuiltinSignatures.ParseResult result = BuiltinSignatures.parseSignatureList(names);
+        @SuppressWarnings({ "rawtypes", "unchecked" })
+        List<NamedFactory<Signature>> factories =
+                (List) ValidateUtils.checkNotNullAndNotEmpty(result.getParsedFactories(), "No supported signature factories: %s", names);
+        Collection<String> unsupported = result.getUnsupportedFactories();
+        ValidateUtils.checkTrue(GenericUtils.isEmpty(unsupported), "Unsupported signature factories found: %s", unsupported);
+        setSignatureFactories(factories);
+    }
+
+    /**
+     * Attempts to use the primary manager's signature factories if not {@code null}/empty,
+     * otherwise uses the secondary ones (regardless of whether there are any...)
+     *
+     * @param primary The primary {@link SignatureFactoriesManager}
+     * @param secondary The secondary {@link SignatureFactoriesManager}
+     * @return The resolved signature factories - may be {@code null}/empty
+     * @see #getSignatureFactories(SignatureFactoriesManager)
+     */
+    static List<NamedFactory<Signature>> resolveSignatureFactories(
+            SignatureFactoriesManager primary, SignatureFactoriesManager secondary) {
+        List<NamedFactory<Signature>> factories = getSignatureFactories(primary);
+        return GenericUtils.isEmpty(factories) ? getSignatureFactories(secondary) : factories;
+    }
+
+    /**
+     * @param manager The {@link SignatureFactoriesManager} instance - ignored if {@code null}
+     * @return The associated list of named <code>Signature</code> factories or {@code null} if
+     * no manager instance
+     */
+    static List<NamedFactory<Signature>> getSignatureFactories(SignatureFactoriesManager manager) {
+        return (manager == null) ? null : manager.getSignatureFactories();
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureFactory.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureFactory.java b/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureFactory.java
new file mode 100644
index 0000000..0881714
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureFactory.java
@@ -0,0 +1,31 @@
+/*
+ * 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.signature;
+
+import org.apache.sshd.common.BuiltinFactory;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+// CHECKSTYLE:OFF
+public interface SignatureFactory extends BuiltinFactory<Signature> {
+    // nothing extra
+}
+//CHECKSTYLE:ON

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureRSA.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureRSA.java b/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureRSA.java
new file mode 100644
index 0000000..ad1d37e
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureRSA.java
@@ -0,0 +1,89 @@
+/*
+ * 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.signature;
+
+import java.math.BigInteger;
+import java.security.PublicKey;
+import java.security.interfaces.RSAKey;
+import java.util.Map;
+
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * RSA <code>Signature</code>
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ * @see <A HREF="https://tools.ietf.org/html/rfc4253#section-6.6">RFC4253 section 6.6</A>
+ */
+public class SignatureRSA extends AbstractSignature {
+    public static final String DEFAULT_ALGORITHM = "SHA1withRSA";
+
+    private int verifierSignatureSize = -1;
+
+    public SignatureRSA() {
+        super(DEFAULT_ALGORITHM);
+    }
+
+    protected SignatureRSA(String algorithm) {
+        super(algorithm);
+    }
+
+    /**
+     * @return The expected number of bytes in the signature - non-positive
+     * if not initialized or not intended to be used for verification
+     */
+    protected int getVerifierSignatureSize() {
+        return verifierSignatureSize;
+    }
+
+    @Override
+    public void initVerifier(PublicKey key) throws Exception {
+        super.initVerifier(key);
+        RSAKey rsaKey = ValidateUtils.checkInstanceOf(key, RSAKey.class, "Not an RSA key");
+        verifierSignatureSize = getVerifierSignatureSize(rsaKey);
+    }
+
+    public static int getVerifierSignatureSize(RSAKey key) {
+        BigInteger modulus = key.getModulus();
+        return (modulus.bitLength() + Byte.SIZE - 1) / Byte.SIZE;
+    }
+
+    @Override
+    public boolean verify(byte[] sig) throws Exception {
+        byte[] data = sig;
+        Map.Entry<String, byte[]> encoding = extractEncodedSignature(data);
+        if (encoding != null) {
+            String keyType = encoding.getKey();
+            ValidateUtils.checkTrue(KeyPairProvider.SSH_RSA.equals(keyType), "Mismatched key type: %s", keyType);
+            data = encoding.getValue();
+        }
+
+        int expectedSize = getVerifierSignatureSize();
+        ValidateUtils.checkTrue(expectedSize > 0, "Signature verification size has not been initialized");
+        // Pad with zero if value is trimmed
+        if (data.length < expectedSize) {
+            byte[] pad = new byte[expectedSize];
+            System.arraycopy(data, 0, pad, pad.length - data.length, data.length);
+            data = pad;
+        }
+
+        return doVerify(data);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/signature/package.html
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/signature/package.html b/sshd-common/src/main/java/org/apache/sshd/common/signature/package.html
new file mode 100644
index 0000000..d1b16e4
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/signature/package.html
@@ -0,0 +1,25 @@
+<!--
+    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.
+-->
+<html>
+<head>
+</head>
+<body>
+
+<a href="{@docRoot}/org/apache/sshd/common/signature/Signature.html"><code>Signature</code></a> implementations.
+
+</body>
+</html>


[33/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common utilities code from sshd-core into sshd-common (new artifact)

Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/client/config/hosts/HostConfigEntryTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/client/config/hosts/HostConfigEntryTest.java b/sshd-common/src/test/java/org/apache/sshd/client/config/hosts/HostConfigEntryTest.java
new file mode 100644
index 0000000..0c8f43c
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/client/config/hosts/HostConfigEntryTest.java
@@ -0,0 +1,325 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.client.config.hosts;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class HostConfigEntryTest extends JUnitTestSupport {
+    public HostConfigEntryTest() {
+        super();
+    }
+
+    @Test
+    public void testNegatingPatternOverridesAll() {
+        String testHost = "37.77.34.7";
+        String[] elements = GenericUtils.split(testHost, '.');
+        StringBuilder sb = new StringBuilder(testHost.length() + Byte.SIZE);
+        List<HostPatternValue> patterns = new ArrayList<>(elements.length + 1);
+        // all wildcard patterns are not negated - only the actual host
+        patterns.add(HostPatternsHolder.toPattern(Character.toString(HostPatternsHolder.NEGATION_CHAR_PATTERN) + testHost));
+
+        for (int i = 0; i < elements.length; i++) {
+            sb.setLength(0);
+
+            for (int j = 0; j < elements.length; j++) {
+                if (j > 0) {
+                    sb.append('.');
+                }
+                if (i == j) {
+                    sb.append(HostPatternsHolder.WILDCARD_PATTERN);
+                } else {
+                    sb.append(elements[j]);
+                }
+            }
+
+            patterns.add(HostPatternsHolder.toPattern(sb));
+        }
+
+        for (int index = 0; index < patterns.size(); index++) {
+            assertFalse("Unexpected match for " + patterns, HostPatternsHolder.isHostMatch(testHost, 0, patterns));
+            Collections.shuffle(patterns);
+        }
+    }
+
+    @Test
+    public void testHostWildcardPatternMatching() {
+        String pkgName = getClass().getPackage().getName();
+        String[] elements = GenericUtils.split(pkgName, '.');
+        StringBuilder sb = new StringBuilder(pkgName.length() + Long.SIZE + 1).append(HostPatternsHolder.WILDCARD_PATTERN);
+        for (int index = elements.length - 1; index >= 0; index--) {
+            sb.append('.').append(elements[index]);
+        }
+
+        String value = sb.toString();
+        HostPatternValue pp = HostPatternsHolder.toPattern(value);
+        Pattern pattern = pp.getPattern();
+        String domain = value.substring(1); // chomp the wildcard prefix
+        for (String host : new String[] {
+                getClass().getSimpleName(),
+                getCurrentTestName(),
+                getClass().getSimpleName() + "-" + getCurrentTestName(),
+                getClass().getSimpleName() + "." + getCurrentTestName(),
+        }) {
+            sb.setLength(0); // start from scratch
+            sb.append(host).append(domain);
+
+            testCaseInsensitivePatternMatching(sb.toString(), pattern, true);
+        }
+    }
+
+    @Test
+    public void testIPAddressWildcardPatternMatching() {
+        StringBuilder sb = new StringBuilder().append("10.0.0.");
+        int sbLen = sb.length();
+
+        Pattern pattern = HostPatternsHolder.toPattern(sb.append(HostPatternsHolder.WILDCARD_PATTERN)).getPattern();
+        for (int v = 0; v <= 255; v++) {
+            sb.setLength(sbLen);    // start from scratch
+            sb.append(v);
+
+            String address = sb.toString();
+            assertTrue("No match for " + address, HostPatternsHolder.isHostMatch(address, pattern));
+        }
+    }
+
+    @Test
+    public void testHostSingleCharPatternMatching() {
+        String value = getCurrentTestName();
+        StringBuilder sb = new StringBuilder(value);
+        for (boolean restoreOriginal : new boolean[] {true, false}) {
+            for (int index = 0; index < value.length(); index++) {
+                sb.setCharAt(index, HostPatternsHolder.SINGLE_CHAR_PATTERN);
+                testCaseInsensitivePatternMatching(value, HostPatternsHolder.toPattern(sb.toString()).getPattern(), true);
+                if (restoreOriginal) {
+                    sb.setCharAt(index, value.charAt(index));
+                }
+            }
+        }
+    }
+
+    @Test
+    public void testIPAddressSingleCharPatternMatching() {
+        StringBuilder sb = new StringBuilder().append("10.0.0.");
+        int sbLen = sb.length();
+
+        for (int v = 0; v <= 255; v++) {
+            sb.setLength(sbLen);    // start from scratch
+            sb.append(v);
+
+            String address = sb.toString();
+            // replace the added digits with single char pattern
+            for (int index = sbLen; index < sb.length(); index++) {
+                sb.setCharAt(index, HostPatternsHolder.SINGLE_CHAR_PATTERN);
+            }
+
+            String pattern = sb.toString();
+            HostPatternValue pp = HostPatternsHolder.toPattern(pattern);
+            assertTrue("No match for " + address + " on pattern=" + pattern, HostPatternsHolder.isHostMatch(address, 0, Collections.singletonList(pp)));
+        }
+    }
+
+    @Test
+    public void testIsValidPatternChar() {
+        for (char ch = '\0'; ch <= ' '; ch++) {
+            assertFalse("Unexpected valid character (0x" + Integer.toHexString(ch & 0xFF) + ")", HostPatternsHolder.isValidPatternChar(ch));
+        }
+
+        for (char ch = 'a'; ch <= 'z'; ch++) {
+            assertTrue("Valid character not recognized: " + Character.toString(ch), HostPatternsHolder.isValidPatternChar(ch));
+        }
+
+        for (char ch = 'A'; ch <= 'Z'; ch++) {
+            assertTrue("Valid character not recognized: " + Character.toString(ch), HostPatternsHolder.isValidPatternChar(ch));
+        }
+
+        for (char ch = '0'; ch <= '9'; ch++) {
+            assertTrue("Valid character not recognized: " + Character.toString(ch), HostPatternsHolder.isValidPatternChar(ch));
+        }
+
+        for (char ch : new char[] {'-', '_', '.', HostPatternsHolder.SINGLE_CHAR_PATTERN, HostPatternsHolder.WILDCARD_PATTERN}) {
+            assertTrue("Valid character not recognized: " + Character.toString(ch), HostPatternsHolder.isValidPatternChar(ch));
+        }
+
+        for (char ch : new char[] {
+            '(', ')', '{', '}', '[', ']', '@',
+            '#', '$', '^', '&', '%', '~', '<', '>',
+            ',', '/', '\\', '\'', '"', ':', ';'
+        }) {
+            assertFalse("Unexpected valid character: " + Character.toString(ch), HostPatternsHolder.isValidPatternChar(ch));
+        }
+
+        for (char ch = 0x7E; ch <= 0xFF; ch++) {
+            assertFalse("Unexpected valid character (0x" + Integer.toHexString(ch & 0xFF) + ")", HostPatternsHolder.isValidPatternChar(ch));
+        }
+    }
+
+    @Test
+    public void testResolvePort() {
+        final int originalPort = Short.MAX_VALUE;
+        final int preferredPort = 7365;
+        assertEquals("Mismatched entry port preference",
+            preferredPort, HostConfigEntry.resolvePort(originalPort, preferredPort));
+
+        for (int entryPort : new int[] {-1, 0}) {
+            assertEquals("Non-preferred original port for entry port=" + entryPort,
+                originalPort, HostConfigEntry.resolvePort(originalPort, entryPort));
+        }
+    }
+
+    @Test
+    public void testResolveUsername() {
+        final String originalUser = getCurrentTestName();
+        final String preferredUser = getClass().getSimpleName();
+        assertSame("Mismatched entry user preference",
+                preferredUser, HostConfigEntry.resolveUsername(originalUser, preferredUser));
+
+        for (String entryUser : new String[] {null, ""}) {
+            assertSame("Non-preferred original user for entry user='" + entryUser + "'",
+                originalUser, HostConfigEntry.resolveUsername(originalUser, entryUser));
+        }
+    }
+
+    @Test
+    public void testReadSimpleHostsConfigEntries() throws IOException {
+        validateHostConfigEntries(readHostConfigEntries());
+    }
+
+    @Test
+    public void testReadGlobalHostsConfigEntries() throws IOException {
+        List<HostConfigEntry> entries = validateHostConfigEntries(readHostConfigEntries());
+        assertTrue("Not enough entries read", GenericUtils.size(entries) > 1);
+
+        // global entry MUST be 1st one
+        HostConfigEntry globalEntry = entries.get(0);
+        assertEquals("Mismatched global entry pattern", HostPatternsHolder.ALL_HOSTS_PATTERN, globalEntry.getHost());
+
+        for (int index = 1; index < entries.size(); index++) {
+            HostConfigEntry entry = entries.get(index);
+            assertFalse("No target host for " + entry, GenericUtils.isEmpty(entry.getHostName()));
+            assertTrue("No target port for " + entry, entry.getPort() > 0);
+            assertFalse("No username for " + entry, GenericUtils.isEmpty(entry.getUsername()));
+            assertFalse("No identities for " + entry, GenericUtils.isEmpty(entry.getIdentities()));
+            assertFalse("No properties for " + entry, GenericUtils.isEmpty(entry.getProperties()));
+        }
+    }
+
+    @Test
+    public void testReadMultipleHostPatterns() throws IOException {
+        List<HostConfigEntry> entries = validateHostConfigEntries(readHostConfigEntries());
+        assertEquals("Mismatched number of entries", 1, GenericUtils.size(entries));
+        assertEquals("Mismatched number of patterns", 3, GenericUtils.size(entries.get(0).getPatterns()));
+    }
+
+    @Test
+    public void testResolveIdentityFilePath() throws Exception {
+        final String hostValue = getClass().getSimpleName();
+        final int portValue = 7365;
+        final String userValue = getCurrentTestName();
+
+        Exception err = null;
+        for (String pattern : new String[] {
+            "~/.ssh/%h.key",
+            "%d/.ssh/%h.key",
+            "/home/%u/.ssh/id_rsa_%p",
+            "/home/%u/.ssh/id_%r_rsa",
+            "/home/%u/.ssh/%h/%l.key"
+        }) {
+            try {
+                String result = HostConfigEntry.resolveIdentityFilePath(pattern, hostValue, portValue, userValue);
+                System.out.append('\t').append(pattern).append(" => ").println(result);
+            } catch (Exception e) {
+                System.err.append("Failed (").append(e.getClass().getSimpleName())
+                          .append(") to process pattern=").append(pattern)
+                          .append(": ").println(e.getMessage());
+                err = e;
+            }
+        }
+
+        if (err != null) {
+            throw err;
+        }
+    }
+
+    @Test
+    public void testFindBestMatch() {
+        final String hostValue = getCurrentTestName();
+        HostConfigEntry expected = new HostConfigEntry(hostValue, hostValue, 7365, hostValue);
+        List<HostConfigEntry> matches = new ArrayList<>();
+        matches.add(new HostConfigEntry(HostPatternsHolder.ALL_HOSTS_PATTERN,
+            getClass().getSimpleName(), Short.MAX_VALUE, getClass().getSimpleName()));
+        matches.add(new HostConfigEntry(hostValue + Character.toString(HostPatternsHolder.WILDCARD_PATTERN),
+            getClass().getSimpleName(), Byte.MAX_VALUE, getClass().getSimpleName()));
+        matches.add(expected);
+
+        for (int index = 0; index < matches.size(); index++) {
+            HostConfigEntry actual = HostConfigEntry.findBestMatch(matches);
+            assertSame("Mismatched best match for " + matches, expected, actual);
+            Collections.shuffle(matches);
+        }
+    }
+
+    private static <C extends Collection<HostConfigEntry>> C validateHostConfigEntries(C entries) {
+        assertFalse("No entries", GenericUtils.isEmpty(entries));
+
+        for (HostConfigEntry entry : entries) {
+            assertFalse("No pattern for " + entry, GenericUtils.isEmpty(entry.getHost()));
+            assertFalse("No extra properties for " + entry, GenericUtils.isEmpty(entry.getProperties()));
+        }
+
+        return entries;
+    }
+
+    private List<HostConfigEntry> readHostConfigEntries() throws IOException {
+        return readHostConfigEntries(getCurrentTestName() + ".config.txt");
+    }
+
+    private List<HostConfigEntry> readHostConfigEntries(String resourceName) throws IOException {
+        URL url = getClass().getResource(resourceName);
+        assertNotNull("Missing resource " + resourceName, url);
+        return HostConfigEntry.readHostConfigEntries(url);
+    }
+
+    private static void testCaseInsensitivePatternMatching(String value, Pattern pattern, boolean expected) {
+        for (int index = 0; index < value.length(); index++) {
+            boolean actual = HostPatternsHolder.isHostMatch(value, pattern);
+            assertEquals("Mismatched match result for " + value + " on pattern=" + pattern.pattern(), expected, actual);
+            value = shuffleCase(value);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/client/config/hosts/KnownHostHashValueTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/client/config/hosts/KnownHostHashValueTest.java b/sshd-common/src/test/java/org/apache/sshd/client/config/hosts/KnownHostHashValueTest.java
new file mode 100644
index 0000000..80d58d0
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/client/config/hosts/KnownHostHashValueTest.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.client.config.hosts;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.junit.runners.Parameterized.UseParametersRunnerFactory;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RunWith(Parameterized.class)   // see https://github.com/junit-team/junit/wiki/Parameterized-tests
+@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
+@Category({ NoIoTestCase.class })
+public class KnownHostHashValueTest extends JUnitTestSupport {
+    private final String hostName;
+    private final String hashValue;
+    private final KnownHostHashValue hash;
+
+    public KnownHostHashValueTest(String hostName, String hashValue) {
+        this.hostName = hostName;
+        this.hashValue = hashValue;
+        this.hash = KnownHostHashValue.parse(hashValue);
+    }
+
+    @Parameters(name = "host={0}, hash={1}")
+    public static Collection<Object[]> parameters() {
+        return Arrays.<Object[]>asList(
+                (Object[]) new String[]{"192.168.1.61", "|1|F1E1KeoE/eEWhi10WpGv4OdiO6Y=|3988QV0VE8wmZL7suNrYQLITLCg="});
+    }
+
+    @Test
+    public void testDecodeEncode() {
+        assertSame("Mismatched digester", KnownHostHashValue.DEFAULT_DIGEST, hash.getDigester());
+        assertEquals("Mismatched encoded form", hashValue, hash.toString());
+    }
+
+    @Test
+    public void testHostMatch() {
+        assertTrue("Specified host does not match", hash.isHostMatch(hostName));
+        assertFalse("Unexpected host match", hash.isHostMatch(getCurrentTestName()));
+    }
+
+    @Test
+    public void testCalculateHashValue() throws Exception {
+        byte[] expected = hash.getDigestValue();
+        byte[] actual = KnownHostHashValue.calculateHashValue(hostName, hash.getDigester(), hash.getSaltValue());
+        assertArrayEquals("Mismatched hash value", expected, actual);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcherTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcherTest.java b/sshd-common/src/test/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcherTest.java
new file mode 100644
index 0000000..19375ec
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcherTest.java
@@ -0,0 +1,161 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.client.config.keys;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.OpenOption;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.EnumMap;
+import java.util.Map;
+import java.util.Objects;
+
+import org.apache.sshd.common.config.keys.BuiltinIdentities;
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.keyprovider.KeyIdentityProvider;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class BuiltinClientIdentitiesWatcherTest extends JUnitTestSupport {
+    public BuiltinClientIdentitiesWatcherTest() {
+        super();
+    }
+
+    @Test
+    public void testMultipleFilesWatch() throws Exception {
+        KeyPair identity = CommonTestSupportUtils.getFirstKeyPair(createTestHostKeyProvider());
+        String keyType = ValidateUtils.checkNotNullAndNotEmpty(KeyUtils.getKeyType(identity), "Cannot determine identity key type");
+
+        Path dir = assertHierarchyTargetFolderExists(getTempTargetRelativeFile(getClass().getSimpleName()));
+        Map<BuiltinIdentities, Path> locationsMap = new EnumMap<>(BuiltinIdentities.class);
+        Map<BuiltinIdentities, KeyPair> idsMap = new EnumMap<>(BuiltinIdentities.class);
+        for (BuiltinIdentities id : BuiltinIdentities.VALUES) {
+            Path idFile = dir.resolve(ClientIdentity.getIdentityFileName(id));
+            Files.deleteIfExists(idFile);
+            assertNull("Multiple file mappings for " + id, locationsMap.put(id, idFile));
+            assertNull("Multiple identity mappings for " + id, idsMap.put(id, KeyUtils.cloneKeyPair(keyType, identity)));
+        }
+
+        ClientIdentityLoader loader = new ClientIdentityLoader() {
+            @Override
+            public KeyPair loadClientIdentity(String location, FilePasswordProvider provider) throws IOException, GeneralSecurityException {
+                BuiltinIdentities id = findIdentity(location);
+                assertNotNull("Invalid location: " + location, id);
+                return idsMap.get(id);
+            }
+
+            @Override
+            public boolean isValidLocation(String location) throws IOException {
+                return findIdentity(location) != null;
+            }
+
+            private BuiltinIdentities findIdentity(String location) {
+                if (GenericUtils.isEmpty(location)) {
+                    return null;
+                }
+
+                for (Map.Entry<BuiltinIdentities, Path> le : locationsMap.entrySet()) {
+                    Path path = le.getValue();
+                    if (String.CASE_INSENSITIVE_ORDER.compare(location, path.toString()) == 0) {
+                        return le.getKey();
+                    }
+                }
+
+                return null;
+            }
+        };
+
+        Map<BuiltinIdentities, KeyPair> existing = new EnumMap<>(BuiltinIdentities.class);
+        KeyPairProvider watcher = new BuiltinClientIdentitiesWatcher(dir, false, loader, FilePasswordProvider.EMPTY, false);
+        testMultipleFilesWatch("No files", watcher, existing.values());
+
+        for (BuiltinIdentities id : BuiltinIdentities.VALUES) {
+            String phase = id + " + " + Objects.toString(existing.keySet());
+            touchIdentityFile(locationsMap.get(id));
+            existing.put(id, idsMap.get(id));
+
+            for (int index = 0; index < Byte.SIZE; index++) {
+                testMultipleFilesWatch(phase + "[" + index + "]", watcher, existing.values());
+            }
+        }
+
+        testMultipleFilesWatch("All files", watcher, existing.values());
+
+        for (BuiltinIdentities id : BuiltinIdentities.VALUES) {
+            existing.remove(id);
+            Files.deleteIfExists(locationsMap.get(id));
+            String phase = Objects.toString(existing.keySet()) + " - " + id;
+
+            for (int index = 0; index < Byte.SIZE; index++) {
+                testMultipleFilesWatch(phase + "[" + index + "]", watcher, existing.values());
+            }
+        }
+    }
+
+    private static void touchIdentityFile(Path idFile) throws IOException {
+        OpenOption[] options = IoUtils.EMPTY_OPEN_OPTIONS;
+        if (Files.exists(idFile, IoUtils.EMPTY_LINK_OPTIONS)) {
+            options = new OpenOption[]{StandardOpenOption.WRITE, StandardOpenOption.APPEND};
+        }
+
+        try (OutputStream out = Files.newOutputStream(idFile, options)) {
+            out.write(new Date(System.currentTimeMillis()).toString().getBytes(StandardCharsets.UTF_8));
+            out.write('\n');
+        }
+    }
+
+    private static void testMultipleFilesWatch(String phase, KeyIdentityProvider watcher, Collection<? extends KeyPair> expected) {
+        Iterable<KeyPair> keys = watcher.loadKeys();
+        Collection<KeyPair> actual = new ArrayList<>();
+        for (KeyPair kp : keys) {
+            actual.add(kp);
+        }
+        assertEquals(phase + ": mismatched sizes", GenericUtils.size(expected), GenericUtils.size(actual));
+
+        if (!GenericUtils.isEmpty(expected)) {
+            for (KeyPair kp : expected) {
+                assertTrue(phase + ": missing key", actual.contains(kp));
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcherTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcherTest.java b/sshd-common/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcherTest.java
new file mode 100644
index 0000000..144a3b0
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcherTest.java
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.client.config.keys;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.OpenOption;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.util.Date;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class ClientIdentityFileWatcherTest extends JUnitTestSupport {
+    public ClientIdentityFileWatcherTest() {
+        super();
+    }
+
+    @Test
+    public void testIdentityReload() throws Exception {
+        Path dir = assertHierarchyTargetFolderExists(getTempTargetRelativeFile(getClass().getSimpleName()));
+        Path idFile = dir.resolve(getCurrentTestName() + ".pem");
+        KeyPair identity = CommonTestSupportUtils.getFirstKeyPair(createTestHostKeyProvider());
+        ClientIdentityLoader loader = new ClientIdentityLoader() {
+            @Override
+            public KeyPair loadClientIdentity(String location, FilePasswordProvider provider) throws IOException, GeneralSecurityException {
+                assertTrue("Invalid location: " + location, isValidLocation(location));
+                return identity;
+            }
+
+            @Override
+            public boolean isValidLocation(String location) throws IOException {
+                return Objects.equals(location, toString());
+            }
+
+            @Override
+            public String toString() {
+                return Objects.toString(idFile);
+            }
+        };
+
+        AtomicInteger reloadCount = new AtomicInteger(0);
+        ClientIdentityProvider idProvider = new ClientIdentityFileWatcher(idFile, loader, FilePasswordProvider.EMPTY, false) {
+            @Override
+            protected KeyPair reloadClientIdentity(Path path) throws IOException, GeneralSecurityException {
+                assertEquals("Mismatched client identity path", idFile, path);
+                reloadCount.incrementAndGet();
+                return super.reloadClientIdentity(path);
+            }
+        };
+        Files.deleteIfExists(idFile);
+
+        testIdentityReload("Non-existing", reloadCount, idProvider, null, 0);
+
+        touchIdentityFile(idFile);
+        for (int index = 1; index < Byte.SIZE; index++) {
+            testIdentityReload("Created iteration " + 1, reloadCount, idProvider, identity, 1);
+        }
+
+        touchIdentityFile(idFile);
+        for (int index = 1; index < Byte.SIZE; index++) {
+            testIdentityReload("Modified iteration " + 1, reloadCount, idProvider, identity, 2);
+        }
+    }
+
+    private static void touchIdentityFile(Path idFile) throws IOException {
+        OpenOption[] options = IoUtils.EMPTY_OPEN_OPTIONS;
+        if (Files.exists(idFile, IoUtils.EMPTY_LINK_OPTIONS)) {
+            options = new OpenOption[]{StandardOpenOption.WRITE, StandardOpenOption.APPEND};
+        }
+
+        try (OutputStream out = Files.newOutputStream(idFile, options)) {
+            out.write(new Date(System.currentTimeMillis()).toString().getBytes(StandardCharsets.UTF_8));
+            out.write('\n');
+        }
+    }
+
+    private static void testIdentityReload(
+            String phase, Number reloadCount, ClientIdentityProvider provider, KeyPair expectedIdentity, int expectedCount)
+                throws Exception {
+        KeyPair actualIdentity = provider.getClientIdentity();
+        assertSame(phase + ": mismatched identity", expectedIdentity, actualIdentity);
+        assertEquals(phase + ": mismatched re-load count", expectedCount, reloadCount.intValue());
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityTest.java b/sshd-common/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityTest.java
new file mode 100644
index 0000000..bc2f02c
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityTest.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.client.config.keys;
+
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.Path;
+import java.security.KeyPair;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.Map;
+
+import org.apache.sshd.common.config.keys.BuiltinIdentities;
+import org.apache.sshd.common.config.keys.IdentityUtils;
+import org.apache.sshd.common.keyprovider.KeyIdentityProvider;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class ClientIdentityTest extends JUnitTestSupport {
+    public ClientIdentityTest() {
+        super();
+    }
+
+    @Test
+    public void testLoadClientIdentities() throws Exception {
+        Path resFolder = getTestResourcesFolder();
+        LinkOption[] options = IoUtils.getLinkOptions(true);
+        Collection<BuiltinIdentities> expected = EnumSet.noneOf(BuiltinIdentities.class);
+        for (BuiltinIdentities type : BuiltinIdentities.VALUES) {
+            String fileName = ClientIdentity.getIdentityFileName(type);
+            Path file = resFolder.resolve(fileName);
+            if (!Files.exists(file, options)) {
+                System.out.println("Skip non-existing identity file " + file);
+                continue;
+            }
+
+            if (!type.isSupported()) {
+                System.out.println("Skip unsupported identity file " + file);
+                continue;
+            }
+
+            expected.add(type);
+        }
+
+        Map<String, KeyPair> ids = ClientIdentity.loadDefaultIdentities(
+                resFolder,
+                false,   // don't be strict
+                null,    // none of the files is password protected
+                options);
+        assertEquals("Mismatched loaded ids count", GenericUtils.size(expected), GenericUtils.size(ids));
+
+        Collection<KeyPair> pairs = new ArrayList<>(ids.size());
+        for (BuiltinIdentities type : BuiltinIdentities.VALUES) {
+            if (expected.contains(type)) {
+                KeyPair kp = ids.get(type.getName());
+                assertNotNull("No key pair loaded for " + type, kp);
+                pairs.add(kp);
+            }
+        }
+
+        KeyIdentityProvider provider = IdentityUtils.createKeyPairProvider(ids, true /* supported only */);
+        assertNotNull("No provider generated", provider);
+
+        Iterable<KeyPair> keys = provider.loadKeys();
+        for (KeyPair kp : keys) {
+            assertTrue("Unexpected loaded key: " + kp, pairs.remove(kp));
+        }
+
+        assertEquals("Not all pairs listed", 0, pairs.size());
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/SshConstantsTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/SshConstantsTest.java b/sshd-common/src/test/java/org/apache/sshd/common/SshConstantsTest.java
new file mode 100644
index 0000000..1d123d3
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/SshConstantsTest.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common;
+
+import java.util.Collection;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class SshConstantsTest extends JUnitTestSupport {
+    public SshConstantsTest() {
+        super();
+    }
+
+    @Test
+    public void testGetDisconnectReason() {
+        for (int reason = SshConstants.SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT; reason <= SshConstants.SSH2_DISCONNECT_ILLEGAL_USER_NAME; reason++) {
+            String name = SshConstants.getDisconnectReasonName(reason);
+            assertTrue("Mismatched name for reason=" + reason + ": " + name, name.startsWith("SSH2_DISCONNECT_"));
+        }
+    }
+
+    @Test
+    public void testGetOpenErrorName() {
+        for (int code = SshConstants.SSH_OPEN_ADMINISTRATIVELY_PROHIBITED; code <= SshConstants.SSH_OPEN_RESOURCE_SHORTAGE; code++) {
+            String name = SshConstants.getOpenErrorCodeName(code);
+            assertTrue("Mismatched name for code=" + code + ": " + name, name.startsWith("SSH_OPEN_"));
+        }
+    }
+
+    @Test
+    public void testAmbiguousOpcodes() throws Exception {
+        int[] knownAmbiguities = {30, 31, 60};
+        Collection<Integer> opcodes = SshConstants.getAmbiguousOpcodes();
+        assertTrue("Not enough ambiguities found", GenericUtils.size(opcodes) >= knownAmbiguities.length);
+
+        for (int cmd : knownAmbiguities) {
+            assertEquals("Mismatched mnemonic for known ambiguity=" + cmd, Integer.toString(cmd), SshConstants.getCommandMessageName(cmd));
+            assertTrue("Known ambiguity not reported as such: " + cmd, SshConstants.isAmbiguousOpcode(cmd));
+            assertTrue("Known ambiguity=" + cmd + " not listed: " + opcodes, opcodes.contains(cmd));
+        }
+
+        for (Integer cmd : opcodes) {
+            assertEquals("Mismatched mnemonic for " + cmd, cmd.toString(), SshConstants.getCommandMessageName(cmd));
+            assertTrue("Opcode not detected as ambiguous: " + cmd, SshConstants.isAmbiguousOpcode(cmd));
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/VersionPropertiesTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/VersionPropertiesTest.java b/sshd-common/src/test/java/org/apache/sshd/common/VersionPropertiesTest.java
new file mode 100644
index 0000000..c0978d4
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/VersionPropertiesTest.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common;
+
+import java.util.Map;
+
+import org.apache.sshd.common.config.VersionProperties;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class VersionPropertiesTest extends JUnitTestSupport {
+    public VersionPropertiesTest() {
+        super();
+    }
+
+    @Test
+    public void testNonEmptyProperties() {
+        Map<?, ?> props = VersionProperties.getVersionProperties();
+        assertTrue(GenericUtils.isNotEmpty(props));
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/cipher/AES192CTRTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/cipher/AES192CTRTest.java b/sshd-common/src/test/java/org/apache/sshd/common/cipher/AES192CTRTest.java
new file mode 100644
index 0000000..6611702
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/cipher/AES192CTRTest.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.cipher;
+
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class AES192CTRTest extends BaseCipherTest {
+    public AES192CTRTest() {
+        super();
+    }
+
+    @Test
+    public void testEncryptDecrypt() throws Exception {
+        // for AES 256 bits we need the JCE unlimited strength policy
+        ensureKeySizeSupported(16, 24, "AES", "AES/CTR/NoPadding");
+        testEncryptDecrypt(BuiltinCiphers.aes192ctr);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/cipher/AES256CBCTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/cipher/AES256CBCTest.java b/sshd-common/src/test/java/org/apache/sshd/common/cipher/AES256CBCTest.java
new file mode 100644
index 0000000..dd39fd4
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/cipher/AES256CBCTest.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.cipher;
+
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class AES256CBCTest extends BaseCipherTest {
+    public AES256CBCTest() {
+        super();
+    }
+
+    @Test
+    public void testEncryptDecrypt() throws Exception {
+        // for AES 256 bits we need the JCE unlimited strength policy
+        ensureKeySizeSupported(16, 32, "AES", "AES/CBC/NoPadding");
+        testEncryptDecrypt(BuiltinCiphers.aes256cbc);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/cipher/ARCFOUR128Test.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/cipher/ARCFOUR128Test.java b/sshd-common/src/test/java/org/apache/sshd/common/cipher/ARCFOUR128Test.java
new file mode 100644
index 0000000..1c37449
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/cipher/ARCFOUR128Test.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.cipher;
+
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class ARCFOUR128Test extends BaseCipherTest {
+    public ARCFOUR128Test() {
+        super();
+    }
+
+    @Test
+    public void testEncryptDecrypt() throws Exception {
+        testEncryptDecrypt(BuiltinCiphers.arcfour128);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/cipher/ARCFOUR256Test.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/cipher/ARCFOUR256Test.java b/sshd-common/src/test/java/org/apache/sshd/common/cipher/ARCFOUR256Test.java
new file mode 100644
index 0000000..5511e0f
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/cipher/ARCFOUR256Test.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.cipher;
+
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class ARCFOUR256Test extends BaseCipherTest {
+    public ARCFOUR256Test() {
+        super();
+    }
+
+    @Test
+    public void testEncryptDecrypt() throws Exception {
+        // for RC4 256 bits we need the JCE unlimited strength policy
+        ensureKeySizeSupported(32, "ARCFOUR", "RC4");
+        testEncryptDecrypt(BuiltinCiphers.arcfour256);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/cipher/BaseCipherTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/cipher/BaseCipherTest.java b/sshd-common/src/test/java/org/apache/sshd/common/cipher/BaseCipherTest.java
new file mode 100644
index 0000000..514cba4
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/cipher/BaseCipherTest.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.cipher;
+
+import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.cipher.Cipher.Mode;
+import org.apache.sshd.common.util.security.SecurityUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.Assume;
+import org.junit.experimental.categories.Category;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@Category({ NoIoTestCase.class })
+public abstract class BaseCipherTest extends JUnitTestSupport {
+    protected BaseCipherTest() {
+        super();
+    }
+
+    protected void ensureKeySizeSupported(int bsize, String algorithm, String transformation) throws GeneralSecurityException {
+        try {
+            javax.crypto.Cipher cipher = SecurityUtils.getCipher(transformation);
+            byte[] key = new byte[bsize];
+            cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, new SecretKeySpec(key, algorithm));
+        } catch (GeneralSecurityException e) {
+            if (e instanceof InvalidKeyException) {    // NOTE: assumption violations are NOT test failures...
+                Assume.assumeTrue(algorithm + "/" + transformation + "[" + bsize + "] N/A", false);
+            }
+
+            throw e;
+        }
+    }
+
+    protected void ensureKeySizeSupported(int ivsize, int bsize, String algorithm, String transformation) throws GeneralSecurityException {
+        try {
+            javax.crypto.Cipher cipher = SecurityUtils.getCipher(transformation);
+            byte[] key = new byte[bsize];
+            byte[] iv = new byte[ivsize];
+            cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, new SecretKeySpec(key, algorithm), new IvParameterSpec(iv));
+        } catch (GeneralSecurityException e) {
+            if (e instanceof InvalidKeyException) {
+                Assume.assumeTrue(algorithm + "/" + transformation + "[" + bsize + "/" + ivsize + "]", false /* force exception */);
+            }
+
+            throw e;
+        }
+    }
+
+    protected void testEncryptDecrypt(NamedFactory<Cipher> factory) throws Exception {
+        String facName = factory.getName();
+        Cipher enc = factory.create();
+        int keySize = enc.getBlockSize();
+        int ivSize = enc.getIVSize();
+        byte[] key = new byte[keySize];
+        byte[] iv = new byte[ivSize];
+        enc.init(Mode.Encrypt, key, iv);
+
+        byte[] expected = facName.getBytes(StandardCharsets.UTF_8);
+        byte[] workBuf = expected.clone();    // need to clone since the cipher works in-line
+        enc.update(workBuf, 0, workBuf.length);
+
+        Cipher dec = factory.create();
+        dec.init(Mode.Decrypt, key, iv);
+        byte[] actual = workBuf.clone();
+        dec.update(actual, 0, actual.length);
+
+        assertArrayEquals(facName, expected, actual);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/cipher/ECCurvesTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/cipher/ECCurvesTest.java b/sshd-common/src/test/java/org/apache/sshd/common/cipher/ECCurvesTest.java
new file mode 100644
index 0000000..2294606
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/cipher/ECCurvesTest.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.cipher;
+
+import java.util.EnumSet;
+import java.util.Set;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class ECCurvesTest extends JUnitTestSupport {
+    public ECCurvesTest() {
+        super();
+    }
+
+    @Test
+    public void testFromName() {
+        for (ECCurves expected : ECCurves.VALUES) {
+            String name = expected.getName();
+            for (int index = 0; index < name.length(); index++) {
+                ECCurves actual = ECCurves.fromCurveName(name);
+                assertSame(name, expected, actual);
+                name = shuffleCase(name);
+            }
+        }
+    }
+
+    @Test
+    public void testAllNamesListed() {
+        Set<ECCurves> listed = EnumSet.noneOf(ECCurves.class);
+        for (String name : ECCurves.NAMES) {
+            ECCurves c = ECCurves.fromCurveName(name);
+            assertNotNull("No curve for listed name=" + name, c);
+            assertTrue("Duplicated listed name: " + name, listed.add(c));
+        }
+
+        assertEquals("Mismatched listed vs. values", ECCurves.VALUES, listed);
+    }
+
+    @Test
+    public void testFromKeySize() {
+        for (ECCurves expected : ECCurves.VALUES) {
+            String name = expected.getName();
+            ECCurves actual = ECCurves.fromCurveSize(expected.getKeySize());
+            assertSame(name, expected, actual);
+        }
+    }
+
+    @Test
+    public void testFromCurveParameters() {
+        for (ECCurves expected : ECCurves.VALUES) {
+            String name = expected.getName();
+            ECCurves actual = ECCurves.fromCurveParameters(expected.getParameters());
+            assertSame(name, expected, actual);
+        }
+    }
+
+    @Test
+    public void testFromKeyType() {
+        for (ECCurves expected : ECCurves.VALUES) {
+            String keyType = expected.getKeyType();
+            for (int index = 0; index < keyType.length(); index++) {
+                ECCurves actual = ECCurves.fromKeyType(keyType);
+                assertSame(keyType, expected, actual);
+                keyType = shuffleCase(keyType);
+            }
+        }
+    }
+
+    @Test
+    public void testAllKeyTypesListed() {
+        Set<ECCurves> listed = EnumSet.noneOf(ECCurves.class);
+        for (String name : ECCurves.KEY_TYPES) {
+            ECCurves c = ECCurves.fromKeyType(name);
+            assertNotNull("No curve for listed key type=" + name, c);
+            assertTrue("Duplicated listed key type: " + name, listed.add(c));
+        }
+
+        assertEquals("Mismatched listed vs. values", ECCurves.VALUES, listed);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/compression/BuiltinCompressionsTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/compression/BuiltinCompressionsTest.java b/sshd-common/src/test/java/org/apache/sshd/common/compression/BuiltinCompressionsTest.java
new file mode 100644
index 0000000..3d1ed00
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/compression/BuiltinCompressionsTest.java
@@ -0,0 +1,171 @@
+/*
+ * 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.compression;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.compression.BuiltinCompressions.ParseResult;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+import org.mockito.Mockito;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class BuiltinCompressionsTest extends JUnitTestSupport {
+    public BuiltinCompressionsTest() {
+        super();
+    }
+
+    @Test
+    public void testFromFactoryName() {
+        for (BuiltinCompressions expected : BuiltinCompressions.VALUES) {
+            String name = expected.getName();
+
+            for (int index = 0; index < name.length(); index++) {
+                BuiltinCompressions actual = BuiltinCompressions.fromFactoryName(name);
+                assertSame(name, expected, actual);
+                name = shuffleCase(name);
+            }
+        }
+    }
+
+    @Test
+    public void testAllConstantsCovered() throws Exception {
+        Set<BuiltinCompressions> avail = EnumSet.noneOf(BuiltinCompressions.class);
+        Field[] fields = BuiltinCompressions.Constants.class.getFields();
+        for (Field f : fields) {
+            String name = (String) f.get(null);
+            BuiltinCompressions value = BuiltinCompressions.fromFactoryName(name);
+            assertNotNull("No match found for " + name, value);
+            assertTrue(name + " re-specified", avail.add(value));
+        }
+
+        assertEquals("Incomplete coverage", BuiltinCompressions.VALUES, avail);
+    }
+
+    @Test
+    public void testParseCompressionsList() {
+        List<String> builtin = NamedResource.getNameList(BuiltinCompressions.VALUES);
+        List<String> unknown = Arrays.asList(getClass().getPackage().getName(), getClass().getSimpleName(), getCurrentTestName());
+        Random rnd = new Random();
+        for (int index = 0; index < (builtin.size() + unknown.size()); index++) {
+            Collections.shuffle(builtin, rnd);
+            Collections.shuffle(unknown, rnd);
+
+            List<String> weavedList = new ArrayList<>(builtin.size() + unknown.size());
+            for (int bIndex = 0, uIndex = 0; (bIndex < builtin.size()) || (uIndex < unknown.size());) {
+                boolean useBuiltin = false;
+                if (bIndex < builtin.size()) {
+                    useBuiltin = uIndex >= unknown.size() || rnd.nextBoolean();
+                }
+
+                if (useBuiltin) {
+                    weavedList.add(builtin.get(bIndex));
+                    bIndex++;
+                } else if (uIndex < unknown.size()) {
+                    weavedList.add(unknown.get(uIndex));
+                    uIndex++;
+                }
+            }
+
+            String fullList = GenericUtils.join(weavedList, ',');
+            ParseResult result = BuiltinCompressions.parseCompressionsList(fullList);
+            List<String> parsed = NamedResource.getNameList(result.getParsedFactories());
+            List<String> missing = result.getUnsupportedFactories();
+
+            // makes sure not only that the contents are the same but also the order
+            assertListEquals(fullList + "[parsed]", builtin, parsed);
+            assertListEquals(fullList + "[unsupported]", unknown, missing);
+        }
+    }
+
+    @Test
+    public void testResolveFactoryOnBuiltinValues() {
+        for (NamedFactory<Compression> expected : BuiltinCompressions.VALUES) {
+            String name = expected.getName();
+            NamedFactory<Compression> actual = BuiltinCompressions.resolveFactory(name);
+            assertSame(name, expected, actual);
+        }
+    }
+
+    @Test
+    public void testNotAllowedToRegisterBuiltinFactories() {
+        for (CompressionFactory expected : BuiltinCompressions.VALUES) {
+            try {
+                BuiltinCompressions.registerExtension(expected);
+                fail("Unexpected success for " + expected.getName());
+            } catch (IllegalArgumentException e) {
+                // expected - ignored
+            }
+        }
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testNotAllowedToOverrideRegisteredFactories() {
+        CompressionFactory expected = Mockito.mock(CompressionFactory.class);
+        Mockito.when(expected.getName()).thenReturn(getCurrentTestName());
+
+        String name = expected.getName();
+        try {
+            for (int index = 1; index <= Byte.SIZE; index++) {
+                BuiltinCompressions.registerExtension(expected);
+                assertEquals("Unexpected success at attempt #" + index, 1, index);
+            }
+        } finally {
+            BuiltinCompressions.unregisterExtension(name);
+        }
+    }
+
+    @Test
+    public void testResolveFactoryOnRegisteredExtension() {
+        CompressionFactory expected = Mockito.mock(CompressionFactory.class);
+        Mockito.when(expected.getName()).thenReturn(getCurrentTestName());
+
+        String name = expected.getName();
+        try {
+            assertNull("Extension already registered", BuiltinCompressions.resolveFactory(name));
+            BuiltinCompressions.registerExtension(expected);
+
+            NamedFactory<Compression> actual = BuiltinCompressions.resolveFactory(name);
+            assertSame("Mismatched resolved instance", expected, actual);
+        } finally {
+            NamedFactory<Compression> actual = BuiltinCompressions.unregisterExtension(name);
+            assertSame("Mismatched unregistered instance", expected, actual);
+            assertNull("Extension not un-registered", BuiltinCompressions.resolveFactory(name));
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/config/TimeValueConfigTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/config/TimeValueConfigTest.java b/sshd-common/src/test/java/org/apache/sshd/common/config/TimeValueConfigTest.java
new file mode 100644
index 0000000..0466d95
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/config/TimeValueConfigTest.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config;
+
+import java.util.concurrent.TimeUnit;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class TimeValueConfigTest extends JUnitTestSupport {
+    public TimeValueConfigTest() {
+        super();
+    }
+
+    @Test
+    public void testDurationOf() {
+        Object[] values = {
+            "600", TimeUnit.SECONDS.toMillis(600L),
+            "10m", TimeUnit.MINUTES.toMillis(10L),
+            "1h30m", TimeUnit.MINUTES.toMillis(90L),
+            "2d", TimeUnit.DAYS.toMillis(2L),
+            "3w", TimeUnit.DAYS.toMillis(3L * 7L)
+        };
+        for (int index = 0; index < values.length; index += 2) {
+            String s = (String) values[index];
+            Number expected = (Number) values[index + 1];
+            long actual = TimeValueConfig.durationOf(s);
+            assertEquals(s, expected.longValue(), actual);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntryLoginOptionsParseTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntryLoginOptionsParseTest.java b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntryLoginOptionsParseTest.java
new file mode 100644
index 0000000..d912998
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntryLoginOptionsParseTest.java
@@ -0,0 +1,124 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.config.keys;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.junit.runners.Parameterized.UseParametersRunnerFactory;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RunWith(Parameterized.class)   // see https://github.com/junit-team/junit/wiki/Parameterized-tests
+@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
+public class AuthorizedKeyEntryLoginOptionsParseTest extends JUnitTestSupport {
+    private final String value;
+    private final String loginPart;
+    private final String keyPart;
+    private final Map<String, String> options;
+
+    public AuthorizedKeyEntryLoginOptionsParseTest(String value, String loginPart, String keyPart, Map<String, String> options) {
+        this.value = value;
+        this.loginPart = loginPart;
+        this.keyPart = keyPart;
+        this.options = options;
+    }
+
+    @Parameters(name = "{0}")
+    public static List<Object[]> parameters() {
+        List<Object[]> params = new ArrayList<>();
+        addData(params, "ssh-rsa AAAAB2...19Q==", "john@example.net", "from=\"*.sales.example.net,!pc.sales.example.net\"");
+        addData(params, "ssh-dss AAAAC3...51R==", "example.net", "command=\"dump /home\"", "no-pty", "no-port-forwarding");
+        addData(params, "ssh-dss AAAAB5...21S==", "", "permitopen=\"192.0.2.1:80\"", "permitopen=\"192.0.2.2:25\"");
+        addData(params, "ssh-rsa AAAA...==", "jane@example.net", "tunnel=\"0\"", "command=\"sh /etc/netstart tun0\"");
+        addData(params, "ssh-rsa AAAA1C8...32Tv==", "user@example.net", "!restrict", "command=\"uptime\"");
+        addData(params, "ssh-rsa AAAA1f8...IrrC5==", "user@example.net", "restrict", "!pty", "command=\"nethack\"");
+        return params;
+    }
+
+    private static void addData(List<Object[]> params, String keyData, String comment, String... comps) {
+        StringBuilder sb = new StringBuilder();
+
+        Map<String, String> optionsMap = new HashMap<>();
+        for (String c : comps) {
+            if (sb.length() > 0) {
+                sb.append(',');
+            }
+            sb.append(c);
+
+            int pos = c.indexOf('=');
+            if (pos > 0) {
+                String name = c.substring(0, pos);
+                String value = GenericUtils.stripQuotes(c.substring(pos + 1)).toString();
+                String prev = optionsMap.put(name, value);
+                if (prev != null) {
+                    optionsMap.put(name, prev + "," + value);
+                }
+            } else {
+                optionsMap.put(c, Boolean.toString(c.charAt(0) != AuthorizedKeyEntry.BOOLEAN_OPTION_NEGATION_INDICATOR));
+            }
+        }
+
+        int pos = sb.length();
+
+        sb.append(' ').append(keyData);
+        if (GenericUtils.isNotEmpty(comment)) {
+            sb.append(' ').append(comment);
+        }
+
+        String value = sb.toString();
+        params.add(new Object[] {value, value.substring(0, pos), value.substring(pos + 1), optionsMap});
+    }
+
+    @Test
+    public void testResolveEntryComponents() {
+        Map.Entry<String, String> actual = AuthorizedKeyEntry.resolveEntryComponents(value);
+        assertNotNull(value, actual);
+        assertEquals("login(" + value + ")", loginPart, actual.getKey());
+        assertEquals("remainder(" + value + ")", keyPart, actual.getValue());
+    }
+
+    @Test
+    public void testParseLoginOptions() {
+        Map<String, String> parsed = AuthorizedKeyEntry.parseLoginOptions(loginPart);
+        options.forEach((key, expected) -> {
+            String actual = parsed.get(key);
+            assertEquals(key, expected, actual);
+        });
+        assertEquals("Mismatched size", options.size(), parsed.size());
+    }
+
+    @Override
+    public String toString() {
+        return getClass().getSimpleName() + "[" + value + "]";
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/config/keys/BuiltinIdentitiesTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/config/keys/BuiltinIdentitiesTest.java b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/BuiltinIdentitiesTest.java
new file mode 100644
index 0000000..4735846
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/BuiltinIdentitiesTest.java
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config.keys;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.util.List;
+
+import org.apache.sshd.common.util.security.SecurityUtils;
+import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.Assume;
+import org.junit.BeforeClass;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.junit.runners.Parameterized.UseParametersRunnerFactory;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RunWith(Parameterized.class)   // see https://github.com/junit-team/junit/wiki/Parameterized-tests
+@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
+@Category({ NoIoTestCase.class })
+public class BuiltinIdentitiesTest extends JUnitTestSupport {
+    private final BuiltinIdentities expected;
+
+    public BuiltinIdentitiesTest(BuiltinIdentities expected) {
+        this.expected = expected;
+    }
+
+    @Parameters(name = "{0}")
+    public static List<Object[]> parameters() {
+        return parameterize(BuiltinIdentities.VALUES);
+    }
+
+    @BeforeClass    // Dirty hack around the parameterized run
+    public static void testAllConstantsCovered() throws Exception {
+        Field[] fields = BuiltinIdentities.Constants.class.getFields();
+        for (Field f : fields) {
+            int mods = f.getModifiers();
+            if (!Modifier.isStatic(mods)) {
+                continue;
+            }
+
+            if (!Modifier.isFinal(mods)) {
+                continue;
+            }
+
+            Class<?> type = f.getType();
+            if (!String.class.isAssignableFrom(type)) {
+                continue;
+            }
+
+            String name = f.getName();
+            String value = (String) f.get(null);
+            BuiltinIdentities id = BuiltinIdentities.fromName(value);
+            assertNotNull("No match found for field " + name + "=" + value, id);
+        }
+    }
+
+    @Test
+    public void testFromName() {
+        String name = expected.getName();
+        for (int index = 0, count = name.length(); index < count; index++) {
+            assertSame(name, expected, BuiltinIdentities.fromName(name));
+            name = shuffleCase(name);
+        }
+    }
+
+    @Test
+    public void testFromAlgorithm() {
+        String algorithm = expected.getAlgorithm();
+        for (int index = 0, count = algorithm.length(); index < count; index++) {
+            assertSame(algorithm, expected, BuiltinIdentities.fromAlgorithm(algorithm));
+            algorithm = shuffleCase(algorithm);
+        }
+    }
+
+    @Test
+    public void testFromKey() throws GeneralSecurityException {
+        Assume.assumeTrue("Unsupported built-in identity", expected.isSupported());
+        KeyPairGenerator gen = SecurityUtils.getKeyPairGenerator(expected.getAlgorithm());
+        KeyPair kp = gen.generateKeyPair();
+        outputDebugMessage("Checking built-in identity: %s", expected);
+        assertSame(expected + "[pair]", expected, BuiltinIdentities.fromKeyPair(kp));
+        assertSame(expected + "[public]", expected, BuiltinIdentities.fromKey(kp.getPublic()));
+        assertSame(expected + "[private]", expected, BuiltinIdentities.fromKey(kp.getPrivate()));
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyRandomArtTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyRandomArtTest.java b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyRandomArtTest.java
new file mode 100644
index 0000000..eedbfe7
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyRandomArtTest.java
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config.keys;
+
+import java.security.KeyPair;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.sshd.common.cipher.ECCurves;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
+import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.AfterClass;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.junit.runners.Parameterized.UseParametersRunnerFactory;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RunWith(Parameterized.class)   // see https://github.com/junit-team/junit/wiki/Parameterized-tests
+@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
+@Category({ NoIoTestCase.class })
+public class KeyRandomArtTest extends JUnitTestSupport {
+    private static final Collection<KeyPair> KEYS = new LinkedList<>();
+
+    private final String algorithm;
+    private final int keySize;
+    private final KeyPair keyPair;
+
+    public KeyRandomArtTest(String algorithm, int keySize) throws Exception {
+        this.algorithm = algorithm;
+        this.keySize = keySize;
+        this.keyPair = CommonTestSupportUtils.generateKeyPair(algorithm, keySize);
+        KEYS.add(this.keyPair);
+    }
+
+    @Parameters(name = "algorithm={0}, key-size={1}")
+    public static List<Object[]> parameters() {
+        List<Object[]> params = new ArrayList<>();
+        for (int keySize : RSA_SIZES) {
+            params.add(new Object[]{KeyUtils.RSA_ALGORITHM, keySize});
+        }
+
+        for (int keySize : DSS_SIZES) {
+            params.add(new Object[]{KeyUtils.DSS_ALGORITHM, keySize});
+        }
+
+        if (SecurityUtils.isECCSupported()) {
+            for (ECCurves curve : ECCurves.VALUES) {
+                params.add(new Object[]{KeyUtils.EC_ALGORITHM, curve.getKeySize()});
+            }
+        }
+
+        if (SecurityUtils.isEDDSACurveSupported()) {
+            for (int keySize : ED25519_SIZES) {
+                params.add(new Object[]{SecurityUtils.EDDSA, keySize});
+            }
+        }
+        return params;
+    }
+
+    @AfterClass
+    public static void dumpAllArts() throws Exception {
+        KeyRandomArt.combine(System.out, ' ', () -> KEYS);
+    }
+
+    @Test
+    public void testRandomArtString() throws Exception {
+        KeyRandomArt art = new KeyRandomArt(keyPair.getPublic());
+        assertEquals("Mismatched algorithm", algorithm, art.getAlgorithm());
+        assertEquals("Mismatched key size", keySize, art.getKeySize());
+
+        String s = art.toString();
+        String[] lines = GenericUtils.split(s, '\n');
+        assertEquals("Mismatched lines count", KeyRandomArt.FLDSIZE_Y + 2, lines.length);
+
+        for (int index = 0; index < lines.length; index++) {
+            String l = lines[index];
+            if ((l.length() > 0) && (l.charAt(l.length() - 1) == '\r')) {
+                l = l.substring(0, l.length() - 1);
+                lines[index] = l;
+            }
+            System.out.append('\t').println(l);
+
+            assertTrue("Mismatched line length #" + (index + 1) + ": " + l.length(), l.length() >= (KeyRandomArt.FLDSIZE_X + 2));
+        }
+    }
+}


[35/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common utilities code from sshd-core into sshd-common (new artifact)

Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/security/AbstractSecurityProviderRegistrar.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/security/AbstractSecurityProviderRegistrar.java b/sshd-common/src/main/java/org/apache/sshd/common/util/security/AbstractSecurityProviderRegistrar.java
new file mode 100644
index 0000000..b665166
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/security/AbstractSecurityProviderRegistrar.java
@@ -0,0 +1,129 @@
+/*
+ * 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.security;
+
+import java.security.Provider;
+import java.security.Security;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractSecurityProviderRegistrar
+                extends AbstractLoggingBean
+                implements SecurityProviderRegistrar {
+    protected final Map<String, Object> props = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+    protected final Map<Class<?>, Map<String, Boolean>> supportedEntities = new HashMap<>();
+    protected final AtomicReference<Provider> providerHolder = new AtomicReference<>(null);
+
+    private final String name;
+
+    protected AbstractSecurityProviderRegistrar(String name) {
+        this.name = ValidateUtils.checkNotNullAndNotEmpty(name, "No name provided");
+    }
+
+    @Override
+    public final String getName() {
+        return name;
+    }
+
+    @Override
+    public Map<String, Object> getProperties() {
+        return props;
+    }
+
+    @Override
+    public boolean isSecurityEntitySupported(Class<?> entityType, String name) {
+        Map<String, Boolean> supportMap;
+        synchronized (supportedEntities) {
+            supportMap = supportedEntities.computeIfAbsent(
+                    entityType, k -> new TreeMap<>(String.CASE_INSENSITIVE_ORDER));
+        }
+
+        Boolean supportFlag;
+        synchronized (supportMap) {
+            supportFlag = supportMap.computeIfAbsent(
+                    name, k -> SecurityProviderRegistrar.super.isSecurityEntitySupported(entityType, name));
+        }
+
+        return supportFlag;
+    }
+
+    /**
+     * Attempts to see if a provider with this name already registered. If not,
+     * then uses reflection API in order to load and instantiate the specified
+     * <tt>providerClassName</tt>
+     *
+     * @param providerClassName The fully-qualified class name to instantiate
+     * if a provider not already registered
+     * @return The resolved {@link Provider} instance - <B>Note:</B> the result
+     * is <U>cached</U> - i.e., successful resolution result will not cause
+     * the code to re-resolve the provider
+     * @throws ReflectiveOperationException If failed to instantiate the provider
+     * @throws UnsupportedOperationException If registrar not supported
+     * @see #isSupported()
+     * @see Security#getProvider(String)
+     * @see #createProviderInstance(String)
+     */
+    protected Provider getOrCreateProvider(String providerClassName) throws ReflectiveOperationException {
+        if (!isSupported()) {
+            throw new UnsupportedOperationException("Provider not supported");
+        }
+
+        Provider provider;
+        boolean created = false;
+        synchronized (providerHolder) {
+            provider = providerHolder.get();
+            if (provider != null) {
+                return provider;
+            }
+
+            provider = Security.getProvider(getName());
+            if (provider == null) {
+                provider = createProviderInstance(providerClassName);
+                created = true;
+            }
+            providerHolder.set(provider);
+        }
+
+        if (created) {
+            log.info("getOrCreateProvider({}) created instance of {}", getName(), providerClassName);
+        } else {
+            log.info("getOrCreateProvider({}) resolved instance of {}", getName(), provider.getClass().getName());
+        }
+
+        return provider;
+    }
+
+    protected Provider createProviderInstance(String providerClassName) throws ReflectiveOperationException {
+        return SecurityProviderChoice.createProviderInstance(getClass(), providerClassName);
+    }
+
+    @Override
+    public String toString() {
+        return getClass().getSimpleName() + "[" + getName() + "]";
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityEntityFactory.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityEntityFactory.java b/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityEntityFactory.java
new file mode 100644
index 0000000..94b9454
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityEntityFactory.java
@@ -0,0 +1,194 @@
+/*
+ * 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.security;
+
+import java.lang.reflect.Method;
+import java.security.GeneralSecurityException;
+import java.security.Provider;
+import java.util.Objects;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * @param <T> Type of security entity being generated by this factory
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface SecurityEntityFactory<T> {
+    Class<T> getEntityType();
+
+    T getInstance(String algorithm) throws GeneralSecurityException;
+
+    /**
+     * Uses reflection in order to wrap the {@code getInstance} method(s)
+     * as a security entity factory.
+     *
+     * @param <F> Type of entity being generated by the factor
+     * @param entityType The entity type class
+     * @param registrar The {@code SecurityProviderRegistrar} to use - if
+     * {@code null} then default provider is used (if specified).
+     * @param defaultProvider Default provider choice to use if no registrar
+     * provided. If {@code null}/empty then JCE default is used
+     * @return The {@link SecurityEntityFactory} for the entity
+     * @throws ReflectiveOperationException If failed to create the factory
+     * @see #toDefaultFactory(Class)
+     * @see #toNamedProviderFactory(Class, String)
+     * @see #toProviderInstanceFactory(Class, Provider)
+     * @see SecurityProviderChoice#isNamedProviderUsed()
+     * @see SecurityProviderChoice#getSecurityProvider()
+     */
+    static <F> SecurityEntityFactory<F> toFactory(
+            Class<F> entityType, SecurityProviderChoice registrar, SecurityProviderChoice defaultProvider)
+            throws ReflectiveOperationException {
+        if (registrar == null) {
+            if ((defaultProvider == null) || (defaultProvider == SecurityProviderChoice.EMPTY)) {
+                return toDefaultFactory(entityType);
+            } else if (defaultProvider.isNamedProviderUsed()) {
+                return toNamedProviderFactory(entityType, defaultProvider.getName());
+            } else {
+                return toProviderInstanceFactory(entityType, defaultProvider.getSecurityProvider());
+            }
+        } else if (registrar.isNamedProviderUsed()) {
+            return toNamedProviderFactory(entityType, registrar.getName());
+        } else {
+            return toProviderInstanceFactory(entityType, registrar.getSecurityProvider());
+        }
+    }
+
+    static <F> SecurityEntityFactory<F> toDefaultFactory(Class<F> entityType)
+            throws ReflectiveOperationException {
+        Method m = entityType.getDeclaredMethod("getInstance", String.class);
+        return new SecurityEntityFactory<F>() {
+            private final String s = SecurityEntityFactory.class.getSimpleName()
+                    + "[" + entityType.getSimpleName() + "]"
+                    + "[default]";
+
+            @Override
+            public Class<F> getEntityType() {
+                return entityType;
+            }
+
+            @Override
+            public F getInstance(String algorithm) throws GeneralSecurityException {
+                try {
+                    Object value = m.invoke(null, algorithm);
+                    return entityType.cast(value);
+                } catch (ReflectiveOperationException t) {
+                    Throwable e = GenericUtils.peelException(t);
+                    if (e instanceof GeneralSecurityException) {
+                        throw (GeneralSecurityException) e;
+                    } else if (e instanceof RuntimeException) {
+                        throw (RuntimeException) e;
+                    } else if (e instanceof Error) {
+                        throw (Error) e;
+                    } else {
+                        throw new GeneralSecurityException(e);
+                    }
+                }
+            }
+
+            @Override
+            public String toString() {
+                return s;
+            }
+        };
+    }
+
+    static <F> SecurityEntityFactory<F> toNamedProviderFactory(Class<F> entityType, String name)
+            throws ReflectiveOperationException {
+        ValidateUtils.checkNotNullAndNotEmpty(name, "No provider name specified");
+        Method m = entityType.getDeclaredMethod("getInstance", String.class, String.class);
+        return new SecurityEntityFactory<F>() {
+            private final String s = SecurityEntityFactory.class.getSimpleName()
+                    + "[" + entityType.getSimpleName() + "]"
+                    + "[" + name + "]";
+
+            @Override
+            public Class<F> getEntityType() {
+                return entityType;
+            }
+
+            @Override
+            public F getInstance(String algorithm) throws GeneralSecurityException {
+                try {
+                    Object value = m.invoke(null, algorithm, name);
+                    return entityType.cast(value);
+                } catch (ReflectiveOperationException t) {
+                    Throwable e = GenericUtils.peelException(t);
+                    if (e instanceof GeneralSecurityException) {
+                        throw (GeneralSecurityException) e;
+                    } else if (e instanceof RuntimeException) {
+                        throw (RuntimeException) e;
+                    } else if (e instanceof Error) {
+                        throw (Error) e;
+                    } else {
+                        throw new GeneralSecurityException(e);
+                    }
+                }
+            }
+
+            @Override
+            public String toString() {
+                return s;
+            }
+        };
+    }
+
+    static <F> SecurityEntityFactory<F> toProviderInstanceFactory(Class<F> entityType, Provider provider)
+            throws ReflectiveOperationException {
+        Objects.requireNonNull(provider, "No provider instance");
+        Method m = entityType.getDeclaredMethod("getInstance", String.class, Provider.class);
+        return new SecurityEntityFactory<F>() {
+            private final String s = SecurityEntityFactory.class.getSimpleName()
+                    + "[" + entityType.getSimpleName() + "]"
+                    + "[" + Provider.class.getSimpleName() + "]"
+                    + "[" + provider.getName() + "]";
+
+            @Override
+            public Class<F> getEntityType() {
+                return entityType;
+            }
+
+            @Override
+            public F getInstance(String algorithm) throws GeneralSecurityException {
+                try {
+                    Object value = m.invoke(null, algorithm, provider);
+                    return entityType.cast(value);
+                } catch (ReflectiveOperationException t) {
+                    Throwable e = GenericUtils.peelException(t);
+                    if (e instanceof GeneralSecurityException) {
+                        throw (GeneralSecurityException) e;
+                    } else if (e instanceof RuntimeException) {
+                        throw (RuntimeException) e;
+                    } else if (e instanceof Error) {
+                        throw (Error) e;
+                    } else {
+                        throw new GeneralSecurityException(e);
+                    }
+                }
+            }
+
+            @Override
+            public String toString() {
+                return s;
+            }
+        };
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityProviderChoice.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityProviderChoice.java b/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityProviderChoice.java
new file mode 100644
index 0000000..c12e747
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityProviderChoice.java
@@ -0,0 +1,130 @@
+/*
+ * 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.security;
+
+import java.security.Provider;
+import java.util.Objects;
+
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.threads.ThreadUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface SecurityProviderChoice extends NamedResource {
+    SecurityProviderChoice EMPTY = new SecurityProviderChoice() {
+        @Override
+        public String getName() {
+            return null;
+        }
+
+        @Override
+        public boolean isNamedProviderUsed() {
+            return false;
+        }
+
+        @Override
+        public Provider getSecurityProvider() {
+            return null;
+        }
+
+        @Override
+        public String toString() {
+            return "EMPTY";
+        }
+    };
+
+    /**
+     * @return {@code true} if to use the provider's name rather than its
+     * {@link Provider} instance - default={@code true}.
+     */
+    default boolean isNamedProviderUsed() {
+        return true;
+    }
+
+    /**
+     * @return The security {@link Provider} to use in case {@link #isNamedProviderUsed()}
+     * is {@code false}. Can be {@code null} if {@link #isNamedProviderUsed()} is {@code true},
+     * but not recommended.
+     */
+    Provider getSecurityProvider();
+
+    static SecurityProviderChoice toSecurityProviderChoice(String name) {
+        ValidateUtils.checkNotNullAndNotEmpty(name, "No name provided");
+        return new SecurityProviderChoice() {
+            private final String s = SecurityProviderChoice.class.getSimpleName() + "[" + name + "]";
+
+            @Override
+            public String getName() {
+                return name;
+            }
+
+            @Override
+            public boolean isNamedProviderUsed() {
+                return true;
+            }
+
+            @Override
+            public Provider getSecurityProvider() {
+                return null;
+            }
+
+            @Override
+            public String toString() {
+                return s;
+            }
+        };
+    }
+
+    static SecurityProviderChoice toSecurityProviderChoice(Provider provider) {
+        Objects.requireNonNull(provider, "No provider instance");
+        return new SecurityProviderChoice() {
+            private final String s = SecurityProviderChoice.class.getSimpleName()
+                    + "[" + Provider.class.getSimpleName() + "]"
+                    + "[" + provider.getName() + "]";
+
+            @Override
+            public String getName() {
+                return provider.getName();
+            }
+
+            @Override
+            public boolean isNamedProviderUsed() {
+                return false;
+            }
+
+            @Override
+            public Provider getSecurityProvider() {
+                return provider;
+            }
+
+            @Override
+            public String toString() {
+                return s;
+            }
+        };
+    }
+
+    static Provider createProviderInstance(Class<?> anchor, String providerClassName)
+            throws ReflectiveOperationException {
+        return ThreadUtils.createDefaultInstance(anchor, Provider.class, providerClassName);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityProviderRegistrar.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityProviderRegistrar.java b/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityProviderRegistrar.java
new file mode 100644
index 0000000..31e428b
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityProviderRegistrar.java
@@ -0,0 +1,337 @@
+/*
+ * 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.security;
+
+import java.security.KeyFactory;
+import java.security.KeyPairGenerator;
+import java.security.MessageDigest;
+import java.security.Provider;
+import java.security.Security;
+import java.security.Signature;
+import java.security.cert.CertificateFactory;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.Predicate;
+
+import javax.crypto.Cipher;
+import javax.crypto.KeyAgreement;
+import javax.crypto.Mac;
+
+import org.apache.sshd.common.OptionalFeature;
+import org.apache.sshd.common.PropertyResolver;
+import org.apache.sshd.common.PropertyResolverUtils;
+import org.apache.sshd.common.SyspropsMapWrapper;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.IgnoringEmptyMap;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface SecurityProviderRegistrar extends SecurityProviderChoice, OptionalFeature, PropertyResolver {
+    /**
+     * Base name for configuration properties related to security providers
+     */
+    String CONFIG_PROP_BASE = "org.apache.sshd.security.provider";
+
+    /**
+     * Property used to configure whether the provider is enabled regardless of
+     * whether it is supported.
+     *
+     * @see #isEnabled()
+     */
+    String ENABLED_PROPERTY = "enabled";
+
+    /**
+     * Property used to configure whether to use the provider's name rather than its
+     * {@link Provider} instance
+     *
+     * @see #isNamedProviderUsed()
+     */
+    String NAMED_PROVIDER_PROPERTY = "useNamed";
+
+    String ALL_OPTIONS_VALUE = "all";
+    String ALL_OPTIONS_WILDCARD = "*";
+
+    String NO_OPTIONS_VALUE = "none";
+
+    /**
+     * All the entities that are used in calls to {@link #isSecurityEntitySupported(Class, String)}
+     */
+    List<Class<?>> SECURITY_ENTITIES =
+            Collections.unmodifiableList(
+                    Arrays.asList(
+                            Cipher.class, KeyFactory.class, MessageDigest.class,
+                            KeyPairGenerator.class, KeyAgreement.class, Mac.class,
+                            Signature.class, CertificateFactory.class));
+
+    default String getBasePropertyName() {
+        return CONFIG_PROP_BASE + "." + getName();
+    }
+
+    default String getConfigurationPropertyName(String name) {
+        return getBasePropertyName() + "." + name;
+    }
+
+    /**
+     * @return {@code true} if the provider is enabled regardless of
+     * whether it is supported - default={@code true}. <B>Note:</B>
+     * checks if the provider has been <U>programmatically</U> disabled
+     * via {@link SecurityUtils#setAPrioriDisabledProvider(String, boolean)}
+     * @see #ENABLED_PROPERTY
+     */
+    default boolean isEnabled() {
+        if (SecurityUtils.isAPrioriDisabledProvider(getName())) {
+            return false;
+        }
+
+        return this.getBooleanProperty(getConfigurationPropertyName(ENABLED_PROPERTY), true);
+    }
+
+    @Override
+    default PropertyResolver getParentPropertyResolver() {
+        return SyspropsMapWrapper.SYSPROPS_RESOLVER;
+    }
+
+    @Override
+    default Map<String, Object> getProperties() {
+        return IgnoringEmptyMap.getInstance();
+    }
+
+    /**
+     * @param transformation The requested {@link Cipher} transformation
+     * @return {@code true} if this security provider supports the transformation
+     * @see #isSecurityEntitySupported(Class, String)
+     */
+    default boolean isCipherSupported(String transformation) {
+        return isSecurityEntitySupported(Cipher.class, transformation);
+    }
+
+    /**
+     * @param algorithm The {@link KeyFactory} algorithm
+     * @return {@code true} if this security provider supports the algorithm
+     * @see #isSecurityEntitySupported(Class, String)
+     */
+    default boolean isKeyFactorySupported(String algorithm) {
+        return isSecurityEntitySupported(KeyFactory.class, algorithm);
+    }
+
+    /**
+     * @param algorithm The {@link MessageDigest} algorithm
+     * @return {@code true} if this security provider supports the algorithm
+     * @see #isSecurityEntitySupported(Class, String)
+     */
+    default boolean isMessageDigestSupported(String algorithm) {
+        return isSecurityEntitySupported(MessageDigest.class, algorithm);
+    }
+
+    /**
+     * @param algorithm The {@link KeyPairGenerator} algorithm
+     * @return {@code true} if this security provider supports the algorithm
+     * @see #isSecurityEntitySupported(Class, String)
+     */
+    default boolean isKeyPairGeneratorSupported(String algorithm) {
+        return isSecurityEntitySupported(KeyPairGenerator.class, algorithm);
+    }
+
+    /**
+     * @param algorithm The {@link KeyAgreement} algorithm
+     * @return {@code true} if this security provider supports the algorithm
+     * @see #isSecurityEntitySupported(Class, String)
+     */
+    default boolean isKeyAgreementSupported(String algorithm) {
+        return isSecurityEntitySupported(KeyAgreement.class, algorithm);
+    }
+
+    /**
+     * @param algorithm The {@link Mac} algorithm
+     * @return {@code true} if this security provider supports the algorithm
+     * @see #isSecurityEntitySupported(Class, String)
+     */
+    default boolean isMacSupported(String algorithm) {
+        return isSecurityEntitySupported(Mac.class, algorithm);
+    }
+
+    /**
+     * @param algorithm The {@link Signature} algorithm
+     * @return {@code true} if this security provider supports the algorithm
+     * @see #isSecurityEntitySupported(Class, String)
+     */
+    default boolean isSignatureSupported(String algorithm) {
+        return isSecurityEntitySupported(Signature.class, algorithm);
+    }
+
+    /**
+     * @param type The {@link CertificateFactory} type
+     * @return {@code true} if this security provider supports the algorithm
+     * @see #isSecurityEntitySupported(Class, String)
+     */
+    default boolean isCertificateFactorySupported(String type) {
+        return isSecurityEntitySupported(CertificateFactory.class, type);
+    }
+
+    /**
+     * @param entityType The requested entity type - its simple name serves to
+     * build the configuration property name.
+     * @return Configuration value to use if no specific configuration provided
+     * - default=empty
+     * @see #isSecurityEntitySupported(Class, String)
+     */
+    default String getDefaultSecurityEntitySupportValue(Class<?> entityType) {
+        return "";
+    }
+
+    default boolean isSecurityEntitySupported(Class<?> entityType, String name) {
+        String defaultValue = getDefaultSecurityEntitySupportValue(entityType);
+        return isSecurityEntitySupported(this, entityType, name, defaultValue);
+    }
+
+    /**
+     * @return {@code true} if to use the provider's name rather than its
+     * {@link Provider} instance - default={@code true}
+     * @see #NAMED_PROVIDER_PROPERTY
+     * @see #getSecurityProvider()
+     * @see #registerSecurityProvider(SecurityProviderRegistrar)
+     */
+    @Override
+    default boolean isNamedProviderUsed() {
+        return PropertyResolverUtils.getBooleanProperty(this,
+                getConfigurationPropertyName(NAMED_PROVIDER_PROPERTY),
+                SecurityProviderChoice.super.isNamedProviderUsed());
+    }
+
+    /**
+     * @param v Value to be examined
+     * @return {@code true} if the value equals (case insensitive) to
+     * either {@link #ALL_OPTIONS_VALUE} or {@link #ALL_OPTIONS_WILDCARD}
+     */
+    static boolean isAllOptionsValue(String v) {
+        return ALL_OPTIONS_VALUE.equalsIgnoreCase(v)
+            || ALL_OPTIONS_WILDCARD.equalsIgnoreCase(v);
+    }
+
+    /**
+     * Checks whether the requested entity type algorithm/name is listed
+     * as supported by the registrar's configuration
+     *
+     * @param registrar The {@link SecurityProviderRegistrar}
+     * @param entityType The requested entity type - its simple name serves to
+     * build the configuration property name.
+     * @param name The requested algorithm/name - <B>Note:</B> if the requested
+     * entity is a {@link Cipher} then the argument is assumed to be a possible
+     * &quot;/&quot; separated transformation and parsed as such in order to
+     * retrieve the pure cipher name
+     * @param defaultValue Configuration value to use if no specific configuration provided
+     * @return {@code true} registrar is supported and the value is listed
+     * (case <U>insensitive</U>) or * the property is one of the &quot;all&quot; markers
+     * @see SecurityProviderRegistrar#isSupported()
+     * @see #isAllOptionsValue(String)
+     */
+    static boolean isSecurityEntitySupported(SecurityProviderRegistrar registrar, Class<?> entityType, String name, String defaultValue) {
+        return Objects.requireNonNull(registrar, "No registrar instance").isSupported()
+            && isSecurityEntitySupported(registrar, registrar.getConfigurationPropertyName(entityType.getSimpleName()), entityType, name, defaultValue);
+    }
+
+    static boolean isSecurityEntitySupported(PropertyResolver resolver, String propName, Class<?> entityType, String name, String defaultValue) {
+        if (GenericUtils.isEmpty(name)) {
+            return false;
+        }
+
+        String propValue = resolver.getString(propName);
+        if (GenericUtils.isEmpty(propValue)) {
+            propValue = defaultValue;
+        }
+
+        if (NO_OPTIONS_VALUE.equalsIgnoreCase(propValue)) {
+            return false;
+        }
+
+        String[] values = GenericUtils.split(propValue, ',');
+        if (GenericUtils.isEmpty(values)) {
+            return false;
+        }
+
+        if ((values.length == 1) && isAllOptionsValue(values[0])) {
+            return true;
+        }
+
+        String effectiveName = getEffectiveSecurityEntityName(entityType, name);
+        int index = Arrays.binarySearch(values, effectiveName, String.CASE_INSENSITIVE_ORDER);
+        return index >= 0;
+    }
+
+    /**
+     * Determines the &quot;pure&quot; security entity name - e.g., for {@link Cipher}s
+     * it strips the trailing transformation specification in order to extract the
+     * base cipher name - e.g., &quot;AES/CBC/NoPadding&quot; =&gt; &quot;AES&quot;
+     *
+     * @param entityType The security entity type - ignored if {@code null}
+     * @param name The effective name - ignored if {@code null}/empty
+     * @return The resolved name
+     */
+    static String getEffectiveSecurityEntityName(Class<?> entityType, String name) {
+        if ((entityType == null) || GenericUtils.isEmpty(name) || (!Cipher.class.isAssignableFrom(entityType))) {
+            return name;
+        }
+
+        int pos = name.indexOf('/');
+        return (pos > 0) ? name.substring(0, pos) : name;
+    }
+
+    /**
+     * Attempts to register the security provider represented by the registrar
+     * if not already registered. <B>Note:</B> if {@link SecurityProviderRegistrar#isNamedProviderUsed()}
+     * is {@code true} then the generated provider will be added to the system's
+     * list of known providers.
+     *
+     * @param registrar The {@link SecurityProviderRegistrar}
+     * @return {@code true} if no provider was previously registered
+     * @see Security#getProvider(String)
+     * @see SecurityProviderRegistrar#getSecurityProvider()
+     * @see Security#addProvider(Provider)
+     */
+    static boolean registerSecurityProvider(SecurityProviderRegistrar registrar) {
+        String name = ValidateUtils.checkNotNullAndNotEmpty(
+                (registrar == null) ? null : registrar.getName(), "No name for registrar=%s", registrar);
+        Provider p = Security.getProvider(name);
+        if (p != null) {
+            return false;
+        }
+
+        p = ValidateUtils.checkNotNull(
+                registrar.getSecurityProvider(), "No provider created for registrar of %s", name);
+        if (registrar.isNamedProviderUsed()) {
+            Security.addProvider(p);
+        }
+
+        return true;
+    }
+
+    static SecurityProviderRegistrar findSecurityProviderRegistrarBySecurityEntity(
+            Predicate<? super SecurityProviderRegistrar> entitySelector,
+            Collection<? extends SecurityProviderRegistrar> registrars) {
+        return GenericUtils.findFirstMatchingMember(
+            r -> r.isEnabled() && r.isSupported() && entitySelector.test(r), registrars);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java
new file mode 100644
index 0000000..ee755e6
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java
@@ -0,0 +1,759 @@
+/*
+ * 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.security;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigInteger;
+import java.nio.file.Path;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.cert.CertificateFactory;
+import java.security.spec.InvalidKeySpecException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Predicate;
+
+import javax.crypto.Cipher;
+import javax.crypto.KeyAgreement;
+import javax.crypto.Mac;
+import javax.crypto.spec.DHParameterSpec;
+
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.config.keys.PrivateKeyEntryDecoder;
+import org.apache.sshd.common.config.keys.PublicKeyEntryDecoder;
+import org.apache.sshd.common.config.keys.loader.KeyPairResourceParser;
+import org.apache.sshd.common.config.keys.loader.openssh.OpenSSHKeyPairResourceParser;
+import org.apache.sshd.common.config.keys.loader.pem.PEMResourceParserUtils;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.random.JceRandomFactory;
+import org.apache.sshd.common.random.RandomFactory;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.common.util.security.bouncycastle.BouncyCastleGeneratorHostKeyProvider;
+import org.apache.sshd.common.util.security.bouncycastle.BouncyCastleKeyPairResourceParser;
+import org.apache.sshd.common.util.security.bouncycastle.BouncyCastleRandomFactory;
+import org.apache.sshd.common.util.security.eddsa.EdDSASecurityProviderUtils;
+import org.apache.sshd.common.util.threads.ThreadUtils;
+import org.apache.sshd.server.keyprovider.AbstractGeneratorHostKeyProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Specific security providers related code
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class SecurityUtils {
+    /**
+     * Bouncycastle JCE provider name
+     */
+    public static final String BOUNCY_CASTLE = "BC";
+
+    /**
+     * EDDSA support - should match {@code EdDSAKey.KEY_ALGORITHM}
+     */
+    public static final String EDDSA = "EdDSA";
+
+    // A copy-paste from the original, but we don't want to drag the classes into the classpath
+    // See EdDSAEngine.SIGNATURE_ALGORITHM
+    public static final String CURVE_ED25519_SHA512 = "NONEwithEdDSA";
+
+    /**
+     * System property used to configure the value for the maximum supported Diffie-Hellman
+     * Group Exchange key size. If not set, then an internal auto-discovery mechanism is employed.
+     * If set to negative value then Diffie-Hellman Group Exchange is disabled. If set to a
+     * negative value then Diffie-Hellman Group Exchange is disabled
+     */
+    public static final String MAX_DHGEX_KEY_SIZE_PROP = "org.apache.sshd.maxDHGexKeySize";
+
+    /**
+     * The min. key size value used for testing whether Diffie-Hellman Group Exchange
+     * is supported or not. According to <A HREF="https://tools.ietf.org/html/rfc4419">RFC 4419</A>
+     * section 3: &quot;Servers and clients SHOULD support groups with a modulus length of k
+     * bits, where 1024 <= k <= 8192&quot;.
+     * </code>
+     */
+    public static final int MIN_DHGEX_KEY_SIZE = 1024;
+    // Keys of size > 1024 are not support by default with JCE
+    public static final int DEFAULT_DHGEX_KEY_SIZE = MIN_DHGEX_KEY_SIZE;
+    public static final int PREFERRED_DHGEX_KEY_SIZE = 4096;
+    public static final int MAX_DHGEX_KEY_SIZE = 8192;
+
+    /**
+     * Comma separated list of fully qualified {@link SecurityProviderRegistrar}s
+     * to automatically register
+     */
+    public static final String SECURITY_PROVIDER_REGISTRARS = "org.apache.sshd.security.registrars";
+    public static final List<String> DEFAULT_SECURITY_PROVIDER_REGISTRARS =
+            Collections.unmodifiableList(
+                    Arrays.asList(
+                            "org.apache.sshd.common.util.security.bouncycastle.BouncyCastleSecurityProviderRegistrar",
+                            "org.apache.sshd.common.util.security.eddsa.EdDSASecurityProviderRegistrar"));
+
+
+    /**
+     * System property used to control whether to automatically register the
+     * {@code Bouncyastle} JCE provider
+     * @deprecated Please use &quot;org.apache.sshd.security.provider.BC.enabled&quot;
+     */
+    @Deprecated
+    public static final String REGISTER_BOUNCY_CASTLE_PROP = "org.apache.sshd.registerBouncyCastle";
+
+    /**
+     * System property used to control whether Elliptic Curves are supported or not.
+     * If not set then the support is auto-detected. <B>Note:</B> if set to {@code true}
+     * it is up to the user to make sure that indeed there is a provider for them
+     */
+    public static final String ECC_SUPPORTED_PROP = "org.apache.sshd.eccSupport";
+
+    /**
+     * System property used to decide whether EDDSA curves are supported or not
+     * (in addition or even in spite of {@link #isEDDSACurveSupported()}). If not
+     * set or set to {@code true}, then the existence of the optional support classes
+     * determines the support.
+     * @deprecated Please use &quot;org.apache.sshd.security.provider.EdDSA.enabled&qupt;
+     */
+    @Deprecated
+    public static final String EDDSA_SUPPORTED_PROP = "org.apache.sshd.eddsaSupport";
+
+    public static final String PROP_DEFAULT_SECURITY_PROVIDER = "org.apache.sshd.security.defaultProvider";
+
+    private static final AtomicInteger MAX_DHG_KEY_SIZE_HOLDER = new AtomicInteger(0);
+
+    /*
+     * NOTE: we use a LinkedHashMap in order to preserve registration order
+     * in case several providers support the same security entity
+     */
+    private static final Map<String, SecurityProviderRegistrar> REGISTERED_PROVIDERS = new LinkedHashMap<>();
+    private static final AtomicReference<KeyPairResourceParser> KEYPAIRS_PARSER_HODLER = new AtomicReference<>();
+    // If an entry already exists for the named provider, then it overrides its SecurityProviderRegistrar#isEnabled()
+    private static final Set<String> APRIORI_DISABLED_PROVIDERS = new TreeSet<>();
+    private static final AtomicBoolean REGISTRATION_STATE_HOLDER = new AtomicBoolean(false);
+    private static final Map<Class<?>, Map<String, SecurityEntityFactory<?>>> SECURITY_ENTITY_FACTORIES = new HashMap<>();
+
+    private static final AtomicReference<SecurityProviderChoice> DEFAULT_PROVIDER_HOLDER = new AtomicReference<>();
+
+    private static Boolean hasEcc;
+
+    private SecurityUtils() {
+        throw new UnsupportedOperationException("No instance");
+    }
+
+    /**
+     * @param name The provider's name - never {@code null}/empty
+     * @return {@code true} if the provider is marked as disabled a-priori
+     * @see #setAPrioriDisabledProvider(String, boolean)
+     */
+    public static boolean isAPrioriDisabledProvider(String name) {
+        ValidateUtils.checkNotNullAndNotEmpty(name, "No provider name specified");
+        synchronized (APRIORI_DISABLED_PROVIDERS) {
+            return APRIORI_DISABLED_PROVIDERS.contains(name);
+        }
+    }
+
+    /**
+     * Marks a provider's registrar as &quot;a-priori&quot; <U>programatically</U>
+     * so that when its {@link SecurityProviderRegistrar#isEnabled()} is eventually
+     * consulted it will return {@code false} regardless of the configured value for
+     * the specific provider registrar instance. <B>Note:</B> has no effect if the
+     * provider has already been registered.
+     *
+     * @param name The provider's name - never {@code null}/empty
+     * @param disabled {@code true} whether to disable it a-priori
+     * @see #isAPrioriDisabledProvider(String)
+     */
+    public static void setAPrioriDisabledProvider(String name, boolean disabled) {
+        ValidateUtils.checkNotNullAndNotEmpty(name, "No provider name specified");
+        synchronized (APRIORI_DISABLED_PROVIDERS) {
+            if (disabled) {
+                APRIORI_DISABLED_PROVIDERS.add(name);
+            } else {
+                APRIORI_DISABLED_PROVIDERS.remove(name);
+            }
+        }
+    }
+
+    /**
+     * @return A <U>copy</U> if the current a-priori disabled providers names
+     */
+    public static Set<String> getAPrioriDisabledProviders() {
+        synchronized (APRIORI_DISABLED_PROVIDERS) {
+            return new TreeSet<>(APRIORI_DISABLED_PROVIDERS);
+        }
+    }
+
+    /**
+     * @return {@code true} if Elliptic Curve Cryptography is supported
+     * @see #ECC_SUPPORTED_PROP
+     */
+    public static boolean isECCSupported() {
+        if (hasEcc == null) {
+            String propValue = System.getProperty(ECC_SUPPORTED_PROP);
+            if (GenericUtils.isEmpty(propValue)) {
+                try {
+                    getKeyPairGenerator(KeyUtils.EC_ALGORITHM);
+                    hasEcc = Boolean.TRUE;
+                } catch (Throwable t) {
+                    hasEcc = Boolean.FALSE;
+                }
+            } else {
+                Logger logger = LoggerFactory.getLogger(SecurityUtils.class);
+                logger.info("Override ECC support value: " + propValue);
+                hasEcc = Boolean.valueOf(propValue);
+            }
+        }
+
+        return hasEcc;
+    }
+
+    /**
+     * @return {@code true} if Diffie-Hellman Group Exchange is supported
+     * @see #getMaxDHGroupExchangeKeySize()
+     */
+    public static boolean isDHGroupExchangeSupported() {
+        return getMaxDHGroupExchangeKeySize() > 0;
+    }
+
+    /**
+     * @param keySize The expected key size
+     * @return {@code true} if Oakely Diffie-Hellman Group Exchange is supported
+     * for the specified key size
+     * @see #getMaxDHGroupExchangeKeySize()
+     */
+    public static boolean isDHOakelyGroupSupported(int keySize) {
+        return getMaxDHGroupExchangeKeySize() >= keySize;
+    }
+
+    /**
+     * @return The maximum supported Diffie-Hellman Group Exchange key size,
+     * or non-positive if not supported
+     */
+    public static int getMaxDHGroupExchangeKeySize() {
+        int maxSupportedKeySize;
+        synchronized (MAX_DHG_KEY_SIZE_HOLDER) {
+            maxSupportedKeySize = MAX_DHG_KEY_SIZE_HOLDER.get();
+            if (maxSupportedKeySize != 0) { // 1st time we are called ?
+                return maxSupportedKeySize;
+            }
+
+            String propValue = System.getProperty(MAX_DHGEX_KEY_SIZE_PROP);
+            if (GenericUtils.isEmpty(propValue)) {
+                maxSupportedKeySize = -1;
+                // Go down from max. to min. to ensure we stop at 1st maximum value success
+                for (int testKeySize = MAX_DHGEX_KEY_SIZE; testKeySize >= MIN_DHGEX_KEY_SIZE; testKeySize -= 1024) {
+                    if (isDHGroupExchangeSupported(testKeySize)) {
+                        maxSupportedKeySize = testKeySize;
+                        break;
+                    }
+                }
+            } else {
+                Logger logger = LoggerFactory.getLogger(SecurityUtils.class);
+                logger.info("Override max. DH group exchange key size: " + propValue);
+                maxSupportedKeySize = Integer.parseInt(propValue);
+                // negative is OK - means user wants to disable DH group exchange
+                ValidateUtils.checkTrue(maxSupportedKeySize != 0,
+                        "Configured " + MAX_DHGEX_KEY_SIZE_PROP + " value must be non-zero: %d", maxSupportedKeySize);
+            }
+
+            MAX_DHG_KEY_SIZE_HOLDER.set(maxSupportedKeySize);
+        }
+
+        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);
+
+        try {
+            BigInteger r = new BigInteger("0").setBit(maxKeySize - 1);
+            DHParameterSpec dhSkipParamSpec = new DHParameterSpec(r, r);
+            KeyPairGenerator kpg = getKeyPairGenerator("DH");
+            kpg.initialize(dhSkipParamSpec);
+            return true;
+        } catch (GeneralSecurityException t) {
+            return false;
+        }
+    }
+
+    public static SecurityProviderChoice getDefaultProviderChoice() {
+        SecurityProviderChoice choice;
+        synchronized (DEFAULT_PROVIDER_HOLDER) {
+            choice = DEFAULT_PROVIDER_HOLDER.get();
+            if (choice != null) {
+                return choice;
+            }
+
+            String name = System.getProperty(PROP_DEFAULT_SECURITY_PROVIDER);
+            choice = (GenericUtils.isEmpty(name) || "none".equalsIgnoreCase(name))
+                    ? SecurityProviderChoice.EMPTY
+                    : SecurityProviderChoice.toSecurityProviderChoice(name);
+            DEFAULT_PROVIDER_HOLDER.set(choice);
+        }
+
+        return choice;
+    }
+
+    public static void setDefaultProviderChoice(SecurityProviderChoice choice) {
+        DEFAULT_PROVIDER_HOLDER.set(choice);
+    }
+
+    /**
+     * @return A <U>copy</U> of the currently registered security providers
+     */
+    public static Set<String> getRegisteredProviders() {
+        // returns a COPY of the providers in order to avoid modifications
+        synchronized (REGISTERED_PROVIDERS) {
+            return new TreeSet<>(REGISTERED_PROVIDERS.keySet());
+        }
+    }
+
+    public static boolean isBouncyCastleRegistered() {
+        register();
+        return isProviderRegistered(BOUNCY_CASTLE);
+    }
+
+    public static boolean isProviderRegistered(String provider) {
+        return getRegisteredProvider(provider) != null;
+    }
+
+    public static SecurityProviderRegistrar getRegisteredProvider(String provider) {
+        ValidateUtils.checkNotNullAndNotEmpty(provider, "No provider name specified");
+        synchronized (REGISTERED_PROVIDERS) {
+            return REGISTERED_PROVIDERS.get(provider);
+        }
+    }
+
+    public static boolean isRegistrationCompleted() {
+        return REGISTRATION_STATE_HOLDER.get();
+    }
+
+    private static void register() {
+        synchronized (REGISTRATION_STATE_HOLDER) {
+            if (REGISTRATION_STATE_HOLDER.get()) {
+                return;
+            }
+
+            String regsList = System.getProperty(SECURITY_PROVIDER_REGISTRARS,
+                    GenericUtils.join(DEFAULT_SECURITY_PROVIDER_REGISTRARS, ','));
+            boolean bouncyCastleRegistered = false;
+            if ((GenericUtils.length(regsList) > 0) && (!"none".equalsIgnoreCase(regsList))) {
+                String[] classes = GenericUtils.split(regsList, ',');
+                Logger logger = LoggerFactory.getLogger(SecurityUtils.class);
+                boolean debugEnabled = logger.isDebugEnabled();
+                for (String registrarClass : classes) {
+                    SecurityProviderRegistrar r;
+                    try {
+                        r = ThreadUtils.createDefaultInstance(SecurityUtils.class, SecurityProviderRegistrar.class, registrarClass);
+                    } catch (ReflectiveOperationException t) {
+                        Throwable e = GenericUtils.peelException(t);
+                        logger.error("Failed ({}) to create default {} registrar instance: {}",
+                                     e.getClass().getSimpleName(), registrarClass, e.getMessage());
+                        if (e instanceof RuntimeException) {
+                            throw (RuntimeException) e;
+                        } else if (e instanceof Error) {
+                            throw (Error) e;
+                        } else {
+                            throw new RuntimeException(e);
+                        }
+                    }
+
+                    String name = r.getName();
+                    SecurityProviderRegistrar registeredInstance = registerSecurityProvider(r);
+                    if (registeredInstance == null) {
+                        if (debugEnabled) {
+                            logger.debug("register({}) not registered - enabled={}, supported={}",
+                                         name, r.isEnabled(), r.isSupported());
+                        }
+                        continue;   // provider not registered - e.g., disabled, not supported
+                    }
+
+                    if (BOUNCY_CASTLE.equalsIgnoreCase(name)) {
+                        bouncyCastleRegistered = true;
+                    }
+                }
+            }
+
+            SecurityProviderChoice choice = getDefaultProviderChoice();
+            if (((choice == null) || (choice == SecurityProviderChoice.EMPTY)) && bouncyCastleRegistered) {
+                setDefaultProviderChoice(SecurityProviderChoice.toSecurityProviderChoice(BOUNCY_CASTLE));
+            }
+
+            REGISTRATION_STATE_HOLDER.set(true);
+        }
+    }
+
+    /**
+     * @param registrar The registrar instance to register
+     * @return The registered instance - may be different than required
+     * if already registered. Returns {@code null} if not already registered
+     * and not enabled or not supported registrar.
+     */
+    public static SecurityProviderRegistrar registerSecurityProvider(SecurityProviderRegistrar registrar) {
+        Objects.requireNonNull(registrar, "No registrar instance to register");
+        String name = registrar.getName();
+        SecurityProviderRegistrar registeredInstance = getRegisteredProvider(name);
+        if ((registeredInstance == null) && registrar.isEnabled() && registrar.isSupported()) {
+            try {
+                SecurityProviderRegistrar.registerSecurityProvider(registrar);
+                synchronized (REGISTERED_PROVIDERS) {
+                    REGISTERED_PROVIDERS.put(name, registrar);
+                }
+
+                return registrar;
+            } catch (Throwable t) {
+                Logger logger = LoggerFactory.getLogger(SecurityUtils.class);
+                logger.error("Failed {} to register {} as a JCE provider: {}",
+                             t.getClass().getSimpleName(), name, t.getMessage());
+                throw new RuntimeException("Failed to register " + name + " as a JCE provider", t);
+            }
+        }
+
+        return registeredInstance;
+    }
+
+    ///////////////// Bouncycastle specific implementations //////////////////
+
+    /* -------------------------------------------------------------------- */
+
+    /**
+     * @param resourceKey An identifier of the key being loaded - used as
+     *                    argument to the {@link FilePasswordProvider#getPassword(String)}
+     *                    invocation
+     * @param inputStream The {@link InputStream} for the <U>private</U> key
+     * @param provider    A {@link FilePasswordProvider} - may be {@code null}
+     *                    if the loaded key is <U>guaranteed</U> not to be encrypted
+     * @return The loaded {@link KeyPair}
+     * @throws IOException              If failed to read/parse the input stream
+     * @throws GeneralSecurityException If failed to generate the keys
+     */
+    public static KeyPair loadKeyPairIdentity(String resourceKey, InputStream inputStream, FilePasswordProvider provider)
+            throws IOException, GeneralSecurityException {
+        KeyPairResourceParser parser = getKeyPairResourceParser();
+        if (parser == null) {
+            throw new NoSuchProviderException("No registered key-pair resource parser");
+        }
+
+        Collection<KeyPair> ids = parser.loadKeyPairs(resourceKey, provider, inputStream);
+        int numLoaded = GenericUtils.size(ids);
+        if (numLoaded <= 0) {
+            throw new InvalidKeyException("Unsupported private key file format: " + resourceKey);
+        }
+        if (numLoaded != 1) {
+            throw new InvalidKeySpecException("Multiple private key pairs N/A: " + resourceKey);
+        }
+
+        return ids.iterator().next();
+    }
+
+    /* -------------------------------------------------------------------- */
+
+    public static AbstractGeneratorHostKeyProvider createGeneratorHostKeyProvider(Path path) {
+        ValidateUtils.checkTrue(isBouncyCastleRegistered(), "BouncyCastle not registered");
+        return new BouncyCastleGeneratorHostKeyProvider(path);
+    }
+
+    public static KeyPairResourceParser getBouncycastleKeyPairResourceParser() {
+        ValidateUtils.checkTrue(isBouncyCastleRegistered(), "BouncyCastle not registered");
+        return BouncyCastleKeyPairResourceParser.INSTANCE;
+    }
+
+    /**
+     * @return If {@link #isBouncyCastleRegistered()} then a {@link BouncyCastleRandomFactory}
+     * instance, otherwise a {@link JceRandomFactory} one
+     */
+    public static RandomFactory getRandomFactory() {
+        if (isBouncyCastleRegistered()) {
+            return BouncyCastleRandomFactory.INSTANCE;
+        } else {
+            return JceRandomFactory.INSTANCE;
+        }
+    }
+
+    ///////////////////////////// ED25519 support ///////////////////////////////
+
+    /**
+     * @return {@code true} if EDDSA curves (e.g., {@code ed25519}) are supported
+     */
+    public static boolean isEDDSACurveSupported() {
+        register();
+
+        SecurityProviderRegistrar r = getRegisteredProvider(EDDSA);
+        return (r != null) && r.isEnabled() && r.isSupported();
+    }
+
+    /* -------------------------------------------------------------------- */
+
+    public static PublicKeyEntryDecoder<? extends PublicKey, ? extends PrivateKey> getEDDSAPublicKeyEntryDecoder() {
+        if (!isEDDSACurveSupported()) {
+            throw new UnsupportedOperationException(EDDSA + " provider N/A");
+        }
+
+        return EdDSASecurityProviderUtils.getEDDSAPublicKeyEntryDecoder();
+    }
+
+    public static PrivateKeyEntryDecoder<? extends PublicKey, ? extends PrivateKey> getOpenSSHEDDSAPrivateKeyEntryDecoder() {
+        if (!isEDDSACurveSupported()) {
+            throw new UnsupportedOperationException(EDDSA + " provider N/A");
+        }
+
+        return EdDSASecurityProviderUtils.getOpenSSHEDDSAPrivateKeyEntryDecoder();
+    }
+
+    public static org.apache.sshd.common.signature.Signature getEDDSASigner() {
+        if (isEDDSACurveSupported()) {
+            return EdDSASecurityProviderUtils.getEDDSASignature();
+        }
+
+        throw new UnsupportedOperationException(EDDSA + " Signer not available");
+    }
+
+    public static int getEDDSAKeySize(Key key) {
+        return EdDSASecurityProviderUtils.getEDDSAKeySize(key);
+    }
+
+    public static Class<? extends PublicKey> getEDDSAPublicKeyType() {
+        return isEDDSACurveSupported() ? EdDSASecurityProviderUtils.getEDDSAPublicKeyType() : PublicKey.class;
+    }
+
+    public static Class<? extends PrivateKey> getEDDSAPrivateKeyType() {
+        return isEDDSACurveSupported() ? EdDSASecurityProviderUtils.getEDDSAPrivateKeyType() : PrivateKey.class;
+    }
+
+    public static boolean compareEDDSAPPublicKeys(PublicKey k1, PublicKey k2) {
+        return isEDDSACurveSupported() ? EdDSASecurityProviderUtils.compareEDDSAPPublicKeys(k1, k2) : false;
+    }
+
+    public static boolean compareEDDSAPrivateKeys(PrivateKey k1, PrivateKey k2) {
+        return isEDDSACurveSupported() ? EdDSASecurityProviderUtils.compareEDDSAPrivateKeys(k1, k2) : false;
+    }
+
+    public static PublicKey recoverEDDSAPublicKey(PrivateKey key) throws GeneralSecurityException {
+        if (!isEDDSACurveSupported()) {
+            throw new NoSuchAlgorithmException(EDDSA + " provider not supported");
+        }
+
+        return EdDSASecurityProviderUtils.recoverEDDSAPublicKey(key);
+    }
+
+    public static PublicKey generateEDDSAPublicKey(String keyType, byte[] seed) throws GeneralSecurityException {
+        if (!KeyPairProvider.SSH_ED25519.equals(keyType)) {
+            throw new InvalidKeyException("Unsupported key type: " + keyType);
+        }
+
+        if (!isEDDSACurveSupported()) {
+            throw new NoSuchAlgorithmException(EDDSA + " provider not supported");
+        }
+
+        return EdDSASecurityProviderUtils.generateEDDSAPublicKey(seed);
+    }
+
+    public static <B extends Buffer> B putRawEDDSAPublicKey(B buffer, PublicKey key) {
+        if (!isEDDSACurveSupported()) {
+            throw new UnsupportedOperationException(EDDSA + " provider not supported");
+        }
+
+        return EdDSASecurityProviderUtils.putRawEDDSAPublicKey(buffer, key);
+    }
+
+    public static <B extends Buffer> B putEDDSAKeyPair(B buffer, KeyPair kp) {
+        return putEDDSAKeyPair(buffer, Objects.requireNonNull(kp, "No key pair").getPublic(), kp.getPrivate());
+    }
+
+    public static <B extends Buffer> B putEDDSAKeyPair(B buffer, PublicKey pubKey, PrivateKey prvKey) {
+        if (!isEDDSACurveSupported()) {
+            throw new UnsupportedOperationException(EDDSA + " provider not supported");
+        }
+
+        return EdDSASecurityProviderUtils.putEDDSAKeyPair(buffer, pubKey, prvKey);
+    }
+
+    public static KeyPair extractEDDSAKeyPair(Buffer buffer, String keyType) throws GeneralSecurityException {
+        if (!KeyPairProvider.SSH_ED25519.equals(keyType)) {
+            throw new InvalidKeyException("Unsupported key type: " + keyType);
+        }
+
+        if (!isEDDSACurveSupported()) {
+            throw new NoSuchAlgorithmException(EDDSA + " provider not supported");
+        }
+
+        throw new GeneralSecurityException("Full SSHD-440 implementation N/A");
+    }
+
+    //////////////////////////////////////////////////////////////////////////
+
+    public static KeyPairResourceParser getKeyPairResourceParser() {
+        KeyPairResourceParser parser;
+        synchronized (KEYPAIRS_PARSER_HODLER) {
+            parser = KEYPAIRS_PARSER_HODLER.get();
+            if (parser != null) {
+                return parser;
+            }
+
+            parser = KeyPairResourceParser.aggregate(
+                    PEMResourceParserUtils.PROXY,
+                    OpenSSHKeyPairResourceParser.INSTANCE);
+            KEYPAIRS_PARSER_HODLER.set(parser);
+        }
+
+        return parser;
+    }
+
+    /**
+     * @param parser The system-wide {@code KeyPairResourceParser} to use.
+     * If set to {@code null}, then the default parser will be re-constructed
+     * on next call to {@link #getKeyPairResourceParser()}
+     */
+    public static void setKeyPairResourceParser(KeyPairResourceParser parser) {
+        synchronized (KEYPAIRS_PARSER_HODLER) {
+            KEYPAIRS_PARSER_HODLER.set(parser);
+        }
+    }
+
+    //////////////////////////// Security entities factories /////////////////////////////
+
+    @SuppressWarnings("unchecked")
+    public static <T> SecurityEntityFactory<T> resolveSecurityEntityFactory(
+            Class<T> entityType, String algorithm, Predicate<? super SecurityProviderRegistrar> entitySelector) {
+        Map<String, SecurityEntityFactory<?>> factoriesMap;
+        synchronized (SECURITY_ENTITY_FACTORIES) {
+            factoriesMap =
+                    SECURITY_ENTITY_FACTORIES.computeIfAbsent(
+                            entityType, k -> new TreeMap<>(String.CASE_INSENSITIVE_ORDER));
+        }
+
+        String effectiveName = SecurityProviderRegistrar.getEffectiveSecurityEntityName(entityType, algorithm);
+        SecurityEntityFactory<?> factoryEntry;
+        synchronized (factoriesMap) {
+            factoryEntry =
+                    factoriesMap.computeIfAbsent(
+                            effectiveName, k -> createSecurityEntityFactory(entityType, entitySelector));
+        }
+
+        return (SecurityEntityFactory<T>) factoryEntry;
+    }
+
+    public static <T> SecurityEntityFactory<T> createSecurityEntityFactory(
+            Class<T> entityType, Predicate<? super SecurityProviderRegistrar> entitySelector) {
+        register();
+
+        SecurityProviderRegistrar registrar;
+        synchronized (REGISTERED_PROVIDERS) {
+            registrar =
+                 SecurityProviderRegistrar.findSecurityProviderRegistrarBySecurityEntity(
+                         entitySelector, REGISTERED_PROVIDERS.values());
+        }
+
+        try {
+            return SecurityEntityFactory.toFactory(entityType, registrar, getDefaultProviderChoice());
+        } catch (ReflectiveOperationException t) {
+            Throwable e = GenericUtils.peelException(t);
+            if (e instanceof RuntimeException) {
+                throw (RuntimeException) e;
+            } else if (e instanceof Error) {
+                throw (Error) e;
+            } else {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    public static KeyFactory getKeyFactory(String algorithm) throws GeneralSecurityException {
+        SecurityEntityFactory<KeyFactory> factory =
+                resolveSecurityEntityFactory(KeyFactory.class, algorithm, r -> r.isKeyFactorySupported(algorithm));
+        return factory.getInstance(algorithm);
+    }
+
+    public static Cipher getCipher(String transformation) throws GeneralSecurityException {
+        SecurityEntityFactory<Cipher> factory =
+                resolveSecurityEntityFactory(Cipher.class, transformation, r -> r.isCipherSupported(transformation));
+        return factory.getInstance(transformation);
+    }
+
+    public static MessageDigest getMessageDigest(String algorithm) throws GeneralSecurityException {
+        SecurityEntityFactory<MessageDigest> factory =
+                resolveSecurityEntityFactory(MessageDigest.class, algorithm, r -> r.isMessageDigestSupported(algorithm));
+        return factory.getInstance(algorithm);
+    }
+
+    public static KeyPairGenerator getKeyPairGenerator(String algorithm) throws GeneralSecurityException {
+        SecurityEntityFactory<KeyPairGenerator> factory =
+                resolveSecurityEntityFactory(KeyPairGenerator.class, algorithm, r -> r.isKeyPairGeneratorSupported(algorithm));
+        return factory.getInstance(algorithm);
+    }
+
+    public static KeyAgreement getKeyAgreement(String algorithm) throws GeneralSecurityException {
+        SecurityEntityFactory<KeyAgreement> factory =
+                resolveSecurityEntityFactory(KeyAgreement.class, algorithm, r -> r.isKeyAgreementSupported(algorithm));
+        return factory.getInstance(algorithm);
+    }
+
+    public static Mac getMac(String algorithm) throws GeneralSecurityException {
+        SecurityEntityFactory<Mac> factory =
+                resolveSecurityEntityFactory(Mac.class, algorithm, r -> r.isMacSupported(algorithm));
+        return factory.getInstance(algorithm);
+    }
+
+    public static Signature getSignature(String algorithm) throws GeneralSecurityException {
+        SecurityEntityFactory<Signature> factory =
+                resolveSecurityEntityFactory(Signature.class, algorithm, r -> r.isSignatureSupported(algorithm));
+        return factory.getInstance(algorithm);
+    }
+
+    public static CertificateFactory getCertificateFactory(String type) throws GeneralSecurityException {
+        SecurityEntityFactory<CertificateFactory> factory =
+                resolveSecurityEntityFactory(CertificateFactory.class, type, r -> r.isCertificateFactorySupported(type));
+        return factory.getInstance(type);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleGeneratorHostKeyProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleGeneratorHostKeyProvider.java b/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleGeneratorHostKeyProvider.java
new file mode 100644
index 0000000..3716719
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleGeneratorHostKeyProvider.java
@@ -0,0 +1,48 @@
+/*
+ * 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.security.bouncycastle;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Path;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+
+import org.apache.sshd.server.keyprovider.AbstractGeneratorHostKeyProvider;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class BouncyCastleGeneratorHostKeyProvider extends AbstractGeneratorHostKeyProvider {
+    public BouncyCastleGeneratorHostKeyProvider(Path path) {
+        setPath(path);
+    }
+
+    @SuppressWarnings("deprecation")
+    @Override
+    protected void doWriteKeyPair(String resourceKey, KeyPair kp, OutputStream outputStream) throws IOException, GeneralSecurityException {
+        try (org.bouncycastle.openssl.PEMWriter w =
+                     new org.bouncycastle.openssl.PEMWriter(new OutputStreamWriter(outputStream, StandardCharsets.UTF_8))) {
+            w.writeObject(kp);
+            w.flush();
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleKeyPairResourceParser.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleKeyPairResourceParser.java b/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleKeyPairResourceParser.java
new file mode 100644
index 0000000..4c8722a
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleKeyPairResourceParser.java
@@ -0,0 +1,130 @@
+/*
+ * 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.security.bouncycastle;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.security.NoSuchProviderException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.loader.AbstractKeyPairResourceParser;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.common.util.security.SecurityProviderRegistrar;
+import org.apache.sshd.common.util.security.SecurityUtils;
+import org.bouncycastle.openssl.PEMDecryptorProvider;
+import org.bouncycastle.openssl.PEMEncryptedKeyPair;
+import org.bouncycastle.openssl.PEMKeyPair;
+import org.bouncycastle.openssl.PEMParser;
+import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
+import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class BouncyCastleKeyPairResourceParser extends AbstractKeyPairResourceParser {
+    public static final List<String> BEGINNERS =
+            Collections.unmodifiableList(
+                    Arrays.asList(
+                            "BEGIN RSA PRIVATE KEY",
+                            "BEGIN DSA PRIVATE KEY",
+                            "BEGIN EC PRIVATE KEY"));
+
+    public static final List<String> ENDERS =
+            Collections.unmodifiableList(
+                    Arrays.asList(
+                            "END RSA PRIVATE KEY",
+                            "END DSA PRIVATE KEY",
+                            "END EC PRIVATE KEY"));
+
+    public static final BouncyCastleKeyPairResourceParser INSTANCE = new BouncyCastleKeyPairResourceParser();
+
+    public BouncyCastleKeyPairResourceParser() {
+        super(BEGINNERS, ENDERS);
+    }
+
+    @Override
+    public Collection<KeyPair> extractKeyPairs(
+            String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, List<String> lines)
+                    throws IOException, GeneralSecurityException {
+        StringBuilder writer = new StringBuilder(beginMarker.length() + endMarker.length() + lines.size() * 80);
+        writer.append(beginMarker).append(IoUtils.EOL);
+        lines.forEach(l -> writer.append(l).append(IoUtils.EOL));
+        writer.append(endMarker).append(IoUtils.EOL);
+
+        String data = writer.toString();
+        byte[] dataBytes = data.getBytes(StandardCharsets.UTF_8);
+        try (InputStream bais = new ByteArrayInputStream(dataBytes)) {
+            return extractKeyPairs(resourceKey, beginMarker, endMarker, passwordProvider, bais);
+        }
+    }
+
+    @Override
+    public Collection<KeyPair> extractKeyPairs(
+            String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, InputStream stream)
+                    throws IOException, GeneralSecurityException {
+        KeyPair kp = loadKeyPair(resourceKey, stream, passwordProvider);
+        return (kp == null) ? Collections.emptyList() : Collections.singletonList(kp);
+    }
+
+    public static KeyPair loadKeyPair(String resourceKey, InputStream inputStream, FilePasswordProvider provider)
+            throws IOException, GeneralSecurityException {
+        try (PEMParser r = new PEMParser(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
+            Object o = r.readObject();
+
+            SecurityProviderRegistrar registrar = SecurityUtils.getRegisteredProvider(SecurityUtils.BOUNCY_CASTLE);
+            if (registrar == null) {
+                throw new NoSuchProviderException(SecurityUtils.BOUNCY_CASTLE + " registrar not available");
+            }
+
+            JcaPEMKeyConverter pemConverter = new JcaPEMKeyConverter();
+            if (registrar.isNamedProviderUsed()) {
+                pemConverter.setProvider(registrar.getName());
+            } else {
+                pemConverter.setProvider(registrar.getSecurityProvider());
+            }
+            if (o instanceof PEMEncryptedKeyPair) {
+                ValidateUtils.checkNotNull(provider, "No password provider for resource=%s", resourceKey);
+
+                String password = ValidateUtils.checkNotNullAndNotEmpty(provider.getPassword(resourceKey), "No password provided for resource=%s", resourceKey);
+                JcePEMDecryptorProviderBuilder decryptorBuilder = new JcePEMDecryptorProviderBuilder();
+                PEMDecryptorProvider pemDecryptor = decryptorBuilder.build(password.toCharArray());
+                o = ((PEMEncryptedKeyPair) o).decryptKeyPair(pemDecryptor);
+            }
+
+            if (o instanceof PEMKeyPair) {
+                return pemConverter.getKeyPair((PEMKeyPair) o);
+            } else if (o instanceof KeyPair) {
+                return (KeyPair) o;
+            } else {
+                throw new IOException("Failed to read " + resourceKey + " - unknown result object: " + o);
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleRandom.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleRandom.java b/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleRandom.java
new file mode 100644
index 0000000..36c23e7
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleRandom.java
@@ -0,0 +1,88 @@
+/*
+ * 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.security.bouncycastle;
+
+import java.security.SecureRandom;
+
+import org.apache.sshd.common.random.AbstractRandom;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+import org.bouncycastle.crypto.prng.RandomGenerator;
+import org.bouncycastle.crypto.prng.VMPCRandomGenerator;
+
+/**
+ * BouncyCastle <code>Random</code>.
+ * This pseudo random number generator uses the a very fast PRNG from BouncyCastle.
+ * The JRE random will be used when creating a new generator to add some random
+ * data to the seed.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class BouncyCastleRandom extends AbstractRandom {
+    public static final String NAME = SecurityUtils.BOUNCY_CASTLE;
+    private final RandomGenerator random;
+
+    public BouncyCastleRandom() {
+        ValidateUtils.checkTrue(SecurityUtils.isBouncyCastleRegistered(), "BouncyCastle not registered");
+        this.random = new VMPCRandomGenerator();
+        byte[] seed = new SecureRandom().generateSeed(8);
+        this.random.addSeedMaterial(seed);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    public void fill(byte[] bytes, int start, int len) {
+        this.random.nextBytes(bytes, start, len);
+    }
+
+    /**
+     * Returns a pseudo-random uniformly distributed {@code int}
+     * in the half-open range [0, n).
+     */
+    @Override
+    public int random(int n) {
+        ValidateUtils.checkTrue(n > 0, "Limit must be positive: %d", n);
+        if ((n & -n) == n) {
+            return (int) ((n * (long) next(31)) >> 31);
+        }
+
+        int bits;
+        int val;
+        do {
+            bits = next(31);
+            val = bits % n;
+        } while (bits - val + (n - 1) < 0);
+        return val;
+    }
+
+    private int next(int numBits) {
+        int bytes = (numBits + 7) / 8;
+        byte next[] = new byte[bytes];
+        int ret = 0;
+        random.nextBytes(next);
+        for (int i = 0; i < bytes; i++) {
+            ret = (next[i] & 0xFF) | (ret << 8);
+        }
+        return ret >>> (bytes * 8 - numBits);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleRandomFactory.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleRandomFactory.java b/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleRandomFactory.java
new file mode 100644
index 0000000..720c7a5
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleRandomFactory.java
@@ -0,0 +1,45 @@
+/*
+ * 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.security.bouncycastle;
+
+import org.apache.sshd.common.random.AbstractRandomFactory;
+import org.apache.sshd.common.random.Random;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * Named factory for the BouncyCastle <code>Random</code>
+ */
+public final class BouncyCastleRandomFactory extends AbstractRandomFactory {
+    public static final String NAME = "bouncycastle";
+    public static final BouncyCastleRandomFactory INSTANCE = new BouncyCastleRandomFactory();
+
+    public BouncyCastleRandomFactory() {
+        super(NAME);
+    }
+
+    @Override
+    public boolean isSupported() {
+        return SecurityUtils.isBouncyCastleRegistered();
+    }
+
+    @Override
+    public Random create() {
+        return new BouncyCastleRandom();
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleSecurityProviderRegistrar.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleSecurityProviderRegistrar.java b/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleSecurityProviderRegistrar.java
new file mode 100644
index 0000000..b47ca80
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleSecurityProviderRegistrar.java
@@ -0,0 +1,128 @@
+/*
+ * 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.security.bouncycastle;
+
+import java.security.KeyFactory;
+import java.security.KeyPairGenerator;
+import java.security.Provider;
+import java.security.Signature;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ReflectionUtils;
+import org.apache.sshd.common.util.security.AbstractSecurityProviderRegistrar;
+import org.apache.sshd.common.util.security.SecurityUtils;
+import org.apache.sshd.common.util.threads.ThreadUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class BouncyCastleSecurityProviderRegistrar extends AbstractSecurityProviderRegistrar {
+    // We want to use reflection API so as not to require BouncyCastle to be present in the classpath
+    public static final String PROVIDER_CLASS = "org.bouncycastle.jce.provider.BouncyCastleProvider";
+    // Do not define a static registrar instance to minimize class loading issues
+    private final AtomicReference<Boolean> supportHolder = new AtomicReference<>(null);
+    private final AtomicReference<String> allSupportHolder = new AtomicReference<>();
+
+    public BouncyCastleSecurityProviderRegistrar() {
+        super(SecurityUtils.BOUNCY_CASTLE);
+    }
+
+    @Override
+    public boolean isEnabled() {
+        if (!super.isEnabled()) {
+            return false;
+        }
+
+        // For backward compatibility
+        return this.getBooleanProperty(SecurityUtils.REGISTER_BOUNCY_CASTLE_PROP, true);
+    }
+
+    @Override
+    public Provider getSecurityProvider() {
+        try {
+            return getOrCreateProvider(PROVIDER_CLASS);
+        } catch (ReflectiveOperationException t) {
+            Throwable e = GenericUtils.peelException(t);
+            log.error("getSecurityProvider({}) failed ({}) to instantiate {}: {}",
+                      getName(), e.getClass().getSimpleName(), PROVIDER_CLASS, e.getMessage());
+            if (e instanceof RuntimeException) {
+                throw (RuntimeException) e;
+            }
+
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public String getDefaultSecurityEntitySupportValue(Class<?> entityType) {
+        String allValue = allSupportHolder.get();
+        if (GenericUtils.length(allValue) > 0) {
+            return allValue;
+        }
+
+        String propName = getConfigurationPropertyName("supportAll");
+        allValue = this.getStringProperty(propName, ALL_OPTIONS_VALUE);
+        if (GenericUtils.isEmpty(allValue)) {
+            allValue = NO_OPTIONS_VALUE;
+        }
+
+        allSupportHolder.set(allValue);
+        return allValue;
+    }
+
+    @Override
+    public boolean isSecurityEntitySupported(Class<?> entityType, String name) {
+        if (!isSupported()) {
+            return false;
+        }
+
+        // Some known values it does not support
+        if (KeyPairGenerator.class.isAssignableFrom(entityType)
+                || KeyFactory.class.isAssignableFrom(entityType)) {
+            if (Objects.compare(name, SecurityUtils.EDDSA, String.CASE_INSENSITIVE_ORDER) == 0) {
+                return false;
+            }
+        } else if (Signature.class.isAssignableFrom(entityType)) {
+            if (Objects.compare(name, SecurityUtils.CURVE_ED25519_SHA512, String.CASE_INSENSITIVE_ORDER) == 0) {
+                return false;
+            }
+        }
+
+        return super.isSecurityEntitySupported(entityType, name);
+    }
+
+    @Override
+    public boolean isSupported() {
+        Boolean supported;
+        synchronized (supportHolder) {
+            supported = supportHolder.get();
+            if (supported != null) {
+                return supported.booleanValue();
+            }
+
+            ClassLoader cl = ThreadUtils.resolveDefaultClassLoader(getClass());
+            supported = ReflectionUtils.isClassAvailable(cl, PROVIDER_CLASS);
+            supportHolder.set(supported);
+        }
+
+        return supported.booleanValue();
+    }
+}


[04/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common utilities code from sshd-core into sshd-common (new artifact)

Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-DSA-KeyPair
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-DSA-KeyPair b/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-DSA-KeyPair
deleted file mode 100644
index 1d3ef24..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-DSA-KeyPair
+++ /dev/null
@@ -1,12 +0,0 @@
------BEGIN DSA PRIVATE KEY-----
-MIIBvAIBAAKBgQDIPyMbBuQcZxeYDOyCqqkdK37cWQvp+RpWzvieB/oiG/ykfDQX
-oZMRtwqwWTBfejNitbBBmC6G/t5OK+9aFmj7pfJ+a7fZKXfiUquIg9soDsoOindf
-2AwR6MZ3os8uiP2xrC8IQAClnETa15mFShs4a4b2VjddgCQ6tphnY97MywIVAPtr
-YyW11RIXsVTf/9KlbhYaNlt5AoGAX9JzbHykC/0xDKOyKU6xDIOVdEZ0ooAl9Psl
-BEUuNhlv2XgmQScO6C9l2W7gbbut7zIw4FaZ2/dgXa3D4IyexBVug5XMnrssErZo
-NcoF5g0dgEAsb9Hl9gkIK3VHM5kWteeUg1VE700JTtSMisdL8CgIdR+xN8iVH5Ew
-CbLWxmECgYEAtv+cdRfNevYFkp55jVqazc8zRLvfb64jzgc5oSJVc64kFs4yx+ab
-YpGX9WxNxDlG6g2WiY8voDBB0YnUJsn0kVRjBKX9OceROxrfT4K4dVbQZsdt+SLa
-XWL4lGJFrFZL3LZqvySvq6xfhJfakQDDivW4hUOhFPXPHrE5/Ia3T7ACFQCE6flG
-nmVCAbzo9YsbdJWBnxMnBA==
------END DSA PRIVATE KEY-----
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-DSA-KeyPair.pub
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-DSA-KeyPair.pub b/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-DSA-KeyPair.pub
deleted file mode 100644
index c0790ed..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-DSA-KeyPair.pub
+++ /dev/null
@@ -1 +0,0 @@
-ssh-dss AAAAB3NzaC1kc3MAAACBAMg/IxsG5BxnF5gM7IKqqR0rftxZC+n5GlbO+J4H+iIb/KR8NBehkxG3CrBZMF96M2K1sEGYLob+3k4r71oWaPul8n5rt9kpd+JSq4iD2ygOyg6Kd1/YDBHoxneizy6I/bGsLwhAAKWcRNrXmYVKGzhrhvZWN12AJDq2mGdj3szLAAAAFQD7a2MltdUSF7FU3//SpW4WGjZbeQAAAIBf0nNsfKQL/TEMo7IpTrEMg5V0RnSigCX0+yUERS42GW/ZeCZBJw7oL2XZbuBtu63vMjDgVpnb92BdrcPgjJ7EFW6DlcyeuywStmg1ygXmDR2AQCxv0eX2CQgrdUczmRa155SDVUTvTQlO1IyKx0vwKAh1H7E3yJUfkTAJstbGYQAAAIEAtv+cdRfNevYFkp55jVqazc8zRLvfb64jzgc5oSJVc64kFs4yx+abYpGX9WxNxDlG6g2WiY8voDBB0YnUJsn0kVRjBKX9OceROxrfT4K4dVbQZsdt+SLaXWL4lGJFrFZL3LZqvySvq6xfhJfakQDDivW4hUOhFPXPHrE5/Ia3T7A= dsa-key-20130709
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-EC-256-KeyPair
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-EC-256-KeyPair b/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-EC-256-KeyPair
deleted file mode 100644
index 31b1268..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-EC-256-KeyPair
+++ /dev/null
@@ -1,5 +0,0 @@
------BEGIN EC PRIVATE KEY-----
-MHcCAQEEIPKmiQzAASg656IP4PuuElLdLdO/MIXrGxQG6tGkKZ1HoAoGCCqGSM49
-AwEHoUQDQgAEobHtw9wkL332ep9fi8Gw5g8sEGwslNonPUCDR6YUZ9mjOehliLpF
-DLHLxlIFafrVM+LIpagjpRKZcnpGPWQDnA==
------END EC PRIVATE KEY-----

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-EC-256-KeyPair.pub
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-EC-256-KeyPair.pub b/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-EC-256-KeyPair.pub
deleted file mode 100644
index 1c9763f..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-EC-256-KeyPair.pub
+++ /dev/null
@@ -1 +0,0 @@
-ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBKGx7cPcJC999nqfX4vBsOYPLBBsLJTaJz1Ag0emFGfZoznoZYi6RQyxy8ZSBWn61TPiyKWoI6USmXJ6Rj1kA5w= root@osv-linux
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-EC-384-KeyPair
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-EC-384-KeyPair b/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-EC-384-KeyPair
deleted file mode 100644
index 29af76f..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-EC-384-KeyPair
+++ /dev/null
@@ -1,6 +0,0 @@
------BEGIN EC PRIVATE KEY-----
-MIGkAgEBBDB15z4n/vjug4fcEXPcgeonCHQuxJOwgFDIap/rgtM3EwuFDpE9wkfM
-K64UwV1ZSlygBwYFK4EEACKhZANiAARSJmbXE4/ONrLZXFRyxQRcUxMe5bt41vWm
-Qr3dK/X1DSmei20T4epdaCeKMwK58O163kAVHOaDXfRweUTSfI5dZ1l2OXFwQOzH
-Gayma2JpPs8TYR+lC/pDC2iZMp4CR0M=
------END EC PRIVATE KEY-----

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-EC-384-KeyPair.pub
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-EC-384-KeyPair.pub b/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-EC-384-KeyPair.pub
deleted file mode 100644
index 93ceef0..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-EC-384-KeyPair.pub
+++ /dev/null
@@ -1 +0,0 @@
-ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBFImZtcTj842stlcVHLFBFxTEx7lu3jW9aZCvd0r9fUNKZ6LbRPh6l1oJ4ozArnw7XreQBUc5oNd9HB5RNJ8jl1nWXY5cXBA7McZrKZrYmk+zxNhH6UL+kMLaJkyngJHQw== root@osv-linux
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-EC-521-KeyPair
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-EC-521-KeyPair b/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-EC-521-KeyPair
deleted file mode 100644
index 43f79d7..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-EC-521-KeyPair
+++ /dev/null
@@ -1,7 +0,0 @@
------BEGIN EC PRIVATE KEY-----
-MIHcAgEBBEIBUacw+zn8Mw0PYaqqtAlOyaVXARegI6sK5YBhl5E1l9sqTzVN77ce
-1RrqQ8smfvZ6Hiw5gdGcPTszbiorVV5npg6gBwYFK4EEACOhgYkDgYYABACg4siC
-q1iqr4U/spXmw6b2VwBMsof7XLQGoD9wfwUikb8XWthNSmPP1nL6rlzJ5j8Bezn9
-BSSDfVAJfgqxmGIHdgHRVc0mkdq1/Q/DKhBgRyjZc29eo0o2ck3SNGNVaAabRYj6
-ck/iub/U6trKM7bdqy/joYYMwZdxLyYW5YxkPbqEfQ==
------END EC PRIVATE KEY-----

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-EC-521-KeyPair.pub
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-EC-521-KeyPair.pub b/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-EC-521-KeyPair.pub
deleted file mode 100644
index 520b64e..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-EC-521-KeyPair.pub
+++ /dev/null
@@ -1 +0,0 @@
-ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBACg4siCq1iqr4U/spXmw6b2VwBMsof7XLQGoD9wfwUikb8XWthNSmPP1nL6rlzJ5j8Bezn9BSSDfVAJfgqxmGIHdgHRVc0mkdq1/Q/DKhBgRyjZc29eo0o2ck3SNGNVaAabRYj6ck/iub/U6trKM7bdqy/joYYMwZdxLyYW5YxkPbqEfQ== root@osv-linux
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-RSA-KeyPair
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-RSA-KeyPair b/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-RSA-KeyPair
deleted file mode 100644
index afc6aa8..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-RSA-KeyPair
+++ /dev/null
@@ -1,27 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIIEoQIBAAKCAQEAxr3N5fkt966xJINl0hH7Q6lLDRR1D0yMjcXCE5roE9VFut2c
-tGFuo90TCOxkPOMnwzwConeyScVF4ConZeWsxbG9VtRh61IeZ6R5P5ZTvE9xPdZB
-gIEWvU1bRfrrOfSMihqF98pODspE6NoTtND2eglwSGwxcYFmpdTAmu+8qgxgGxlE
-aaCjqwdiNPZhygrH81Mv2ruolNeZkn4Bj+wFFmZTD/waN1pQaMf+SO1+kEYIYFNl
-5+8JRGuUcr8MhHHJB+gwqMTF2BSBVITJzZUiQR0TMtkK6Vbs7yt1F9hhzDzAFDwh
-V+rsfNQaOHpl3zP07qH+/99A0XG1CVcEdHqVMwIBIwKCAQALW02YHN4OJz1Siypj
-xoNi87slUaBKBF/NlkWauGUIcpZFMTwnkIn6vCz5MhRbQC4oadRDzFNUrC/g7HdH
-prlqYe2P7uEGIfMb3YNFdk3tgOHmRsHqFgFMpVWsOjlTxNTUsQ74N3Isuxnha4wY
-9f90sBULc+WRdRvO9jbkSDaqoYVKAqCFWtocL+ZWwBXWrIrsQW4PElgZ/duc5DX7
-eeJ5DXCSj9dO+1KxsWEOKaoeABEegrRVys1/shcDNPhf/p0QShKIdPcpnDUc8cny
-1bq8GSt6jEQ+tuRoSnYrY+RD+mlkHrx373Xc1a9woV+QKTThmd9TQ8gzHMHNqq0a
-7kR7AoGBAOuPOTRiKaDtQyMTEX7eeHsPNE24EgvONjNpxyQ6gKGthG5SiB0IO7mP
-r7EggbR2EY8eMCY5HjvxzxgH86n2Pqbzsr6UlQq7YTPupCm/7fPgRknu917GA20f
-1cuY8B04Jp4FIGryBmCcScX6usXXhjfAvYCWWfkSytA8gX9+b1TNAoGBANf8shbp
-wRnQfgAzw2S+xs29pdwa6Jb/xuLvHSyklmgidrK4nsVI8G+zeCqwkqkNM02sM+vR
-c8EX7+myrGf+S2V3JS3AMNXEhavrWVH0CuqFHlBjSwHZ0uKuPpWHlCnud+23AdQz
-Bf1H7tYKt5es3J/B37o4YxhAL6U9qq+ewZH/AoGBAOTURjLjA94oT9jt870SoOyS
-bVLQEYfPol3UeE8UQnEsN4Ec+UDGK2PNaNfzsTL2WjNB5aF5UJIA184znD60seQC
-raMxQFORdF5ViYekgMEFwJ+XrnlSpD4e7PGqgtqOUWZOH33VKsRADSa5DTU3xDYo
-8porp9wDoHKD64MqXYWTAoGADFeVJeF4v6WDivukw+2k9dBSntz3WRXuG5iilNY0
-evqnsnDzITdeMkTFCcDycA9iBHA9ezCKRYxW2id3kOn1rgbO7KvliI7jESNkMJGa
-OUlvck7RFgxyc1pp+ep9fr0rbKtfMLJ1Xu4q56jXSn7oCSEFeFr+WSg9PKRwJ0rm
-fV8CgYAkYOiNY8jH5lwwoPWOIPJg62zdzmRQktrU3z7Nzk5udN6JnG3g57QjkozX
-AgHARKQ2MuXW9OfOnYNhbGeavcBQmg5XUx3eL4PRFw8mFZdjpBD/kM/dfCEwEta3
-FpRlVGn0RNqVV5xxClObD/CikkDqKZG4MSj3CrO3JK33gl1Lgg==
------END RSA PRIVATE KEY-----

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-RSA-KeyPair.pub
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-RSA-KeyPair.pub b/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-RSA-KeyPair.pub
deleted file mode 100644
index 9fac6f7..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-RSA-KeyPair.pub
+++ /dev/null
@@ -1 +0,0 @@
-ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAxr3N5fkt966xJINl0hH7Q6lLDRR1D0yMjcXCE5roE9VFut2ctGFuo90TCOxkPOMnwzwConeyScVF4ConZeWsxbG9VtRh61IeZ6R5P5ZTvE9xPdZBgIEWvU1bRfrrOfSMihqF98pODspE6NoTtND2eglwSGwxcYFmpdTAmu+8qgxgGxlEaaCjqwdiNPZhygrH81Mv2ruolNeZkn4Bj+wFFmZTD/waN1pQaMf+SO1+kEYIYFNl5+8JRGuUcr8MhHHJB+gwqMTF2BSBVITJzZUiQR0TMtkK6Vbs7yt1F9hhzDzAFDwhV+rsfNQaOHpl3zP07qH+/99A0XG1CVcEdHqVMw== lgoldstein@LGOLDSTEIN-WIN7

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-AES-128-key
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-AES-128-key b/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-AES-128-key
deleted file mode 100644
index 2b93a42..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-AES-128-key
+++ /dev/null
@@ -1,30 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-Proc-Type: 4,ENCRYPTED
-DEK-Info: AES-128-CBC,D41AC063160FCC09B1E1931FB43BCEAA
-
-V218dGT2pUpimnwEVn+2ljvK6mvm2aNLlaakMunlHfIswrakJ1WTs8a61pYILOn9
-MGHrCiqe6ZI7FBJ2wXpSxhcuM3fzk6/dW4Ghh4EHG1Y94w97EizxNfyz/iI2XQw0
-i6ttaDLVzP8UcSRElqG+Zpe1A7EE/DkdkXD3f/DaGHtu1zirVeaEIggMLjfTdwnR
-sH9VnUZhe74VdPV0x16h7JjLt5fcbIjqJ6NWW4QvQpPBv3k0oiUy/nP4FXg1b7VW
-7SowuCPi+mF821hj4xSO8ETlAU1eZdtgXqtejtKm0iDtsjnTBZPvDDrq5ephBlxO
-k7JBJG1LFUiDIGnpxos5nCsKEo8UAw9a5/D4xE3C6UTocXon28XGzVCbkZBN6jcd
-UbpjCVwKMJmFL97487u9S57xrGTmJdi1AtF9Rei8juTTQY4+r3l2c7JtdtcbLUhj
-iLvdYnbh6kUEyE19/+omJaWGQlFhYp7ZMRRQSiz6TD8lhSIBPpXzs+uMfhkrifVk
-3WpjRoikmPOOFLtecee5Rp+SpGd700XgLnxwZ47l0FNfrKKqd3+nZX4JILQ2M0JP
-sBx8gcIew8aUqMzWrwZxbrt9Pd1+2kSNVG9hpLoNoA4WpQnYQMo4L0eTCeMNUOap
-f9H0Hh3QnqXTPHbcYZJCGE2RUxLzn/d7rUxUdEzER+pkhJcw9JbV/izTrpDHs9bM
-cfBLggQvs+UIBww2OFz2BztwoQzsSEuNW/SxG/y6SfRUQq5TZw9NxYnrrqfBXKtx
-svB1JVbn2fKq2Lvi8AZ1fF3tyrNot/tptDf0yDHejWDUvVx5cXsKVK2BbVjbZ88k
-mBtUbw7ea9Ev7ZsihNB2EdhPjLhhKlKLIZznPKeXL3GDTXqCgCxTVh4wLvaR8rDU
-C3Isil4WprCeynmZpOe7bxAZDm2QCobnDB8sLQqBI4zgH8X/1iyXJVdSKfK9vxcB
-sJ5pYCcS2q0C+CJkn6HVTlMQ5CyyzvPaDJukJoxwxsZ5hgCsUHFzrvyGnXqGfTBD
-qEW+oA7cj48CfweV5pXHj+mZpCrpn1zRVJRz4h1FZRsttPGtBRAlns5I3kh5BPRs
-4m1BO1jiWyp/7HkUrDRhEf/QeJsP+mTH32pQgnngZ/AGA0PUcKanMUpe1d2ju83V
-EIcTz9ycTHPiOAM6GaVt54fKj9WRBU+7pf14ZdJmfhp6twc0jNtaTh+/I6Pfb0jN
-0d6yKV//pOeJJBNhuOJgm/0vfkOnOojIJchOQCRt5Lg/a4fD/JXtLOed2zOQa+0J
-3d8Y93mQX/iN1wi95/sG79YBYF3FkJYVhjosSKbiIaxIn76zIx4IAlziycDKvgpr
-JgZcVvCDc4flwrf3Cv/uHK7UWOE+16X1CfAy8JzFg5bhiMmhgsJyHmd+zDGrY6NX
-zz+wLmwOenEwC40gpt89OXbgMcwJMtfiSusatRtZ+AAs0jb/8jExVXfcYE3m3r1/
-FqLZ7seTQT2D01YoPlwUtSPxzaZbziAJ/NaGmURnBGVibDCJxwUAiOSIQH4prIfg
-Q2FCJeMTbLV43Lanlby5nrmLkzsw3uo1MO8Of1DbcnVUHNSwrp/nNzrYdxBLIvqS
------END RSA PRIVATE KEY-----

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-AES-128-key.pub
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-AES-128-key.pub b/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-AES-128-key.pub
deleted file mode 100644
index b1d66a6..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-AES-128-key.pub
+++ /dev/null
@@ -1 +0,0 @@
-ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC/oDbs/yYxBdT02ldP6JIrcETJQ+TCml1tHYuo8cIQp0DZCgRZiEZ4foucAT8R/vLK01nnjRzrI42MXiCzyAHb1sPRD0Fsbpa4TFJczPBBRM2mp56airnArQUMmg/ZKlOf82hn+u7Kgn+ljyjYG5FrdoUBju62i0H4+oBfX+pTkd5ruUgqLyPUC3qtNLwjS4PIPAda/pfpsi9UawQ4ommWCCLlwK55NiSrPDBwKNuVWROcQps2NZRxzRLQEiiCEVBEdiUqqUQ+dg2beLV/4cCS860ZZRvCfe+ko8TUBJ7SLtcrvOEYJOKIZDVhcnQKN/wyXCHExSYytUconlFn/9YX root@devenv-aas
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-AES-192-key
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-AES-192-key b/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-AES-192-key
deleted file mode 100644
index 014410d..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-AES-192-key
+++ /dev/null
@@ -1,30 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-Proc-Type: 4,ENCRYPTED
-DEK-Info: AES-192-CBC,EDF8E3E634D2106991113815A3C1F11F
-
-PZaaf2TCSCRmPZDnUa9Hq+RVGAXRXl8T8rduiqFLKG2Vk192AEo4WRPWhIcJ4ov+
-o6N69V0Fj/NrRyuwjihQQM27gTK2JK8cvDen+WB3mp5wmfFaazHXO21xNGMAp3bm
-obEeO+NHaG0080yxQ4AU2BzPZ1q/ePjDGBiENUsBAHeZvN2fM5uI4Cs9pqko2OM/
-ZiJQkn2QlnpSPm8B7kspqpEV9bn1peVYAMIkp/yES18Kw50kan3R2CqccutVQfdP
-8ddTPPUvWUrDSrW/Ae2qZvRoCXVLDhy1jBKQDMUCrxqDJCo58GR0FWxK4nhCgfeL
-wmxGaKL/3uKDl5otkj93C1qCbEvQCJm5rcwEjkRVfeSPvNNh+WQrJE7uNcJB4kXX
-tF8c5peZI8ymdlFFXlCqjVpdhJh0YKRhpxhWKLA15I1bbwwY4/mwIIMyXOcAQo58
-G7XOwBPw01/lSJ/YTR8y1ubFbWJ6LgoLgSzsOLnBxJJji148UiNDPPbal0FN8vK2
-VsSRBrjrNHSmh+BSA0AStq4QtQpwNMU0qKFLzx5HvoYiaSG+OKtJLZNqTGzDG5aS
-Xe8rNoTQTaZBy5yTY1zTn2lMFngWSt/aYyXfc1BXdYyTOk/eoGHtXxp91nQtCdkG
-4VMMfPlnbW1Q2Cq4ATdcIUPPKLaNVlP2TbloBb1wYjXy8RfFFqZVNOLnxXnXOXUG
-U5SlG44XV5AouwhrlUPrm+0+9jIh4pO4sfG0ldGwabGbUMZ6vvgcU9w1yRcsjycG
-v9yH7CfFKKXjUL5RR2weZVD8vRjQMbfVHRt3P4NFClI6e1gXx92FmT7x97866e+T
-TohyC7uZQq2prBzt8BwxFP0h19+CiTVzofe2I9x+Hce67F+ATnLq9fxOnjZlwK1i
-K95eK+Itc9zSu9tIZGaLvghOExZ24893Ncz5NJ2nn0YptxSi24e8PcECHhSUrjv9
-1VOi8JUmv58bQ5ocuUlHmB2FkpnsYUNjtQx1OT2IRovQJIDCttHhT3QIGoiqoeTw
-sSwyz0gDYzAJtuVkSZxqXJ4LEMozaA3uwxv0TfaQordm+GuGKuV5MsKyEQtITUk5
-5bgQfS0VQqkbq2fRjtd3nIJWCF5otrVulY0tzoTBjGZ2trNj5nz5ZEUTFdRSAaxb
-F+i3Mv8gGPC+2YIo8feDCHnRQ1AALjLon8nKefRsWAOYUozNCtHvu4g7WK6+GcPd
-eiYM7tbSrNCHg+KdB+eWrJCfVCgVHTj8mGb3EiH6oxgx51TmlyuQYJAVseWjQOdm
-IDKiAsNNZijmgMWLsFZG1VvF/446VWIGkSW9eB6bB7aR1Kg4F18VkXdn+SPmaHrC
-NHhNa9dOmcOvlNXIs7jlTjRVEC0pp8DH0AUdPjqlR/F1IHb7uFyqlza8Cqb7PZJs
-x+9GbSwuyKLKLvMzmsnBtpRT32Srsbx6pWst40gaoOMFxto22vmGxz4IABGi96V/
-NrrDHtuZ5OaePl7GihKcUCSSVcoFO2jKKNMMHSSVGhxkfir7qGzMe5LZ9/zl+bvW
-vBS0wSPUd+P3mLc0m53mH+g26I479ryX6/2W+GecB1h5p7S7vJMPQjqZAJskogf5
------END RSA PRIVATE KEY-----

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-AES-192-key.pub
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-AES-192-key.pub b/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-AES-192-key.pub
deleted file mode 100644
index 3fda036..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-AES-192-key.pub
+++ /dev/null
@@ -1 +0,0 @@
-ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCutvykV6G6t4JlLhlJOQKknSB++PXLTtuSjXaH77hUUEl/yc9VbEsfOz1NySBRQDWT0sgRZ1D2YAxaIvK9+0wZPnRNO1ptDrwj650Gflh8vKkEQOlHH7ISTxQTHtyHGeXfm3Bl+qqxTMPG5gcxxkKINMvKAkNYBnOyVX1OXUvqtM1jx6THlfWPQ9yFuVmZDVj1W5VItxjG293RejbjN1EjhhGRBiqwczcIPgPiMDfR0+xFNEQt6YD4k9TFEkLmIfCn+fcqKJhP/z99bRosMHtkRwfX/Rco+G9dRwe1sHvVH7ETeS7v3vl3WoM58AlpYYGUIXsxozRdGE93AmTkaSIj SecurityUtilsTest
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-AES-256-key
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-AES-256-key b/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-AES-256-key
deleted file mode 100644
index 26b699e..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-AES-256-key
+++ /dev/null
@@ -1,30 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-Proc-Type: 4,ENCRYPTED
-DEK-Info: AES-256-CBC,4FF564F3E05187BEDEDA0529D44EE13E
-
-z7KGBVkxJzNS1Z8vMurcYmHhlbxnUYHry87VgzV38UkwsLSo5FIPNUaxvLPeD3Im
-YW4hQ/1Z+3onSwTgaUqxih6YK+fHS0RZD5R4HjOWaERCPZa5/oDciLrjOU3EKkSV
-n84/ezqUn8GlMJYcpB2sURCmnJZ6erHCVSPAhVlsrWPO9uZHx8BpaclqsvC4Gq7a
-C4liGjDNsFvVO998eDIDLmHeO+qGnyeQK+pu3/+Ausvd9QjUf4L3T+Lu+Cgr0w59
-tB3TxeGxPel9Av88VYIhCddIEvRyMcN8lzLrrYrsLNPMmaF1x2uNDORJCTlEjvLw
-QG6zugofiUVdZdsWRPFmM8DbujeoUHkPkwImXedhiKz1Tp0Rz6gqGx6tzsqEe0kD
-eS2e0Ibj41fFxpkQlaG/xDliUvntkoQ6v7ZKbXQrsBP4hyoMcjeUMhWL+5shkJEV
-RzvZOl7WKoovFnf3glLdiDFqlfLK5BhBJckLmdmnVvsD1HYeKqSDrx+r3hUF3yZ5
-Obxxk++QTC1qHhnyAwqW7Y2owq81pmOhgxKqh/4OoxYV0trIgzI47+5hKsp12Van
-7qBCfTtXv4IlBKaKARTroQ2sDPAi1B2j8mU5lrGm81BzsLSqh1IJjOMsccWNfjMS
-jQMKvcQN94y7AvxcBDGVn9FZftNWs9/ZwitRCFfrz/n0u6hLDQBbH6muDffo4ZhF
-ZiH23W743N/ZASC/kA7LZ3pmH6TNl2Zf1gP+tTrfXu17BRDJYi8VqHBcuck/M4Ox
-qOpP4mK2B/aAVmTkty67+QJerIwsW+h8LsjkQTnXGoneobsx2nL8OvJtPHkOUo4b
-1zFyFvQK+V8DWWAml4Y8V+4NCgrybqqK11ApRl7FfaOyCMgZ+AvqzjK3E5s1gsaf
-AaxORV0iDE0Y8nhq9EEhDNTr5Yi6FeHuRmVsack1AQqYn1U64k21x4ZEmDjslq9t
-WKizqG6jz6xtRtsypr0oCBYirt8p5qDmeO58MdES6A+nkOZC0ysyhizpDWdj01rQ
-6w3el0t/wUh1lBC3UCTeD7Vz20B1rAs5AlbdFmOUGHoH2Hjty1DQVZ+Fl5/K9AQi
-yaVR07IDE45i9rvl7DThRHT6Qd0KAb7bIo+Xlz/xKUgA0UVLkpfGtgDNi1DSTsBQ
-m6xlvR9M3tmMwnHlOEfX8YAGIQR9JLiImUtypjXnp6IdxCn682UIWC9XakqMFbzS
-Hm4Y0VBHYPisByl7PuavEP69pgYVdt1PqFFOc3xL42QVihYsM9huw1j4rlP7/jXB
-zJyH6yuqZHV7BYmurBByxRCe4joN24D6HYWTLKhPjgIG++n/IZ+PO7s0khrTh3Pl
-L3bOujyLtr88w6PPhBOCVDPgmJEC6lIGC8zhh+S9Knewr50PsvOoQVKGDTSPO+G7
-NNa/ugN9/eTeO2ru2y6HuahWrf2seKrTQB8JVYP0SZkOe4zVdMRR5M559iLIdh0e
-U/hrj6v/dYVbIkmSWdhtxx0jXbM2VlWTeQQjUYzeVumVhZ1pk2P0LeHrSWhHa5h2
-hadw69Ht0hRTbljZ1MHjK9BbQI1Ph+YBuDr0aPVBf2ePYFvPObw2Mik7ctiDlmQT
------END RSA PRIVATE KEY-----

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-AES-256-key.pub
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-AES-256-key.pub b/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-AES-256-key.pub
deleted file mode 100644
index 6a3a0ac..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-AES-256-key.pub
+++ /dev/null
@@ -1 +0,0 @@
-ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDNUSajd7RzgqvQ2Q2em3CFA4Fh2f8YJkzJTPrcn9L9SFZnent2rABlVVr216jh3hAjhRFEkRhsoWUxUB7UYN6Ox/hGXrfCtD9wB0QbpOjNb0tnSovl3e+LfnWO5mpaRAb9croiuKVwQXuiwLNOQqs5C2wvLJhc77U4myuvFycYQ9Gh9g12g05Q8Qs+Du6hFSV1n1ZDMTtiVYknhe2gCLmBh1ghpaGeG3eWpd8EXumTrpwrylb1hJtj4yo748dXFa/YUJpqzCNyoA+0aY+v0MwgxZcS6zWt5VQ3yqRi4YrXX7vvhz80rKmJQlEhvM9rVKAwKc/qkJdTWlU+v0+/zY7/ hello@world
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-DES-EDE3-key
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-DES-EDE3-key b/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-DES-EDE3-key
deleted file mode 100644
index e6f6156..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-DES-EDE3-key
+++ /dev/null
@@ -1,30 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-Proc-Type: 4,ENCRYPTED
-DEK-Info: DES-EDE3-CBC,7DD956370CB0B83E
-
-oTBNWVUGIKQ3LX/bIE+upuTtiVSWQ9ooTxdNJRIpGsq6oTevtxgkuIF9oO+ShZJq
-zf/mTQPtAyRHSX8JsTKw9GZhd5w7c07V3wfzx689RHFCZDW1DIl67KvwyhRwfRtY
-HXZGYPW/XmOgh3Gw31csmLlJd42r8d+7MRIfmo25SO5Sy67mNccRJikdbYa/HH50
-yMVaf7o73tLLIS3WNnhaKjf9hRsSW370aICk3hfS/NMriae0xAMpqpYQmGkn9Zhh
-zTg+Kjo0FiEZEnMNKZQRRWx7VsoQRfrebOZq3LF7tfEnJcKneJ+grvQp0FpkEMzN
-JeiaEwtoIrXRU5KENQbEnXqF2Mpai6ejh5lVflcHqI01lwWGWkwxnNpxgMbWCKuB
-Unad3fQQLFQU67B8jQpES2AQVXO7UaaDHNtATftC2gFpBktlkSurT/KZBT4bVfS7
-z3K9gFB/lYrji2ef+xuzbLmq+eja9OlZIVxH7RTxODkyrxeNhtEUXJgezOB64Cny
-YFrsDPcR8rSRc5SjMMuAy2dSkyndSp3m2YmtVFHibPrX18K2lblRkiuVDci0EX4p
-1XzaCJ9mhdtMedpIzFdwgCOt/M8OBw9g02qgOK1uBu37tXdACNxhX/wrFPCtbJjD
-yBwnBbXoeca8ZF45uw1egKlHaY2d7kTsH+bdx04bTo4Xq0+lXMALxxkfwTOt27xL
-JQp15AFm6ZAshVt7hxCtm512HoSkkFYMPUPawH9HVZE6f4sbNBlbqgCmYhD5HPMN
-Kd3ssQWTXwiOSd+k8HuBUBhtdZsgdpSdR5xH9wEymS9fcGmrf2yLuRlxhvoqo44X
-+go5CqLoAKoUtVJdtZrkmMdF1OxufDBZ16+fKN8NNE8vQv8Kt221yg/mmDy3t6mh
-axynBSxk1Qtjz2H9iZJ5nglF2Hceb1FQXHGlLCNv+cwA9vRC0pQJ25fPHVhvxhmW
-gnLJ5OY+zXENUWvMIeAGCSfjSVtnSVWAfdJ7kgHZc/wCWO6rYAwlfsPDq8ZdQ8Za
-4Hm6sUnkHHtagvJZahjdSPoziE68FiHqFU3PdmuQsZpCFOzloxahWImgx0UM9n8k
-2DlRPK6bMBCjpUrb5D0jp8YU2KDepw2D4O7RaFLh47w78oxrO1gABY23M4JQfUiP
-lzsfsGy9OI1twCcsJhFOX95nL9w+hlvFALTkY5UT3dB3eWP3k2nE12UxQhE3hVZ9
-vI100y2m/FUnKkeGYkvhRw9yD611jjYuRYGL3LsKNaOK97Ses+vAB0LNOss+12vz
-7bYvDUVJ5csfpPGqxyzYwIbytDwUcvxTLxRlgQNb2YMqzSmVodO0lyXdNF1/KUFF
-nIdlh0KSe4BALzYhya9V71MngLHFifp/erS4gdnFbbBCmZTEZhziBnqimRPoN5Co
-K+22blcRkDytiL+NTRybvfOYvy6Ulb4OK/So+Y5r1B5KGhJqFWSeo0g2ftAB6r1h
-HSbfoxYO/xCMQy7aNXuJI0giVRThcQsQapTO2DMeymt/0nj8qEPF0dubtbmgJ4dd
-aZuYaY1Qxk0umVfq9zHsRTbRb+16TuZtp+7brDDRpRe6lSzdbTNShg==
------END RSA PRIVATE KEY-----

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-DES-EDE3-key.pub
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-DES-EDE3-key.pub b/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-DES-EDE3-key.pub
deleted file mode 100644
index f5c8c85..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-DES-EDE3-key.pub
+++ /dev/null
@@ -1 +0,0 @@
-ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAxvz5QWkCxznYbBOUbMFsCf+PJLauyZTcPsHLPZ/lHYH7EIa9s8NWbfFyTJXVwcuJiDCoRZ8d7KBahAuNjGeXsuJQuCqlLLl6GKoFnI0hnbKbFHDDSub+s3WvLdaTeF22qlTmlPb3RFYUO0cWk9MVpVNM6ev33CUGv4Dmbr0dZNkgUOkcPHiQpxKizsED+EWSVh1Ptx8AEDObNyFHJljbHUeJrHTIlcaekJheRXQWLvsJqKD0TN+Dkvi044MDWG9VjVyNsyXBCz1Vk9VaK1dNkkH+RDGTsFvFj7IPwBS/FliEVRrKOUgjMrmKUPbAm8IblIsno+HLZ60OB6X6hu2iHw== lgoldstein@LGOLDSTEIN-WIN7

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-git/pom.xml
----------------------------------------------------------------------
diff --git a/sshd-git/pom.xml b/sshd-git/pom.xml
index b31f28e..8a4205f 100644
--- a/sshd-git/pom.xml
+++ b/sshd-git/pom.xml
@@ -62,6 +62,13 @@
         </dependency>
         <dependency>
             <groupId>org.apache.sshd</groupId>
+            <artifactId>sshd-common</artifactId>
+            <version>${project.version}</version>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sshd</groupId>
             <artifactId>sshd-sftp</artifactId>
             <version>${project.version}</version>
             <scope>test</scope>

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-git/src/test/java/org/apache/sshd/git/pack/GitPackCommandTest.java
----------------------------------------------------------------------
diff --git a/sshd-git/src/test/java/org/apache/sshd/git/pack/GitPackCommandTest.java b/sshd-git/src/test/java/org/apache/sshd/git/pack/GitPackCommandTest.java
index 77e520a..0764e91 100644
--- a/sshd-git/src/test/java/org/apache/sshd/git/pack/GitPackCommandTest.java
+++ b/sshd-git/src/test/java/org/apache/sshd/git/pack/GitPackCommandTest.java
@@ -30,8 +30,8 @@ import org.apache.sshd.server.SshServer;
 import org.apache.sshd.server.auth.password.AcceptAllPasswordAuthenticator;
 import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory;
 import org.apache.sshd.util.test.BaseTestSupport;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
 import org.apache.sshd.util.test.JSchLogger;
-import org.apache.sshd.util.test.Utils;
 import org.eclipse.jgit.api.Git;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.transport.CredentialsProvider;
@@ -79,14 +79,14 @@ public class GitPackCommandTest extends BaseTestSupport {
             int port = sshd.getPort();
             try {
                 Path serverDir = serverRootDir.resolve(getCurrentTestName() + Constants.DOT_GIT_EXT);
-                Utils.deleteRecursive(serverDir);
+                CommonTestSupportUtils.deleteRecursive(serverDir);
                 Git.init().setBare(true).setDirectory(serverDir.toFile()).call();
 
                 JSch.setConfig("StrictHostKeyChecking", "no");
                 CredentialsProvider.setDefault(new UsernamePasswordCredentialsProvider(getCurrentTestName(), getCurrentTestName()));
                 Path localRootDir = gitRootDir.resolve("local");
                 Path localDir = localRootDir.resolve(serverDir.getFileName());
-                Utils.deleteRecursive(localDir);
+                CommonTestSupportUtils.deleteRecursive(localDir);
 
                 SshClient client = SshClient.setUpDefaultClient();
                 SshSessionFactory.setInstance(new GitSshdSessionFactory(client));

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-git/src/test/java/org/apache/sshd/git/pgm/GitPgmCommandTest.java
----------------------------------------------------------------------
diff --git a/sshd-git/src/test/java/org/apache/sshd/git/pgm/GitPgmCommandTest.java b/sshd-git/src/test/java/org/apache/sshd/git/pgm/GitPgmCommandTest.java
index 1e5973d..18cf599 100644
--- a/sshd-git/src/test/java/org/apache/sshd/git/pgm/GitPgmCommandTest.java
+++ b/sshd-git/src/test/java/org/apache/sshd/git/pgm/GitPgmCommandTest.java
@@ -34,7 +34,7 @@ import org.apache.sshd.git.GitLocationResolver;
 import org.apache.sshd.server.SshServer;
 import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory;
 import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.Utils;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
 import org.eclipse.jgit.api.Git;
 import org.junit.FixMethodOrder;
 import org.junit.Test;
@@ -58,7 +58,7 @@ public class GitPgmCommandTest extends BaseTestSupport {
 
             int port = sshd.getPort();
             try {
-                Utils.deleteRecursive(serverDir);
+                CommonTestSupportUtils.deleteRecursive(serverDir);
 
                 try (SshClient client = setupTestClient()) {
                     client.start();

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-ldap/pom.xml
----------------------------------------------------------------------
diff --git a/sshd-ldap/pom.xml b/sshd-ldap/pom.xml
index 3415c1f..9f6a9ba 100644
--- a/sshd-ldap/pom.xml
+++ b/sshd-ldap/pom.xml
@@ -102,6 +102,13 @@
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>org.apache.sshd</groupId>
+            <artifactId>sshd-common</artifactId>
+            <version>${project.version}</version>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>junit</groupId>
             <artifactId>junit</artifactId>
             <scope>test</scope>

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-ldap/src/test/java/org/apache/sshd/server/auth/BaseAuthenticatorTest.java
----------------------------------------------------------------------
diff --git a/sshd-ldap/src/test/java/org/apache/sshd/server/auth/BaseAuthenticatorTest.java b/sshd-ldap/src/test/java/org/apache/sshd/server/auth/BaseAuthenticatorTest.java
index a614869..d4881ca 100644
--- a/sshd-ldap/src/test/java/org/apache/sshd/server/auth/BaseAuthenticatorTest.java
+++ b/sshd-ldap/src/test/java/org/apache/sshd/server/auth/BaseAuthenticatorTest.java
@@ -55,8 +55,8 @@ import org.apache.directory.shared.ldap.schema.registries.SchemaLoader;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.ValidateUtils;
 import org.apache.sshd.util.test.BaseTestSupport;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
 import org.apache.sshd.util.test.NoIoTestCase;
-import org.apache.sshd.util.test.Utils;
 import org.junit.experimental.categories.Category;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -103,8 +103,9 @@ public abstract class BaseAuthenticatorTest extends BaseTestSupport {
     @SuppressWarnings("checkstyle:avoidnestedblocks")
     public static SimpleImmutableEntry<LdapServer, DirectoryService> startApacheDs(Class<?> anchor) throws Exception {
         Logger log = LoggerFactory.getLogger(anchor);
-        File targetFolder = Objects.requireNonNull(Utils.detectTargetFolder(anchor), "Failed to detect target folder");
-        File workingDirectory = assertHierarchyTargetFolderExists(Utils.deleteRecursive(Utils.resolve(targetFolder, anchor.getSimpleName(), "apacheds-work")));
+        File targetFolder = Objects.requireNonNull(CommonTestSupportUtils.detectTargetFolder(anchor), "Failed to detect target folder");
+        File anchorFolder = CommonTestSupportUtils.resolve(targetFolder, anchor.getSimpleName(), "apacheds-work");
+        File workingDirectory = assertHierarchyTargetFolderExists(CommonTestSupportUtils.deleteRecursive(anchorFolder));
 
         DirectoryService directoryService = new DefaultDirectoryService();
         directoryService.setWorkingDirectory(workingDirectory);
@@ -140,7 +141,9 @@ public abstract class BaseAuthenticatorTest extends BaseTestSupport {
         {
             JdbmPartition systemPartition = new JdbmPartition();
             systemPartition.setId("system");
-            systemPartition.setPartitionDir(assertHierarchyTargetFolderExists(Utils.deleteRecursive(new File(workingDirectory, systemPartition.getId()))));
+
+            File partitionFolder = CommonTestSupportUtils.deleteRecursive(new File(workingDirectory, systemPartition.getId()));
+            systemPartition.setPartitionDir(assertHierarchyTargetFolderExists(partitionFolder));
             systemPartition.setSuffix(ServerDNConstants.SYSTEM_DN);
             systemPartition.setSchemaManager(schemaManager);
             directoryService.setSystemPartition(systemPartition);
@@ -151,7 +154,9 @@ public abstract class BaseAuthenticatorTest extends BaseTestSupport {
             JdbmPartition partition = new JdbmPartition();
             partition.setId("users");
             partition.setSuffix(BASE_DN_TEST);
-            partition.setPartitionDir(assertHierarchyTargetFolderExists(Utils.deleteRecursive(new File(workingDirectory, partition.getId()))));
+
+            File partitionFolder = CommonTestSupportUtils.deleteRecursive(new File(workingDirectory, partition.getId()));
+            partition.setPartitionDir(assertHierarchyTargetFolderExists(partitionFolder));
             directoryService.addPartition(partition);
         }
 
@@ -251,7 +256,7 @@ public abstract class BaseAuthenticatorTest extends BaseTestSupport {
         log.info("Directory service shut down");
 
         log.info("Deleting " + workDir.getAbsolutePath());
-        Utils.deleteRecursive(workDir);
+        CommonTestSupportUtils.deleteRecursive(workDir);
         log.info(workDir.getAbsolutePath() + " deleted");
     }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-mina/pom.xml
----------------------------------------------------------------------
diff --git a/sshd-mina/pom.xml b/sshd-mina/pom.xml
index 14f37a5..5defe91 100644
--- a/sshd-mina/pom.xml
+++ b/sshd-mina/pom.xml
@@ -57,6 +57,13 @@
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>org.apache.sshd</groupId>
+            <artifactId>sshd-common</artifactId>
+            <version>${project.version}</version>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>net.i2p.crypto</groupId>
             <artifactId>eddsa</artifactId>
             <scope>test</scope>

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-netty/pom.xml
----------------------------------------------------------------------
diff --git a/sshd-netty/pom.xml b/sshd-netty/pom.xml
index 5dcdb2e..3482db4 100644
--- a/sshd-netty/pom.xml
+++ b/sshd-netty/pom.xml
@@ -64,6 +64,13 @@
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>org.apache.sshd</groupId>
+            <artifactId>sshd-common</artifactId>
+            <version>${project.version}</version>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>net.i2p.crypto</groupId>
             <artifactId>eddsa</artifactId>
             <scope>test</scope>

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-scp/pom.xml
----------------------------------------------------------------------
diff --git a/sshd-scp/pom.xml b/sshd-scp/pom.xml
index ac02bd9..f6eae7d 100644
--- a/sshd-scp/pom.xml
+++ b/sshd-scp/pom.xml
@@ -42,7 +42,7 @@
             <artifactId>sshd-core</artifactId>
             <version>${project.version}</version>
         </dependency>
-
+            <!-- Test dependencies -->
         <dependency>
             <groupId>org.apache.sshd</groupId>
             <artifactId>sshd-core</artifactId>
@@ -51,6 +51,13 @@
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>org.apache.sshd</groupId>
+            <artifactId>sshd-common</artifactId>
+            <version>${project.version}</version>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>junit</groupId>
             <artifactId>junit</artifactId>
             <scope>test</scope>

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-scp/src/test/java/org/apache/sshd/client/scp/ScpTest.java
----------------------------------------------------------------------
diff --git a/sshd-scp/src/test/java/org/apache/sshd/client/scp/ScpTest.java b/sshd-scp/src/test/java/org/apache/sshd/client/scp/ScpTest.java
index 9d81194..21ff1a4 100644
--- a/sshd-scp/src/test/java/org/apache/sshd/client/scp/ScpTest.java
+++ b/sshd-scp/src/test/java/org/apache/sshd/client/scp/ScpTest.java
@@ -61,9 +61,10 @@ import org.apache.sshd.server.command.Command;
 import org.apache.sshd.server.scp.ScpCommand;
 import org.apache.sshd.server.scp.ScpCommandFactory;
 import org.apache.sshd.util.test.BaseTestSupport;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
+import org.apache.sshd.util.test.CoreTestSupportUtils;
 import org.apache.sshd.util.test.JSchLogger;
 import org.apache.sshd.util.test.SimpleUserInfo;
-import org.apache.sshd.util.test.Utils;
 import org.junit.AfterClass;
 import org.junit.Before;
 import org.junit.BeforeClass;
@@ -143,12 +144,12 @@ public class ScpTest extends BaseTestSupport {
     @BeforeClass
     public static void setupClientAndServer() throws Exception {
         JSchLogger.init();
-        sshd = Utils.setupTestServer(ScpTest.class);
+        sshd = CoreTestSupportUtils.setupTestServer(ScpTest.class);
         sshd.setCommandFactory(new ScpCommandFactory());
         sshd.start();
         port = sshd.getPort();
 
-        client = Utils.setupTestClient(ScpTest.class);
+        client = CoreTestSupportUtils.setupTestClient(ScpTest.class);
         client.start();
     }
 
@@ -180,17 +181,18 @@ public class ScpTest extends BaseTestSupport {
     public void testNormalizedScpRemotePaths() throws Exception {
         Path targetPath = detectTargetFolder();
         Path parentPath = targetPath.getParent();
-        Path scpRoot = Utils.resolve(targetPath, ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
-        Utils.deleteRecursive(scpRoot);
+        Path scpRoot = CommonTestSupportUtils.resolve(targetPath,
+            ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
+        CommonTestSupportUtils.deleteRecursive(scpRoot);
 
         Path localDir = assertHierarchyTargetFolderExists(scpRoot.resolve("local"));
         Path localFile = localDir.resolve("file.txt");
-        byte[] data = Utils.writeFile(localFile, getClass().getName() + "#" + getCurrentTestName() + IoUtils.EOL);
+        byte[] data = CommonTestSupportUtils.writeFile(localFile, getClass().getName() + "#" + getCurrentTestName() + IoUtils.EOL);
 
         Path remoteDir = assertHierarchyTargetFolderExists(scpRoot.resolve("remote"));
         Path remoteFile = remoteDir.resolve(localFile.getFileName().toString());
         String localPath = localFile.toString();
-        String remotePath = Utils.resolveRelativeRemotePath(parentPath, remoteFile);
+        String remotePath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, remoteFile);
         String[] remoteComps = GenericUtils.split(remotePath, '/');
         Factory<? extends Random> factory = client.getRandomFactory();
         Random rnd = factory.create();
@@ -240,17 +242,18 @@ public class ScpTest extends BaseTestSupport {
     public void testUploadAbsoluteDriveLetter() throws Exception {
         Path targetPath = detectTargetFolder();
         Path parentPath = targetPath.getParent();
-        Path scpRoot = Utils.resolve(targetPath, ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
-        Utils.deleteRecursive(scpRoot);
+        Path scpRoot = CommonTestSupportUtils.resolve(targetPath,
+            ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
+        CommonTestSupportUtils.deleteRecursive(scpRoot);
 
         Path localDir = assertHierarchyTargetFolderExists(scpRoot.resolve("local"));
         Path localFile = localDir.resolve("file-1.txt");
-        byte[] data = Utils.writeFile(localFile, getClass().getName() + "#" + getCurrentTestName() + IoUtils.EOL);
+        byte[] data = CommonTestSupportUtils.writeFile(localFile, getClass().getName() + "#" + getCurrentTestName() + IoUtils.EOL);
 
         Path remoteDir = assertHierarchyTargetFolderExists(scpRoot.resolve("remote"));
         Path remoteFile = remoteDir.resolve(localFile.getFileName().toString());
         String localPath = localFile.toString();
-        String remotePath = Utils.resolveRelativeRemotePath(parentPath, remoteFile);
+        String remotePath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, remoteFile);
         try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port)
                     .verify(CONNECT_TIMEOUT, TimeUnit.SECONDS)
                     .getSession()) {
@@ -262,12 +265,12 @@ public class ScpTest extends BaseTestSupport {
             assertFileLength(remoteFile, data.length, TimeUnit.SECONDS.toMillis(5L));
 
             Path secondRemote = remoteDir.resolve("file-2.txt");
-            String secondPath = Utils.resolveRelativeRemotePath(parentPath, secondRemote);
+            String secondPath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, secondRemote);
             scp.upload(localPath, secondPath);
             assertFileLength(secondRemote, data.length, TimeUnit.SECONDS.toMillis(5L));
 
             Path pathRemote = remoteDir.resolve("file-path.txt");
-            String pathPath = Utils.resolveRelativeRemotePath(parentPath, pathRemote);
+            String pathPath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, pathRemote);
             scp.upload(localFile, pathPath);
             assertFileLength(pathRemote, data.length, TimeUnit.SECONDS.toMillis(5L));
         }
@@ -286,18 +289,19 @@ public class ScpTest extends BaseTestSupport {
 
             Path targetPath = detectTargetFolder();
             Path parentPath = targetPath.getParent();
-            Path scpRoot = Utils.resolve(targetPath, ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
-            Utils.deleteRecursive(scpRoot);
+            Path scpRoot = CommonTestSupportUtils.resolve(targetPath,
+                ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
+            CommonTestSupportUtils.deleteRecursive(scpRoot);
 
             Path localDir = assertHierarchyTargetFolderExists(scpRoot.resolve("local"));
             Path localFile = localDir.resolve("file.txt");
-            Utils.writeFile(localFile, data);
+            CommonTestSupportUtils.writeFile(localFile, data);
 
             Path remoteDir = assertHierarchyTargetFolderExists(scpRoot.resolve("remote"));
             Path remoteFile = remoteDir.resolve(localFile.getFileName());
-            Utils.writeFile(remoteFile, data + data);
+            CommonTestSupportUtils.writeFile(remoteFile, data + data);
 
-            String remotePath = Utils.resolveRelativeRemotePath(parentPath, remoteFile);
+            String remotePath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, remoteFile);
             scp.upload(localFile.toString(), remotePath);
             assertFileLength(remoteFile, data.length(), TimeUnit.SECONDS.toMillis(5L));
         }
@@ -306,7 +310,8 @@ public class ScpTest extends BaseTestSupport {
     @Test
     public void testScpUploadZeroLengthFile() throws Exception {
         Path targetPath = detectTargetFolder();
-        Path scpRoot = Utils.resolve(targetPath, ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
+        Path scpRoot = CommonTestSupportUtils.resolve(targetPath,
+            ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
         Path localDir = assertHierarchyTargetFolderExists(scpRoot.resolve("local"));
         Path remoteDir = assertHierarchyTargetFolderExists(scpRoot.resolve("remote"));
         Path zeroLocal = localDir.resolve("zero.txt");
@@ -330,7 +335,7 @@ public class ScpTest extends BaseTestSupport {
             session.auth().verify(AUTH_TIMEOUT, TimeUnit.SECONDS);
 
             ScpClient scp = createScpClient(session);
-            String remotePath = Utils.resolveRelativeRemotePath(targetPath.getParent(), zeroRemote);
+            String remotePath = CommonTestSupportUtils.resolveRelativeRemotePath(targetPath.getParent(), zeroRemote);
             scp.upload(zeroLocal.toString(), remotePath);
             assertFileLength(zeroRemote, 0L, TimeUnit.SECONDS.toMillis(5L));
         }
@@ -339,7 +344,8 @@ public class ScpTest extends BaseTestSupport {
     @Test
     public void testScpDownloadZeroLengthFile() throws Exception {
         Path targetPath = detectTargetFolder();
-        Path scpRoot = Utils.resolve(targetPath, ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
+        Path scpRoot = CommonTestSupportUtils.resolve(targetPath,
+            ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
         Path localDir = assertHierarchyTargetFolderExists(scpRoot.resolve("local"));
         Path remoteDir = assertHierarchyTargetFolderExists(scpRoot.resolve("remote"));
         Path zeroLocal = localDir.resolve(getCurrentTestName());
@@ -362,7 +368,7 @@ public class ScpTest extends BaseTestSupport {
             session.auth().verify(AUTH_TIMEOUT, TimeUnit.SECONDS);
 
             ScpClient scp = createScpClient(session);
-            String remotePath = Utils.resolveRelativeRemotePath(targetPath.getParent(), zeroRemote);
+            String remotePath = CommonTestSupportUtils.resolveRelativeRemotePath(targetPath.getParent(), zeroRemote);
             scp.download(remotePath, zeroLocal.toString());
             assertFileLength(zeroLocal, 0L, TimeUnit.SECONDS.toMillis(5L));
         }
@@ -375,8 +381,9 @@ public class ScpTest extends BaseTestSupport {
 
         Path targetPath = detectTargetFolder();
         Path parentPath = targetPath.getParent();
-        Path scpRoot = Utils.resolve(targetPath, ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
-        Utils.deleteRecursive(scpRoot);
+        Path scpRoot = CommonTestSupportUtils.resolve(targetPath,
+            ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
+        CommonTestSupportUtils.deleteRecursive(scpRoot);
 
         Path localDir = assertHierarchyTargetFolderExists(scpRoot.resolve("local"));
         Path localOutFile = localDir.resolve("file-1.txt");
@@ -390,12 +397,12 @@ public class ScpTest extends BaseTestSupport {
             session.auth().verify(AUTH_TIMEOUT, TimeUnit.SECONDS);
 
             ScpClient scp = createScpClient(session);
-            Utils.writeFile(localOutFile, data);
+            CommonTestSupportUtils.writeFile(localOutFile, data);
 
             assertFalse("Remote folder already exists: " + remoteDir, Files.exists(remoteDir));
 
             String localOutPath = localOutFile.toString();
-            String remoteOutPath = Utils.resolveRelativeRemotePath(parentPath, remoteOutFile);
+            String remoteOutPath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, remoteOutFile);
             outputDebugMessage("Expect upload failure %s => %s", localOutPath, remoteOutPath);
             try {
                 scp.upload(localOutPath, remoteOutPath);
@@ -410,13 +417,13 @@ public class ScpTest extends BaseTestSupport {
             assertFileLength(remoteOutFile, data.length(), TimeUnit.SECONDS.toMillis(11L));
 
             Path secondLocal = localDir.resolve(localOutFile.getFileName());
-            String downloadTarget = Utils.resolveRelativeRemotePath(parentPath, secondLocal);
+            String downloadTarget = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, secondLocal);
             outputDebugMessage("Expect download success %s => %s", remoteOutPath, downloadTarget);
             scp.download(remoteOutPath, downloadTarget);
             assertFileLength(secondLocal, data.length(), TimeUnit.SECONDS.toMillis(11L));
 
             Path localPath = localDir.resolve("file-path.txt");
-            downloadTarget = Utils.resolveRelativeRemotePath(parentPath, localPath);
+            downloadTarget = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, localPath);
             outputDebugMessage("Expect download success %s => %s", remoteOutPath, downloadTarget);
             scp.download(remoteOutPath, downloadTarget);
             assertFileLength(localPath, data.length(), TimeUnit.SECONDS.toMillis(11L));
@@ -434,19 +441,20 @@ public class ScpTest extends BaseTestSupport {
             ScpClient scp = createScpClient(session);
             Path targetPath = detectTargetFolder();
             Path parentPath = targetPath.getParent();
-            Path scpRoot = Utils.resolve(targetPath, ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
-            Utils.deleteRecursive(scpRoot);
+            Path scpRoot = CommonTestSupportUtils.resolve(targetPath,
+                ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
+            CommonTestSupportUtils.deleteRecursive(scpRoot);
 
             Path localDir = assertHierarchyTargetFolderExists(scpRoot.resolve("local"));
             Path local1 = localDir.resolve("file-1.txt");
-            byte[] data = Utils.writeFile(local1, getClass().getName() + "#" + getCurrentTestName() + IoUtils.EOL);
+            byte[] data = CommonTestSupportUtils.writeFile(local1, getClass().getName() + "#" + getCurrentTestName() + IoUtils.EOL);
 
             Path local2 = localDir.resolve("file-2.txt");
             Files.write(local2, data);
 
             Path remoteDir = assertHierarchyTargetFolderExists(scpRoot.resolve("remote"));
             Path remote1 = remoteDir.resolve(local1.getFileName());
-            String remote1Path = Utils.resolveRelativeRemotePath(parentPath, remote1);
+            String remote1Path = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, remote1);
             String[] locals = {local1.toString(), local2.toString()};
             try {
                 scp.upload(locals, remote1Path);
@@ -464,7 +472,7 @@ public class ScpTest extends BaseTestSupport {
             }
 
             Path remoteSubDir = assertHierarchyTargetFolderExists(remoteDir.resolve("dir"));
-            scp.upload(locals, Utils.resolveRelativeRemotePath(parentPath, remoteSubDir));
+            scp.upload(locals, CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, remoteSubDir));
 
             Path remoteSub1 = remoteSubDir.resolve(local1.getFileName());
             assertFileLength(remoteSub1, data.length, TimeUnit.SECONDS.toMillis(11L));
@@ -473,12 +481,12 @@ public class ScpTest extends BaseTestSupport {
             assertFileLength(remoteSub2, data.length, TimeUnit.SECONDS.toMillis(11L));
 
             String[] remotes = {
-                Utils.resolveRelativeRemotePath(parentPath, remoteSub1),
-                Utils.resolveRelativeRemotePath(parentPath, remoteSub2),
+                CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, remoteSub1),
+                CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, remoteSub2),
             };
 
             try {
-                scp.download(remotes, Utils.resolveRelativeRemotePath(parentPath, local1));
+                scp.download(remotes, CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, local1));
                 fail("Unexpected download success to existing local file: " + local1);
             } catch (IOException e) {
                 // Ok
@@ -511,26 +519,27 @@ public class ScpTest extends BaseTestSupport {
             ScpClient scp = createScpClient(session);
             Path targetPath = detectTargetFolder();
             Path parentPath = targetPath.getParent();
-            Path scpRoot = Utils.resolve(targetPath, ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
-            Utils.deleteRecursive(scpRoot);
+            Path scpRoot  = CommonTestSupportUtils.resolve(targetPath,
+                ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
+            CommonTestSupportUtils.deleteRecursive(scpRoot);
 
             Path localDir = scpRoot.resolve("local");
             Path localSubDir = assertHierarchyTargetFolderExists(localDir.resolve("dir"));
             Path localSub1 = localSubDir.resolve("file-1.txt");
-            byte[] data = Utils.writeFile(localSub1, getClass().getName() + "#" + getCurrentTestName() + IoUtils.EOL);
+            byte[] data = CommonTestSupportUtils.writeFile(localSub1, getClass().getName() + "#" + getCurrentTestName() + IoUtils.EOL);
             Path localSub2 = localSubDir.resolve("file-2.txt");
             Files.write(localSub2, data);
 
             Path remoteDir = assertHierarchyTargetFolderExists(scpRoot.resolve("remote"));
-            scp.upload(localSubDir, Utils.resolveRelativeRemotePath(parentPath, remoteDir), ScpClient.Option.Recursive);
+            scp.upload(localSubDir, CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, remoteDir), ScpClient.Option.Recursive);
 
             Path remoteSubDir = remoteDir.resolve(localSubDir.getFileName());
             assertFileLength(remoteSubDir.resolve(localSub1.getFileName()), data.length, TimeUnit.SECONDS.toMillis(11L));
             assertFileLength(remoteSubDir.resolve(localSub2.getFileName()), data.length, TimeUnit.SECONDS.toMillis(11L));
 
-            Utils.deleteRecursive(localSubDir);
+            CommonTestSupportUtils.deleteRecursive(localSubDir);
 
-            scp.download(Utils.resolveRelativeRemotePath(parentPath, remoteSubDir), localDir, ScpClient.Option.Recursive);
+            scp.download(CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, remoteSubDir), localDir, ScpClient.Option.Recursive);
             assertFileLength(localSub1, data.length, TimeUnit.SECONDS.toMillis(11L));
             assertFileLength(localSub2, data.length, TimeUnit.SECONDS.toMillis(11L));
         }
@@ -547,17 +556,18 @@ public class ScpTest extends BaseTestSupport {
             ScpClient scp = createScpClient(session);
             Path targetPath = detectTargetFolder();
             Path parentPath = targetPath.getParent();
-            Path scpRoot = Utils.resolve(targetPath, ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
-            Utils.deleteRecursive(scpRoot);
+            Path scpRoot  = CommonTestSupportUtils.resolve(targetPath,
+                ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
+            CommonTestSupportUtils.deleteRecursive(scpRoot);
 
             Path localDir = assertHierarchyTargetFolderExists(scpRoot.resolve("local"));
             Path local1 = localDir.resolve("file-1.txt");
-            byte[] data = Utils.writeFile(local1, getClass().getName() + "#" + getCurrentTestName() + IoUtils.EOL);
+            byte[] data = CommonTestSupportUtils.writeFile(local1, getClass().getName() + "#" + getCurrentTestName() + IoUtils.EOL);
             Path local2 = localDir.resolve("file-2.txt");
             Files.write(local2, data);
 
             Path remoteDir = assertHierarchyTargetFolderExists(scpRoot.resolve("remote"));
-            String remotePath = Utils.resolveRelativeRemotePath(parentPath, remoteDir);
+            String remotePath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, remoteDir);
             scp.upload(localDir.toString() + File.separator + "*", remotePath);
             assertFileLength(remoteDir.resolve(local1.getFileName()), data.length, TimeUnit.SECONDS.toMillis(11L));
             assertFileLength(remoteDir.resolve(local2.getFileName()), data.length, TimeUnit.SECONDS.toMillis(11L));
@@ -581,18 +591,19 @@ public class ScpTest extends BaseTestSupport {
             ScpClient scp = createScpClient(session);
             Path targetPath = detectTargetFolder();
             Path parentPath = targetPath.getParent();
-            Path scpRoot = Utils.resolve(targetPath, ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
-            Utils.deleteRecursive(scpRoot);
+            Path scpRoot  = CommonTestSupportUtils.resolve(targetPath,
+                ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
+            CommonTestSupportUtils.deleteRecursive(scpRoot);
 
             Path localDir = scpRoot.resolve("local");
             Path localSubDir = assertHierarchyTargetFolderExists(localDir.resolve("dir"));
             Path local1 = localDir.resolve("file-1.txt");
-            byte[] data = Utils.writeFile(local1, getClass().getName() + "#" + getCurrentTestName() + IoUtils.EOL);
+            byte[] data = CommonTestSupportUtils.writeFile(local1, getClass().getName() + "#" + getCurrentTestName() + IoUtils.EOL);
             Path localSub2 = localSubDir.resolve("file-2.txt");
             Files.write(localSub2, data);
 
             Path remoteDir = assertHierarchyTargetFolderExists(scpRoot.resolve("remote"));
-            String remotePath = Utils.resolveRelativeRemotePath(parentPath, remoteDir);
+            String remotePath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, remoteDir);
             scp.upload(localDir.toString() + File.separator + "*", remotePath, ScpClient.Option.Recursive);
             assertFileLength(remoteDir.resolve(local1.getFileName()), data.length, TimeUnit.SECONDS.toMillis(11L));
 
@@ -600,7 +611,7 @@ public class ScpTest extends BaseTestSupport {
             assertFileLength(remoteSubDir.resolve(localSub2.getFileName()), data.length, TimeUnit.SECONDS.toMillis(11L));
 
             Files.delete(local1);
-            Utils.deleteRecursive(localSubDir);
+            CommonTestSupportUtils.deleteRecursive(localSubDir);
 
             scp.download(remotePath + "/*", localDir);
             assertFileLength(local1, data.length, TimeUnit.SECONDS.toMillis(11L));
@@ -624,8 +635,9 @@ public class ScpTest extends BaseTestSupport {
             ScpClient scp = createScpClient(session);
             Path targetPath = detectTargetFolder();
             Path parentPath = targetPath.getParent();
-            Path scpRoot = Utils.resolve(targetPath, ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
-            Utils.deleteRecursive(scpRoot);
+            Path scpRoot  = CommonTestSupportUtils.resolve(targetPath,
+                ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
+            CommonTestSupportUtils.deleteRecursive(scpRoot);
 
             Path localDir = scpRoot.resolve("local");
             Path localSubDir = assertHierarchyTargetFolderExists(localDir.resolve("dir"));
@@ -633,7 +645,7 @@ public class ScpTest extends BaseTestSupport {
             final long lastModMillis = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(1);
             final long lastModSecs = TimeUnit.MILLISECONDS.toSeconds(lastModMillis);
             Path local1 = localDir.resolve("file-1.txt");
-            byte[] data = Utils.writeFile(local1, getClass().getName() + "#" + getCurrentTestName() + IoUtils.EOL);
+            byte[] data = CommonTestSupportUtils.writeFile(local1, getClass().getName() + "#" + getCurrentTestName() + IoUtils.EOL);
 
             File lclFile1 = local1.toFile();
             boolean lcl1ModSet = lclFile1.setLastModified(lastModMillis);
@@ -646,7 +658,7 @@ public class ScpTest extends BaseTestSupport {
             boolean lclSub2ModSet = lclSubFile2.setLastModified(lastModMillis);
 
             Path remoteDir = assertHierarchyTargetFolderExists(scpRoot.resolve("remote"));
-            String remotePath = Utils.resolveRelativeRemotePath(parentPath, remoteDir);
+            String remotePath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, remoteDir);
             scp.upload(localDir.toString() + File.separator + "*", remotePath, ScpClient.Option.Recursive, ScpClient.Option.PreserveAttributes);
 
             Path remote1 = remoteDir.resolve(local1.getFileName());
@@ -662,7 +674,7 @@ public class ScpTest extends BaseTestSupport {
             File remSubFile2 = remoteSub2.toFile();
             assertLastModifiedTimeEquals(remSubFile2, lclSub2ModSet, lastModSecs);
 
-            Utils.deleteRecursive(localDir);
+            CommonTestSupportUtils.deleteRecursive(localDir);
             assertHierarchyTargetFolderExists(localDir);
 
             scp.download(remotePath + "/*", localDir, ScpClient.Option.Recursive, ScpClient.Option.PreserveAttributes);
@@ -684,12 +696,13 @@ public class ScpTest extends BaseTestSupport {
             ScpClient scp = createScpClient(session);
             Path targetPath = detectTargetFolder();
             Path parentPath = targetPath.getParent();
-            Path scpRoot = Utils.resolve(targetPath, ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
-            Utils.deleteRecursive(scpRoot);
+            Path scpRoot  = CommonTestSupportUtils.resolve(targetPath,
+                ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
+            CommonTestSupportUtils.deleteRecursive(scpRoot);
 
             Path remoteDir = assertHierarchyTargetFolderExists(scpRoot.resolve("remote"));
             Path remoteFile = remoteDir.resolve("file.txt");
-            String remotePath = Utils.resolveRelativeRemotePath(parentPath, remoteFile);
+            String remotePath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, remoteFile);
             byte[] data = (getClass().getName() + "#" + getCurrentTestName()).getBytes(StandardCharsets.UTF_8);
             outputDebugMessage("Upload data to %s", remotePath);
             scp.upload(data, remotePath, EnumSet.allOf(PosixFilePermission.class), null);
@@ -753,8 +766,9 @@ public class ScpTest extends BaseTestSupport {
 
             Path targetPath = detectTargetFolder();
             Path parentPath = targetPath.getParent();
-            Path scpRoot = Utils.resolve(targetPath, ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
-            Utils.deleteRecursive(scpRoot);
+            Path scpRoot  = CommonTestSupportUtils.resolve(targetPath,
+                ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
+            CommonTestSupportUtils.deleteRecursive(scpRoot);
 
             Path remoteDir = assertHierarchyTargetFolderExists(scpRoot);
             Path localFile = remoteDir.resolve("data.txt");
@@ -762,7 +776,7 @@ public class ScpTest extends BaseTestSupport {
             Files.write(localFile, data);
 
             Path remoteFile = remoteDir.resolve("upload.txt");
-            String remotePath = Utils.resolveRelativeRemotePath(parentPath, remoteFile);
+            String remotePath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, remoteFile);
             outputDebugMessage("Upload data to %s", remotePath);
             scp.upload(localFile, remotePath);
             assertFileLength(remoteFile, data.length, TimeUnit.SECONDS.toMillis(11L));
@@ -838,12 +852,13 @@ public class ScpTest extends BaseTestSupport {
             ScpClient scp = creator.createScpClient(session);
             Path targetPath = detectTargetFolder();
             Path parentPath = targetPath.getParent();
-            Path scpRoot = Utils.resolve(targetPath, ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
-            Utils.deleteRecursive(scpRoot);
+            Path scpRoot  = CommonTestSupportUtils.resolve(targetPath,
+                ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
+            CommonTestSupportUtils.deleteRecursive(scpRoot);
 
             Path remoteDir = assertHierarchyTargetFolderExists(scpRoot.resolve("remote"));
             Path remoteFile = remoteDir.resolve("file.txt");
-            String remotePath = Utils.resolveRelativeRemotePath(parentPath, remoteFile);
+            String remotePath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, remoteFile);
             byte[] data = (getClass().getName() + "#" + getCurrentTestName()).getBytes(StandardCharsets.UTF_8);
             outputDebugMessage("Upload data to %s", remotePath);
             try {
@@ -918,7 +933,7 @@ public class ScpTest extends BaseTestSupport {
             String unixPath = unixDir + "/" + fileName;
             File root = new File(unixDir);
             File target = new File(unixPath);
-            Utils.deleteRecursive(root);
+            CommonTestSupportUtils.deleteRecursive(root);
             root.mkdirs();
             assertTrue("Failed to ensure existence of " + root, root.exists());
 
@@ -961,12 +976,13 @@ public class ScpTest extends BaseTestSupport {
     public void testWithGanymede() throws Exception {
         Path targetPath = detectTargetFolder();
         Path parentPath = targetPath.getParent();
-        Path scpRoot = Utils.resolve(targetPath, ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
-        Utils.deleteRecursive(scpRoot);
+        Path scpRoot  = CommonTestSupportUtils.resolve(targetPath,
+            ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
+        CommonTestSupportUtils.deleteRecursive(scpRoot);
 
         byte[] expected = (getClass().getName() + "#" + getCurrentTestName()).getBytes(StandardCharsets.UTF_8);
         Path remoteDir = assertHierarchyTargetFolderExists(scpRoot.resolve("remote"));
-        String remotePath = Utils.resolveRelativeRemotePath(parentPath, remoteDir);
+        String remotePath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, remoteDir);
         String fileName = "file.txt";
         Path remoteFile = remoteDir.resolve(fileName);
         String mode = ScpHelper.getOctalPermissions(EnumSet.of(

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-scp/src/test/java/org/apache/sshd/client/scp/SimpleScpClientTest.java
----------------------------------------------------------------------
diff --git a/sshd-scp/src/test/java/org/apache/sshd/client/scp/SimpleScpClientTest.java b/sshd-scp/src/test/java/org/apache/sshd/client/scp/SimpleScpClientTest.java
index d9b9b08..b66071a 100644
--- a/sshd-scp/src/test/java/org/apache/sshd/client/scp/SimpleScpClientTest.java
+++ b/sshd-scp/src/test/java/org/apache/sshd/client/scp/SimpleScpClientTest.java
@@ -29,7 +29,7 @@ import org.apache.sshd.common.scp.ScpHelper;
 import org.apache.sshd.common.session.Session;
 import org.apache.sshd.common.util.io.IoUtils;
 import org.apache.sshd.server.scp.ScpCommandFactory;
-import org.apache.sshd.util.test.Utils;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
 import org.apache.sshd.util.test.client.simple.BaseSimpleClientTestSupport;
 import org.junit.FixMethodOrder;
 import org.junit.Test;
@@ -77,17 +77,18 @@ public class SimpleScpClientTest extends BaseSimpleClientTestSupport {
     @Test
     public void testScpUploadProxy() throws Exception {
         try (CloseableScpClient scp = login()) {
-            Path scpRoot = Utils.resolve(targetPath, ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
-            Utils.deleteRecursive(scpRoot);
+            Path scpRoot = CommonTestSupportUtils.resolve(targetPath,
+                ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
+            CommonTestSupportUtils.deleteRecursive(scpRoot);
 
             Path localDir = assertHierarchyTargetFolderExists(scpRoot.resolve("local"));
             Path localFile = localDir.resolve("file.txt");
             String data = getClass().getName() + "#" + getCurrentTestName() + IoUtils.EOL;
-            byte[] written = Utils.writeFile(localFile, data);
+            byte[] written = CommonTestSupportUtils.writeFile(localFile, data);
 
             Path remoteDir = assertHierarchyTargetFolderExists(scpRoot.resolve("remote"));
             Path remoteFile = remoteDir.resolve(localFile.getFileName());
-            String remotePath = Utils.resolveRelativeRemotePath(parentPath, remoteFile);
+            String remotePath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, remoteFile);
             scp.upload(localFile, remotePath);
 
             byte[] uploaded = Files.readAllBytes(remoteFile);
@@ -98,16 +99,17 @@ public class SimpleScpClientTest extends BaseSimpleClientTestSupport {
     @Test
     public void testScpDownloadProxy() throws Exception {
         try (CloseableScpClient scp = login()) {
-            Path scpRoot = Utils.resolve(targetPath, ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
-            Utils.deleteRecursive(scpRoot);
+            Path scpRoot = CommonTestSupportUtils.resolve(targetPath,
+                ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
+            CommonTestSupportUtils.deleteRecursive(scpRoot);
 
             Path remoteDir = assertHierarchyTargetFolderExists(scpRoot.resolve("remote"));
             Path remoteFile = remoteDir.resolve("file.txt");
             String data = getClass().getName() + "#" + getCurrentTestName() + IoUtils.EOL;
-            byte[] written = Utils.writeFile(remoteFile, data);
+            byte[] written = CommonTestSupportUtils.writeFile(remoteFile, data);
             Path localDir = assertHierarchyTargetFolderExists(scpRoot.resolve("local"));
             Path localFile = localDir.resolve(remoteFile.getFileName());
-            String remotePath = Utils.resolveRelativeRemotePath(parentPath, remoteFile);
+            String remotePath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, remoteFile);
             scp.download(remotePath, localFile);
 
             byte[] downloaded = Files.readAllBytes(localFile);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-sftp/pom.xml
----------------------------------------------------------------------
diff --git a/sshd-sftp/pom.xml b/sshd-sftp/pom.xml
index e26eb32..f639511 100644
--- a/sshd-sftp/pom.xml
+++ b/sshd-sftp/pom.xml
@@ -42,7 +42,7 @@
             <artifactId>sshd-core</artifactId>
             <version>${project.version}</version>
         </dependency>
-
+            <!-- Test dependencies -->
         <dependency>
             <groupId>org.apache.sshd</groupId>
             <artifactId>sshd-core</artifactId>
@@ -51,6 +51,13 @@
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>org.apache.sshd</groupId>
+            <artifactId>sshd-common</artifactId>
+            <version>${project.version}</version>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>junit</groupId>
             <artifactId>junit</artifactId>
             <scope>test</scope>

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpFileSystemProvider.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpFileSystemProvider.java b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpFileSystemProvider.java
index df33c61..9810ba1 100644
--- a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpFileSystemProvider.java
+++ b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpFileSystemProvider.java
@@ -74,7 +74,7 @@ import org.apache.sshd.client.subsystem.sftp.SftpClient.Attributes;
 import org.apache.sshd.common.PropertyResolver;
 import org.apache.sshd.common.PropertyResolverUtils;
 import org.apache.sshd.common.SshException;
-import org.apache.sshd.common.config.SshConfigFileReader;
+import org.apache.sshd.common.config.ConfigFileReaderSupport;
 import org.apache.sshd.common.io.IoSession;
 import org.apache.sshd.common.subsystem.sftp.SftpConstants;
 import org.apache.sshd.common.subsystem.sftp.SftpException;
@@ -180,7 +180,7 @@ public class SftpFileSystemProvider extends FileSystemProvider {
         String host = ValidateUtils.checkNotNullAndNotEmpty(uri.getHost(), "Host not provided");
         int port = uri.getPort();
         if (port <= 0) {
-            port = SshConfigFileReader.DEFAULT_PORT;
+            port = ConfigFileReaderSupport.DEFAULT_PORT;
         }
 
         String userInfo = ValidateUtils.checkNotNullAndNotEmpty(uri.getUserInfo(), "UserInfo not provided");
@@ -1219,13 +1219,13 @@ public class SftpFileSystemProvider extends FileSystemProvider {
             InetSocketAddress inetAddr = (InetSocketAddress) addr;
             return getFileSystemIdentifier(inetAddr.getHostString(), inetAddr.getPort(), username);
         } else {
-            return getFileSystemIdentifier(addr.toString(), SshConfigFileReader.DEFAULT_PORT, username);
+            return getFileSystemIdentifier(addr.toString(), ConfigFileReaderSupport.DEFAULT_PORT, username);
         }
     }
 
     public static String getFileSystemIdentifier(String host, int port, String username) {
         return GenericUtils.trimToEmpty(host) + ':'
-                + ((port <= 0) ? SshConfigFileReader.DEFAULT_PORT : port) + ':'
+                + ((port <= 0) ? ConfigFileReaderSupport.DEFAULT_PORT : port) + ':'
                 + GenericUtils.trimToEmpty(username);
     }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/AbstractSftpClientTestSupport.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/AbstractSftpClientTestSupport.java b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/AbstractSftpClientTestSupport.java
index 49e6c28..8d54edf 100644
--- a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/AbstractSftpClientTestSupport.java
+++ b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/AbstractSftpClientTestSupport.java
@@ -31,8 +31,8 @@ import org.apache.sshd.common.file.virtualfs.VirtualFileSystemFactory;
 import org.apache.sshd.server.SshServer;
 import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory;
 import org.apache.sshd.util.test.BaseTestSupport;
+import org.apache.sshd.util.test.CoreTestSupportUtils;
 import org.apache.sshd.util.test.JSchLogger;
-import org.apache.sshd.util.test.Utils;
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
 
@@ -55,12 +55,12 @@ public abstract class AbstractSftpClientTestSupport extends BaseTestSupport {
     @BeforeClass
     public static void setupClientAndServer() throws Exception {
         JSchLogger.init();
-        sshd = Utils.setupTestServer(AbstractSftpClientTestSupport.class);
+        sshd = CoreTestSupportUtils.setupTestServer(AbstractSftpClientTestSupport.class);
         sshd.setSubsystemFactories(Collections.singletonList(new SftpSubsystemFactory()));
         sshd.start();
         port = sshd.getPort();
 
-        client = Utils.setupTestClient(AbstractSftpClientTestSupport.class);
+        client = CoreTestSupportUtils.setupTestClient(AbstractSftpClientTestSupport.class);
         client.start();
     }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/DefaultCloseableHandleTest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/DefaultCloseableHandleTest.java b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/DefaultCloseableHandleTest.java
index e5265d5..606e17e 100644
--- a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/DefaultCloseableHandleTest.java
+++ b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/DefaultCloseableHandleTest.java
@@ -26,7 +26,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
 import org.apache.sshd.client.subsystem.sftp.SftpClient.CloseableHandle;
 import org.apache.sshd.client.subsystem.sftp.SftpClient.Handle;
 import org.apache.sshd.client.subsystem.sftp.impl.DefaultCloseableHandle;
-import org.apache.sshd.util.test.BaseTestSupport;
+import org.apache.sshd.util.test.JUnitTestSupport;
 import org.apache.sshd.util.test.NoIoTestCase;
 import org.junit.FixMethodOrder;
 import org.junit.Test;
@@ -40,7 +40,7 @@ import org.mockito.Mockito;
  */
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 @Category({ NoIoTestCase.class })
-public class DefaultCloseableHandleTest extends BaseTestSupport {
+public class DefaultCloseableHandleTest extends JUnitTestSupport {
     public DefaultCloseableHandleTest() {
         super();
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpFileSystemTest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpFileSystemTest.java b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpFileSystemTest.java
index 80428eb..9ecb690 100644
--- a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpFileSystemTest.java
+++ b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpFileSystemTest.java
@@ -70,7 +70,8 @@ import org.apache.sshd.server.SshServer;
 import org.apache.sshd.server.subsystem.sftp.SftpSubsystemEnvironment;
 import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory;
 import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.Utils;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
+import org.apache.sshd.util.test.CoreTestSupportUtils;
 import org.junit.AfterClass;
 import org.junit.Before;
 import org.junit.BeforeClass;
@@ -94,7 +95,7 @@ public class SftpFileSystemTest extends BaseTestSupport {
 
     @BeforeClass
     public static void setupServerInstance() throws Exception {
-        sshd = Utils.setupTestServer(SftpFileSystemTest.class);
+        sshd = CoreTestSupportUtils.setupTestServer(SftpFileSystemTest.class);
         sshd.setSubsystemFactories(Collections.singletonList(new SftpSubsystemFactory()));
         sshd.start();
         port = sshd.getPort();
@@ -157,8 +158,9 @@ public class SftpFileSystemTest extends BaseTestSupport {
     @Test
     public void testAttributes() throws IOException {
         Path targetPath = detectTargetFolder();
-        Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
-        Utils.deleteRecursive(lclSftp);
+        Path lclSftp = CommonTestSupportUtils.resolve(targetPath,
+            SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
+        CommonTestSupportUtils.deleteRecursive(lclSftp);
 
         try (FileSystem fs = FileSystems.newFileSystem(createDefaultFileSystemURI(),
                 GenericUtils.<String, Object>mapBuilder()
@@ -168,7 +170,7 @@ public class SftpFileSystemTest extends BaseTestSupport {
 
             Path parentPath = targetPath.getParent();
             Path clientFolder = lclSftp.resolve("client");
-            String remFilePath = Utils.resolveRelativeRemotePath(parentPath, clientFolder.resolve("file.txt"));
+            String remFilePath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, clientFolder.resolve("file.txt"));
             Path file = fs.getPath(remFilePath);
             assertHierarchyTargetFolderExists(file.getParent());
             Files.write(file, (getCurrentTestName() + "\n").getBytes(StandardCharsets.UTF_8));
@@ -203,7 +205,7 @@ public class SftpFileSystemTest extends BaseTestSupport {
     public void testRootFileSystem() throws IOException {
         Path targetPath = detectTargetFolder();
         Path rootNative = targetPath.resolve("root").toAbsolutePath();
-        Utils.deleteRecursive(rootNative);
+        CommonTestSupportUtils.deleteRecursive(rootNative);
         assertHierarchyTargetFolderExists(rootNative);
 
         try (FileSystem fs = FileSystems.newFileSystem(URI.create("root:" + rootNative.toUri().toString() + "!/"), null)) {
@@ -215,13 +217,14 @@ public class SftpFileSystemTest extends BaseTestSupport {
     @Test   // see SSHD-697
     public void testFileChannel() throws IOException {
         Path targetPath = detectTargetFolder();
-        Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName());
+        Path lclSftp = CommonTestSupportUtils.resolve(targetPath,
+            SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName());
         Path lclFile = lclSftp.resolve(getCurrentTestName() + ".txt");
         Files.deleteIfExists(lclFile);
         byte[] expected = (getClass().getName() + "#" + getCurrentTestName() + "(" + new Date() + ")").getBytes(StandardCharsets.UTF_8);
         try (FileSystem fs = FileSystems.newFileSystem(createDefaultFileSystemURI(), Collections.emptyMap())) {
             Path parentPath = targetPath.getParent();
-            String remFilePath = Utils.resolveRelativeRemotePath(parentPath, lclFile);
+            String remFilePath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, lclFile);
             Path file = fs.getPath(remFilePath);
 
             FileSystemProvider provider = fs.provider();
@@ -376,15 +379,16 @@ public class SftpFileSystemTest extends BaseTestSupport {
         }
 
         Path targetPath = detectTargetFolder();
-        Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
-        Utils.deleteRecursive(lclSftp);
+        Path lclSftp = CommonTestSupportUtils.resolve(targetPath,
+            SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
+        CommonTestSupportUtils.deleteRecursive(lclSftp);
 
         Path current = fs.getPath(".").toRealPath().normalize();
         outputDebugMessage("CWD: %s", current);
 
         Path parentPath = targetPath.getParent();
         Path clientFolder = lclSftp.resolve("client");
-        String remFile1Path = Utils.resolveRelativeRemotePath(parentPath, clientFolder.resolve("file-1.txt"));
+        String remFile1Path = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, clientFolder.resolve("file-1.txt"));
         Path file1 = fs.getPath(remFile1Path);
         assertHierarchyTargetFolderExists(file1.getParent());
 
@@ -409,9 +413,9 @@ public class SftpFileSystemTest extends BaseTestSupport {
             assertListEquals("Mismatched ACLs for " + file1, aclView.getAcl(), acl);
         }
 
-        String remFile2Path = Utils.resolveRelativeRemotePath(parentPath, clientFolder.resolve("file-2.txt"));
+        String remFile2Path = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, clientFolder.resolve("file-2.txt"));
         Path file2 = fs.getPath(remFile2Path);
-        String remFile3Path = Utils.resolveRelativeRemotePath(parentPath, clientFolder.resolve("file-3.txt"));
+        String remFile3Path = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, clientFolder.resolve("file-3.txt"));
         Path file3 = fs.getPath(remFile3Path);
         try {
             outputDebugMessage("Move with failure expected %s => %s", file2, file3);


[21/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common utilities code from sshd-core into sshd-common (new artifact)

Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractPrivateKeyEntryDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractPrivateKeyEntryDecoder.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractPrivateKeyEntryDecoder.java
deleted file mode 100644
index 574412e..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractPrivateKeyEntryDecoder.java
+++ /dev/null
@@ -1,40 +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.config.keys.impl;
-
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.util.Collection;
-
-import org.apache.sshd.common.config.keys.PrivateKeyEntryDecoder;
-
-/**
- * @param <PUB> Type of {@link PublicKey}
- * @param <PRV> Type of {@link PrivateKey}
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class AbstractPrivateKeyEntryDecoder<PUB extends PublicKey, PRV extends PrivateKey>
-            extends AbstractKeyEntryResolver<PUB, PRV>
-            implements PrivateKeyEntryDecoder<PUB, PRV> {
-    protected AbstractPrivateKeyEntryDecoder(Class<PUB> pubType, Class<PRV> prvType, Collection<String> names) {
-        super(pubType, prvType, names);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractPublicKeyEntryDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractPublicKeyEntryDecoder.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractPublicKeyEntryDecoder.java
deleted file mode 100644
index 59cbf3a..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractPublicKeyEntryDecoder.java
+++ /dev/null
@@ -1,41 +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.config.keys.impl;
-
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.util.Collection;
-
-import org.apache.sshd.common.config.keys.PublicKeyEntryDecoder;
-
-/**
- * Useful base class implementation for a decoder of an {@code OpenSSH} encoded key data
- *
- * @param <PUB> Type of {@link PublicKey}
- * @param <PRV> Type of {@link PrivateKey}
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class AbstractPublicKeyEntryDecoder<PUB extends PublicKey, PRV extends PrivateKey>
-            extends AbstractKeyEntryResolver<PUB, PRV>
-            implements PublicKeyEntryDecoder<PUB, PRV> {
-    protected AbstractPublicKeyEntryDecoder(Class<PUB> pubType, Class<PRV> prvType, Collection<String> names) {
-        super(pubType, prvType, names);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/DSSPublicKeyEntryDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/DSSPublicKeyEntryDecoder.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/DSSPublicKeyEntryDecoder.java
deleted file mode 100644
index 464d2b0..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/DSSPublicKeyEntryDecoder.java
+++ /dev/null
@@ -1,119 +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.config.keys.impl;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.math.BigInteger;
-import java.security.GeneralSecurityException;
-import java.security.InvalidKeyException;
-import java.security.KeyFactory;
-import java.security.KeyPairGenerator;
-import java.security.interfaces.DSAParams;
-import java.security.interfaces.DSAPrivateKey;
-import java.security.interfaces.DSAPublicKey;
-import java.security.spec.DSAPrivateKeySpec;
-import java.security.spec.DSAPublicKeySpec;
-import java.security.spec.InvalidKeySpecException;
-import java.util.Collections;
-import java.util.Objects;
-
-import org.apache.sshd.common.config.keys.KeyEntryResolver;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class DSSPublicKeyEntryDecoder extends AbstractPublicKeyEntryDecoder<DSAPublicKey, DSAPrivateKey> {
-    public static final DSSPublicKeyEntryDecoder INSTANCE = new DSSPublicKeyEntryDecoder();
-
-    public DSSPublicKeyEntryDecoder() {
-        super(DSAPublicKey.class, DSAPrivateKey.class, Collections.unmodifiableList(Collections.singletonList(KeyPairProvider.SSH_DSS)));
-    }
-
-    @Override
-    public DSAPublicKey decodePublicKey(String keyType, InputStream keyData) throws IOException, GeneralSecurityException {
-        if (!KeyPairProvider.SSH_DSS.equals(keyType)) { // just in case we were invoked directly
-            throw new InvalidKeySpecException("Unexpected key type: " + keyType);
-        }
-
-        BigInteger p = KeyEntryResolver.decodeBigInt(keyData);
-        BigInteger q = KeyEntryResolver.decodeBigInt(keyData);
-        BigInteger g = KeyEntryResolver.decodeBigInt(keyData);
-        BigInteger y = KeyEntryResolver.decodeBigInt(keyData);
-
-        return generatePublicKey(new DSAPublicKeySpec(y, p, q, g));
-    }
-
-    @Override
-    public String encodePublicKey(OutputStream s, DSAPublicKey key) throws IOException {
-        Objects.requireNonNull(key, "No public key provided");
-
-        DSAParams keyParams = Objects.requireNonNull(key.getParams(), "No DSA params available");
-        KeyEntryResolver.encodeString(s, KeyPairProvider.SSH_DSS);
-        KeyEntryResolver.encodeBigInt(s, keyParams.getP());
-        KeyEntryResolver.encodeBigInt(s, keyParams.getQ());
-        KeyEntryResolver.encodeBigInt(s, keyParams.getG());
-        KeyEntryResolver.encodeBigInt(s, key.getY());
-
-        return KeyPairProvider.SSH_DSS;
-    }
-
-    @Override
-    public DSAPublicKey clonePublicKey(DSAPublicKey key) throws GeneralSecurityException {
-        if (key == null) {
-            return null;
-        }
-
-        DSAParams params = key.getParams();
-        if (params == null) {
-            throw new InvalidKeyException("Missing parameters in key");
-        }
-
-        return generatePublicKey(new DSAPublicKeySpec(key.getY(), params.getP(), params.getQ(), params.getG()));
-    }
-
-    @Override
-    public DSAPrivateKey clonePrivateKey(DSAPrivateKey key) throws GeneralSecurityException {
-        if (key == null) {
-            return null;
-        }
-
-        DSAParams params = key.getParams();
-        if (params == null) {
-            throw new InvalidKeyException("Missing parameters in key");
-        }
-
-        return generatePrivateKey(new DSAPrivateKeySpec(key.getX(), params.getP(), params.getQ(), params.getG()));
-    }
-
-    @Override
-    public KeyPairGenerator getKeyPairGenerator() throws GeneralSecurityException {
-        return SecurityUtils.getKeyPairGenerator(KeyUtils.DSS_ALGORITHM);
-    }
-
-    @Override
-    public KeyFactory getKeyFactoryInstance() throws GeneralSecurityException {
-        return SecurityUtils.getKeyFactory(KeyUtils.DSS_ALGORITHM);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/ECDSAPublicKeyEntryDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/ECDSAPublicKeyEntryDecoder.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/ECDSAPublicKeyEntryDecoder.java
deleted file mode 100644
index 397a007..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/ECDSAPublicKeyEntryDecoder.java
+++ /dev/null
@@ -1,178 +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.config.keys.impl;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.security.GeneralSecurityException;
-import java.security.InvalidKeyException;
-import java.security.KeyFactory;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.security.NoSuchProviderException;
-import java.security.interfaces.ECPrivateKey;
-import java.security.interfaces.ECPublicKey;
-import java.security.spec.ECParameterSpec;
-import java.security.spec.ECPoint;
-import java.security.spec.ECPrivateKeySpec;
-import java.security.spec.ECPublicKeySpec;
-import java.security.spec.InvalidKeySpecException;
-import java.util.Objects;
-
-import org.apache.sshd.common.cipher.ECCurves;
-import org.apache.sshd.common.config.keys.KeyEntryResolver;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.util.buffer.BufferUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class ECDSAPublicKeyEntryDecoder extends AbstractPublicKeyEntryDecoder<ECPublicKey, ECPrivateKey> {
-    public static final ECDSAPublicKeyEntryDecoder INSTANCE = new ECDSAPublicKeyEntryDecoder();
-
-    // see rfc5480 section 2.2
-    public static final byte ECPOINT_UNCOMPRESSED_FORM_INDICATOR = 0x04;
-    public static final byte ECPOINT_COMPRESSED_VARIANT_2 = 0x02;
-    public static final byte ECPOINT_COMPRESSED_VARIANT_3 = 0x02;
-
-    public ECDSAPublicKeyEntryDecoder() {
-        super(ECPublicKey.class, ECPrivateKey.class, ECCurves.KEY_TYPES);
-    }
-
-    @Override
-    public ECPublicKey decodePublicKey(String keyType, InputStream keyData) throws IOException, GeneralSecurityException {
-        ECCurves curve = ECCurves.fromKeyType(keyType);
-        if (curve == null) {
-            throw new InvalidKeySpecException("Not an EC curve name: " + keyType);
-        }
-
-        if (!SecurityUtils.isECCSupported()) {
-            throw new NoSuchProviderException("ECC not supported");
-        }
-
-        String keyCurveName = curve.getName();
-        // see rfc5656 section 3.1
-        String encCurveName = KeyEntryResolver.decodeString(keyData);
-        if (!keyCurveName.equals(encCurveName)) {
-            throw new InvalidKeySpecException("Mismatched key curve name (" + keyCurveName + ") vs. encoded one (" + encCurveName + ")");
-        }
-
-        byte[] octets = KeyEntryResolver.readRLEBytes(keyData);
-        ECPoint w;
-        try {
-            w = ECCurves.octetStringToEcPoint(octets);
-            if (w == null) {
-                throw new InvalidKeySpecException("No ECPoint generated for curve=" + keyCurveName
-                        + " from octets=" + BufferUtils.toHex(':', octets));
-            }
-        } catch (RuntimeException e) {
-            throw new InvalidKeySpecException("Failed (" + e.getClass().getSimpleName() + ")"
-                    + " to generate ECPoint for curve=" + keyCurveName
-                    + " from octets=" + BufferUtils.toHex(':', octets)
-                    + ": " + e.getMessage());
-        }
-
-        ECParameterSpec paramSpec = curve.getParameters();
-        return generatePublicKey(new ECPublicKeySpec(w, paramSpec));
-    }
-
-    @Override
-    public ECPublicKey clonePublicKey(ECPublicKey key) throws GeneralSecurityException {
-        if (!SecurityUtils.isECCSupported()) {
-            throw new NoSuchProviderException("ECC not supported");
-        }
-
-        if (key == null) {
-            return null;
-        }
-
-        ECParameterSpec params = key.getParams();
-        if (params == null) {
-            throw new InvalidKeyException("Missing parameters in key");
-        }
-
-        return generatePublicKey(new ECPublicKeySpec(key.getW(), params));
-    }
-
-    @Override
-    public ECPrivateKey clonePrivateKey(ECPrivateKey key) throws GeneralSecurityException {
-        if (!SecurityUtils.isECCSupported()) {
-            throw new NoSuchProviderException("ECC not supported");
-        }
-
-        if (key == null) {
-            return null;
-        }
-
-        ECParameterSpec params = key.getParams();
-        if (params == null) {
-            throw new InvalidKeyException("Missing parameters in key");
-        }
-
-        return generatePrivateKey(new ECPrivateKeySpec(key.getS(), params));
-    }
-
-    @Override
-    public String encodePublicKey(OutputStream s, ECPublicKey key) throws IOException {
-        Objects.requireNonNull(key, "No public key provided");
-
-        ECParameterSpec params = Objects.requireNonNull(key.getParams(), "No EC parameters available");
-        ECCurves curve = Objects.requireNonNull(ECCurves.fromCurveParameters(params), "Cannot determine curve");
-        String keyType = curve.getKeyType();
-        String curveName = curve.getName();
-        KeyEntryResolver.encodeString(s, keyType);
-        // see rfc5656 section 3.1
-        KeyEntryResolver.encodeString(s, curveName);
-        ECCurves.ECPointCompression.UNCOMPRESSED.writeECPoint(s, curveName, key.getW());
-        return keyType;
-    }
-
-    @Override
-    public KeyFactory getKeyFactoryInstance() throws GeneralSecurityException {
-        if (SecurityUtils.isECCSupported()) {
-            return SecurityUtils.getKeyFactory(KeyUtils.EC_ALGORITHM);
-        } else {
-            throw new NoSuchProviderException("ECC not supported");
-        }
-    }
-
-    @Override
-    public KeyPair generateKeyPair(int keySize) throws GeneralSecurityException {
-        ECCurves curve = ECCurves.fromCurveSize(keySize);
-        if (curve == null) {
-            throw new InvalidKeySpecException("Unknown curve for key size=" + keySize);
-        }
-
-        KeyPairGenerator gen = getKeyPairGenerator();
-        gen.initialize(curve.getParameters());
-        return gen.generateKeyPair();
-    }
-
-    @Override
-    public KeyPairGenerator getKeyPairGenerator() throws GeneralSecurityException {
-        if (SecurityUtils.isECCSupported()) {
-            return SecurityUtils.getKeyPairGenerator(KeyUtils.EC_ALGORITHM);
-        } else {
-            throw new NoSuchProviderException("ECC not supported");
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/RSAPublicKeyDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/RSAPublicKeyDecoder.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/RSAPublicKeyDecoder.java
deleted file mode 100644
index 4550815..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/RSAPublicKeyDecoder.java
+++ /dev/null
@@ -1,117 +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.config.keys.impl;
-
-import java.io.IOException;
-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;
-import java.util.Objects;
-
-import org.apache.sshd.common.config.keys.KeyEntryResolver;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class RSAPublicKeyDecoder extends AbstractPublicKeyEntryDecoder<RSAPublicKey, RSAPrivateKey> {
-    public static final RSAPublicKeyDecoder INSTANCE = new RSAPublicKeyDecoder();
-
-    public RSAPublicKeyDecoder() {
-        super(RSAPublicKey.class, RSAPrivateKey.class, Collections.unmodifiableList(Collections.singletonList(KeyPairProvider.SSH_RSA)));
-    }
-
-    @Override
-    public RSAPublicKey decodePublicKey(String keyType, InputStream keyData) throws IOException, GeneralSecurityException {
-        if (!KeyPairProvider.SSH_RSA.equals(keyType)) { // just in case we were invoked directly
-            throw new InvalidKeySpecException("Unexpected key type: " + keyType);
-        }
-
-        BigInteger e = KeyEntryResolver.decodeBigInt(keyData);
-        BigInteger n = KeyEntryResolver.decodeBigInt(keyData);
-
-        return generatePublicKey(new RSAPublicKeySpec(n, e));
-    }
-
-    @Override
-    public String encodePublicKey(OutputStream s, RSAPublicKey key) throws IOException {
-        Objects.requireNonNull(key, "No public key provided");
-        KeyEntryResolver.encodeString(s, KeyPairProvider.SSH_RSA);
-        KeyEntryResolver.encodeBigInt(s, key.getPublicExponent());
-        KeyEntryResolver.encodeBigInt(s, key.getModulus());
-
-        return KeyPairProvider.SSH_RSA;
-    }
-
-    @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(KeyUtils.RSA_ALGORITHM);
-    }
-
-    @Override
-    public KeyFactory getKeyFactoryInstance() throws GeneralSecurityException {
-        return SecurityUtils.getKeyFactory(KeyUtils.RSA_ALGORITHM);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/AESPrivateKeyObfuscator.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/AESPrivateKeyObfuscator.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/AESPrivateKeyObfuscator.java
deleted file mode 100644
index 4437945..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/AESPrivateKeyObfuscator.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sshd.common.config.keys.loader;
-
-import java.security.GeneralSecurityException;
-import java.security.Key;
-import java.security.spec.InvalidKeySpecException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-import javax.crypto.Cipher;
-import javax.crypto.spec.SecretKeySpec;
-
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class AESPrivateKeyObfuscator extends AbstractPrivateKeyObfuscator {
-    public static final String CIPHER_NAME = "AES";
-    public static final AESPrivateKeyObfuscator INSTANCE = new AESPrivateKeyObfuscator();
-
-    public AESPrivateKeyObfuscator() {
-        super(CIPHER_NAME);
-    }
-
-    @Override
-    public List<Integer> getSupportedKeySizes() {
-        return getAvailableKeyLengths();
-    }
-
-    @Override
-    public byte[] applyPrivateKeyCipher(byte[] bytes, PrivateKeyEncryptionContext encContext, boolean encryptIt) throws GeneralSecurityException {
-        int keyLength = resolveKeyLength(encContext);
-        byte[] keyValue = deriveEncryptionKey(encContext, keyLength / Byte.SIZE);
-        return applyPrivateKeyCipher(bytes, encContext, keyLength, keyValue, encryptIt);
-    }
-
-    @Override
-    protected int resolveKeyLength(PrivateKeyEncryptionContext encContext) throws GeneralSecurityException {
-        String cipherType = encContext.getCipherType();
-        try {
-            int keyLength = Integer.parseInt(cipherType);
-            List<Integer> sizes = getSupportedKeySizes();
-            for (Integer s : sizes) {
-                if (s.intValue() == keyLength) {
-                    return keyLength;
-                }
-            }
-
-            throw new InvalidKeySpecException("Unknown " + getCipherName() + " key length: " + cipherType + " - supported: " + sizes);
-        } catch (NumberFormatException e) {
-            throw new InvalidKeySpecException("Bad " + getCipherName() + " key length (" + cipherType + "): " + e.getMessage(), e);
-        }
-    }
-
-    /**
-     * @return A {@link List} of {@link Integer}s holding the available key
-     * lengths values (in bits) for the JVM. <B>Note:</B> AES 256 requires
-     * special JCE policy extension installation (e.g., for Java 7 see
-     * <A HREF="http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html">this link</A>)
-     */
-    @SuppressWarnings("synthetic-access")
-    public static List<Integer> getAvailableKeyLengths() {
-        return LazyKeyLengthsHolder.KEY_LENGTHS;
-    }
-
-    private static final class LazyKeyLengthsHolder {
-        private static final List<Integer> KEY_LENGTHS = Collections.unmodifiableList(detectSupportedKeySizes());
-
-        private LazyKeyLengthsHolder() {
-            throw new UnsupportedOperationException("No instance allowed");
-        }
-
-        // AES 256 requires special JCE policy extension installation
-        private static List<Integer> detectSupportedKeySizes() {
-            List<Integer> sizes = new ArrayList<>();
-            for (int keyLength = 128; keyLength < Short.MAX_VALUE /* just so it doesn't go forever */; keyLength += 64) {
-                try {
-                    byte[] keyAsBytes = new byte[keyLength / Byte.SIZE];
-                    Key key = new SecretKeySpec(keyAsBytes, CIPHER_NAME);
-                    Cipher c = SecurityUtils.getCipher(CIPHER_NAME);
-                    c.init(Cipher.DECRYPT_MODE, key);
-                    sizes.add(Integer.valueOf(keyLength));
-                } catch (GeneralSecurityException e) {
-                    return sizes;
-                }
-            }
-
-            throw new IllegalStateException("No limit encountered: " + sizes);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/AbstractKeyPairResourceParser.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/AbstractKeyPairResourceParser.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/AbstractKeyPairResourceParser.java
deleted file mode 100644
index a83bf68..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/AbstractKeyPairResourceParser.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.common.config.keys.loader;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.StreamCorruptedException;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.logging.Level;
-
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.buffer.BufferUtils;
-import org.apache.sshd.common.util.logging.AbstractLoggingBean;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class AbstractKeyPairResourceParser extends AbstractLoggingBean implements KeyPairResourceParser {
-    private final List<String> beginners;
-    private final List<String> enders;
-    private final List<List<String>> endingMarkers;
-
-    /**
-     * @param beginners The markers that indicate the beginning of a parsing block
-     * @param enders The <U>matching</U> (by position) markers that indicate the end of a parsing block
-     */
-    protected AbstractKeyPairResourceParser(List<String> beginners, List<String> enders) {
-        this.beginners = ValidateUtils.checkNotNullAndNotEmpty(beginners, "No begin markers");
-        this.enders = ValidateUtils.checkNotNullAndNotEmpty(enders, "No end markers");
-        ValidateUtils.checkTrue(
-                beginners.size() == enders.size(), "Mismatched begin(%d)/end(%d) markers sizes", beginners.size(), enders.size());
-        endingMarkers = new ArrayList<>(enders.size());
-        enders.forEach(m -> endingMarkers.add(Collections.singletonList(m)));
-    }
-
-    public List<String> getBeginners() {
-        return beginners;
-    }
-
-    public List<String> getEnders() {
-        return enders;
-    }
-
-    /**
-     * @return A {@link List} of same size as the ending markers, where
-     * each ending marker is encapsulated inside a singleton list and
-     * resides as the <U>same index</U> as the marker it encapsulates
-     */
-    public List<List<String>> getEndingMarkers() {
-        return endingMarkers;
-    }
-
-    @Override
-    public boolean canExtractKeyPairs(String resourceKey, List<String> lines) throws IOException, GeneralSecurityException {
-        return KeyPairResourceParser.containsMarkerLine(lines, getBeginners());
-    }
-
-    @Override
-    public Collection<KeyPair> loadKeyPairs(String resourceKey, FilePasswordProvider passwordProvider, List<String> lines)
-            throws IOException, GeneralSecurityException {
-        Collection<KeyPair> keyPairs = Collections.emptyList();
-        List<String> beginMarkers = getBeginners();
-        List<List<String>> endMarkers = getEndingMarkers();
-        for (Map.Entry<Integer, Integer> markerPos = KeyPairResourceParser.findMarkerLine(lines, beginMarkers); markerPos != null;) {
-            int startIndex = markerPos.getKey();
-            String startLine = lines.get(startIndex);
-            startIndex++;
-
-            int markerIndex = markerPos.getValue();
-            List<String> ender = endMarkers.get(markerIndex);
-            markerPos = KeyPairResourceParser.findMarkerLine(lines, startIndex, ender);
-            if (markerPos == null) {
-                throw new StreamCorruptedException("Missing end marker (" + ender + ") after line #" + startIndex);
-            }
-
-            int endIndex = markerPos.getKey();
-            String endLine = lines.get(endIndex);
-            Collection<KeyPair> kps =
-                extractKeyPairs(resourceKey, startLine, endLine, passwordProvider, lines.subList(startIndex, endIndex));
-            if (GenericUtils.isNotEmpty(kps)) {
-                if (GenericUtils.isEmpty(keyPairs)) {
-                    keyPairs = new LinkedList<>(kps);
-                } else {
-                    keyPairs.addAll(kps);
-                }
-            }
-
-            // see if there are more
-            markerPos = KeyPairResourceParser.findMarkerLine(lines, endIndex + 1, beginMarkers);
-        }
-
-        return keyPairs;
-    }
-
-    /**
-     * Extracts the key pairs within a <U>single</U> delimited by markers block of lines. By
-     * default cleans up the empty lines, joins them and converts them from BASE64
-     *
-     * @param resourceKey A hint as to the origin of the text lines
-     * @param beginMarker The line containing the begin marker
-     * @param endMarker The line containing the end marker
-     * @param passwordProvider The {@link FilePasswordProvider} to use
-     * in case the data is encrypted - may be {@code null} if no encrypted
-     * @param lines The block of lines between the markers
-     * @return The extracted {@link KeyPair}s - may be {@code null}/empty if none.
-     * @throws IOException If failed to parse the data
-     * @throws GeneralSecurityException If failed to generate the keys
-     * @see #extractKeyPairs(String, String, String, FilePasswordProvider, byte[])
-     */
-    public Collection<KeyPair> extractKeyPairs(
-            String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, List<String> lines)
-                    throws IOException, GeneralSecurityException {
-        return extractKeyPairs(resourceKey, beginMarker, endMarker, passwordProvider, KeyPairResourceParser.extractDataBytes(lines));
-    }
-
-    /**
-     * @param resourceKey A hint as to the origin of the text lines
-     * @param beginMarker The line containing the begin marker
-     * @param endMarker The line containing the end marker
-     * @param passwordProvider The {@link FilePasswordProvider} to use
-     * in case the data is encrypted - may be {@code null} if no encrypted
-     * @param bytes The decoded bytes from the lines containing the data
-     * @return The extracted {@link KeyPair}s - may be {@code null}/empty if none.
-     * @throws IOException If failed to parse the data
-     * @throws GeneralSecurityException If failed to generate the keys
-     * @see #extractKeyPairs(String, String, String, FilePasswordProvider, InputStream)
-     */
-    public Collection<KeyPair> extractKeyPairs(
-            String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, byte[] bytes)
-                    throws IOException, GeneralSecurityException {
-        if (log.isTraceEnabled()) {
-            BufferUtils.dumpHex(getSimplifiedLogger(), Level.FINER, beginMarker, ':', 16, bytes);
-        }
-
-        try (InputStream bais = new ByteArrayInputStream(bytes)) {
-            return extractKeyPairs(resourceKey, beginMarker, endMarker, passwordProvider, bais);
-        }
-    }
-
-    /**
-     * @param resourceKey A hint as to the origin of the text lines
-     * @param beginMarker The line containing the begin marker
-     * @param endMarker The line containing the end marker
-     * @param passwordProvider The {@link FilePasswordProvider} to use
-     * in case the data is encrypted - may be {@code null} if no encrypted
-     * @param stream The decoded data {@link InputStream}
-     * @return The extracted {@link KeyPair}s - may be {@code null}/empty if none.
-     * @throws IOException If failed to parse the data
-     * @throws GeneralSecurityException If failed to generate the keys
-     */
-    public abstract Collection<KeyPair> extractKeyPairs(
-            String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, InputStream stream)
-                    throws IOException, GeneralSecurityException;
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/AbstractPrivateKeyObfuscator.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/AbstractPrivateKeyObfuscator.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/AbstractPrivateKeyObfuscator.java
deleted file mode 100644
index ac93ab4..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/AbstractPrivateKeyObfuscator.java
+++ /dev/null
@@ -1,191 +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.config.keys.loader;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.security.GeneralSecurityException;
-import java.security.MessageDigest;
-import java.security.SecureRandom;
-import java.security.spec.InvalidKeySpecException;
-import java.util.Arrays;
-import java.util.Objects;
-import java.util.Random;
-
-import javax.crypto.Cipher;
-import javax.crypto.spec.IvParameterSpec;
-import javax.crypto.spec.SecretKeySpec;
-
-import org.apache.sshd.common.digest.BuiltinDigests;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.buffer.BufferUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class AbstractPrivateKeyObfuscator implements PrivateKeyObfuscator {
-    private final String algName;
-
-    protected AbstractPrivateKeyObfuscator(String name) {
-        algName = ValidateUtils.checkNotNullAndNotEmpty(name, "No name specified");
-    }
-
-    @Override
-    public final String getCipherName() {
-        return algName;
-    }
-
-    @Override
-    public byte[] generateInitializationVector(PrivateKeyEncryptionContext encContext) throws GeneralSecurityException {
-        return generateInitializationVector(resolveKeyLength(encContext));
-    }
-
-    @Override
-    public <A extends Appendable> A appendPrivateKeyEncryptionContext(A sb, PrivateKeyEncryptionContext encContext) throws IOException {
-        if (encContext == null) {
-            return sb;
-        }
-
-        sb.append("DEK-Info: ").append(encContext.getCipherName())
-          .append('-').append(encContext.getCipherType())
-          .append('-').append(encContext.getCipherMode());
-
-        byte[] initVector = encContext.getInitVector();
-        Objects.requireNonNull(initVector, "No encryption init vector");
-        ValidateUtils.checkTrue(initVector.length > 0, "Empty encryption init vector");
-        BufferUtils.appendHex(sb.append(','), BufferUtils.EMPTY_HEX_SEPARATOR, initVector);
-        sb.append(System.lineSeparator());
-        return sb;
-    }
-
-    protected byte[] generateInitializationVector(int keyLength) {
-        int keySize = keyLength / Byte.SIZE;
-        if ((keyLength % Byte.SIZE) != 0) { // e.g., if 36-bits then we need 5 bytes to hold
-            keySize++;
-        }
-
-        byte[] initVector = new byte[keySize];
-        Random randomizer = new SecureRandom();  // TODO consider using some pre-created singleton instance
-        randomizer.nextBytes(initVector);
-        return initVector;
-    }
-
-    protected abstract int resolveKeyLength(PrivateKeyEncryptionContext encContext) throws GeneralSecurityException;
-
-    // see http://martin.kleppmann.com/2013/05/24/improving-security-of-ssh-private-keys.html
-    // see http://www.ict.griffith.edu.au/anthony/info/crypto/openssl.hints (Password to Encryption Key section)
-    // see http://openssl.6102.n7.nabble.com/DES-EDE3-CBC-technical-details-td24883.html
-    protected byte[] deriveEncryptionKey(PrivateKeyEncryptionContext encContext, int outputKeyLength) throws GeneralSecurityException {
-        Objects.requireNonNull(encContext, "No encryption context");
-        ValidateUtils.checkNotNullAndNotEmpty(encContext.getCipherName(), "No cipher name");
-        ValidateUtils.checkNotNullAndNotEmpty(encContext.getCipherType(), "No cipher type");
-        ValidateUtils.checkNotNullAndNotEmpty(encContext.getCipherMode(), "No cipher mode");
-
-        byte[] initVector = Objects.requireNonNull(encContext.getInitVector(), "No encryption init vector");
-        ValidateUtils.checkTrue(initVector.length > 0, "Empty encryption init vector");
-
-        String password = ValidateUtils.checkNotNullAndNotEmpty(encContext.getPassword(), "No encryption password");
-        byte[] passBytes = password.getBytes(StandardCharsets.UTF_8);
-        byte[] keyValue = new byte[outputKeyLength];
-        MessageDigest hash = SecurityUtils.getMessageDigest(BuiltinDigests.Constants.MD5);
-        byte[]  prevHash = GenericUtils.EMPTY_BYTE_ARRAY;
-        for (int index = 0, remLen = keyValue.length; index < keyValue.length;) {
-            hash.reset();    // just making sure
-
-            hash.update(prevHash, 0, prevHash.length);
-            hash.update(passBytes, 0, passBytes.length);
-            hash.update(initVector, 0, Math.min(initVector.length, 8));
-
-            prevHash = hash.digest();
-
-            System.arraycopy(prevHash, 0, keyValue, index, Math.min(remLen, prevHash.length));
-            index += prevHash.length;
-            remLen -= prevHash.length;
-        }
-
-        return keyValue;
-    }
-
-    protected byte[] applyPrivateKeyCipher(byte[] bytes, PrivateKeyEncryptionContext encContext, int numBits, byte[] keyValue, boolean encryptIt)
-            throws GeneralSecurityException {
-        Objects.requireNonNull(encContext, "No encryption context");
-        String cipherName = ValidateUtils.checkNotNullAndNotEmpty(encContext.getCipherName(), "No cipher name");
-        ValidateUtils.checkNotNullAndNotEmpty(encContext.getCipherType(), "No cipher type");
-        String cipherMode = ValidateUtils.checkNotNullAndNotEmpty(encContext.getCipherMode(), "No cipher mode");
-
-        Objects.requireNonNull(bytes, "No source data");
-        Objects.requireNonNull(keyValue, "No encryption key");
-        ValidateUtils.checkTrue(keyValue.length > 0, "Empty encryption key");
-
-        byte[] initVector = Objects.requireNonNull(encContext.getInitVector(), "No encryption init vector");
-        ValidateUtils.checkTrue(initVector.length > 0, "Empty encryption init vector");
-
-        String xform = cipherName + "/" + cipherMode + "/NoPadding";
-        int maxAllowedBits = Cipher.getMaxAllowedKeyLength(xform);
-        // see http://www.javamex.com/tutorials/cryptography/unrestricted_policy_files.shtml
-        if (numBits > maxAllowedBits) {
-            throw new InvalidKeySpecException("applyPrivateKeyCipher(" + xform + ")[encrypt=" + encryptIt + "]"
-                                            + " required key length (" + numBits + ")"
-                                            + " exceeds max. available: " + maxAllowedBits);
-        }
-
-        SecretKeySpec skeySpec = new SecretKeySpec(keyValue, cipherName);
-        IvParameterSpec ivspec = new IvParameterSpec(initVector);
-        Cipher cipher = SecurityUtils.getCipher(xform);
-        int blockSize = cipher.getBlockSize();
-        int dataSize = bytes.length;
-        cipher.init(encryptIt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE, skeySpec, ivspec);
-        if (blockSize <= 0) {
-            return cipher.doFinal(bytes);
-        }
-
-        int remLen = dataSize % blockSize;
-        if (remLen <= 0) {
-            return cipher.doFinal(bytes);
-        }
-
-        int updateSize = dataSize - remLen;
-        byte[] lastBlock = new byte[blockSize];
-        Arrays.fill(lastBlock, (byte) 10);
-        System.arraycopy(bytes, updateSize, lastBlock, 0, remLen);
-
-        // TODO for some reason, calling cipher.update followed by cipher.doFinal does not work
-        ByteArrayOutputStream baos = new ByteArrayOutputStream(dataSize);
-        try {
-            try {
-                byte[] buf = cipher.update(bytes, 0, updateSize);
-                baos.write(buf);
-
-                buf = cipher.doFinal(lastBlock);
-                baos.write(buf);
-            } finally {
-                baos.close();
-            }
-        } catch (IOException e) {
-            throw new GeneralSecurityException("applyPrivateKeyCipher(" + xform + ")[encrypt=" + encryptIt + "]"
-                                             + " failed (" + e.getClass().getSimpleName() + ")"
-                                             + " to split-write: " + e.getMessage(), e);
-        }
-
-        return baos.toByteArray();
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/DESPrivateKeyObfuscator.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/DESPrivateKeyObfuscator.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/DESPrivateKeyObfuscator.java
deleted file mode 100644
index 2043f06..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/DESPrivateKeyObfuscator.java
+++ /dev/null
@@ -1,76 +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.config.keys.loader;
-
-import java.security.GeneralSecurityException;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class DESPrivateKeyObfuscator extends AbstractPrivateKeyObfuscator {
-    public static final int DEFAULT_KEY_LENGTH = 24 /* hardwired size for 3DES */;
-    public static final List<Integer> AVAILABLE_KEY_LENGTHS =
-            Collections.unmodifiableList(Collections.singletonList(Integer.valueOf(DEFAULT_KEY_LENGTH)));
-    public static final DESPrivateKeyObfuscator INSTANCE = new DESPrivateKeyObfuscator();
-
-    public DESPrivateKeyObfuscator() {
-        super("DES");
-    }
-
-    @Override
-    public byte[] applyPrivateKeyCipher(byte[] bytes, PrivateKeyEncryptionContext encContext, boolean encryptIt) throws GeneralSecurityException {
-        PrivateKeyEncryptionContext effContext = resolveEffectiveContext(encContext);
-        byte[] keyValue = deriveEncryptionKey(effContext, DEFAULT_KEY_LENGTH);
-        return applyPrivateKeyCipher(bytes, effContext, keyValue.length * Byte.SIZE, keyValue, encryptIt);
-    }
-
-    @Override
-    public List<Integer> getSupportedKeySizes() {
-        return AVAILABLE_KEY_LENGTHS;
-    }
-
-    @Override
-    protected int resolveKeyLength(PrivateKeyEncryptionContext encContext) throws GeneralSecurityException {
-        return DEFAULT_KEY_LENGTH;
-    }
-
-    @Override
-    protected byte[] generateInitializationVector(int keyLength) {
-        return super.generateInitializationVector(8 * Byte.SIZE);
-    }
-
-    public static final PrivateKeyEncryptionContext resolveEffectiveContext(PrivateKeyEncryptionContext encContext) {
-        if (encContext == null) {
-            return null;
-        }
-
-        String cipherName = encContext.getCipherName();
-        String cipherType = encContext.getCipherType();
-        PrivateKeyEncryptionContext effContext = encContext;
-        if ("EDE3".equalsIgnoreCase(cipherType)) {
-            cipherName += "ede";
-            effContext = encContext.clone();
-            effContext.setCipherName(cipherName);
-        }
-
-        return effContext;
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/KeyPairResourceLoader.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/KeyPairResourceLoader.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/KeyPairResourceLoader.java
deleted file mode 100644
index fa6930a..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/KeyPairResourceLoader.java
+++ /dev/null
@@ -1,129 +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.config.keys.loader;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.io.StringReader;
-import java.net.URL;
-import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.OpenOption;
-import java.nio.file.Path;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
-
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.util.io.IoUtils;
-
-/**
- * Loads {@link KeyPair}s from text resources
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FunctionalInterface
-public interface KeyPairResourceLoader {
-    /**
-     * An empty loader that never fails but always returns an empty list
-     */
-    KeyPairResourceLoader EMPTY = (resourceKey, passwordProvider, lines) -> Collections.emptyList();
-
-    default Collection<KeyPair> loadKeyPairs(Path path, FilePasswordProvider passwordProvider, OpenOption... options)
-            throws IOException, GeneralSecurityException {
-        return loadKeyPairs(path, passwordProvider, StandardCharsets.UTF_8, options);
-    }
-
-    default Collection<KeyPair> loadKeyPairs(Path path, FilePasswordProvider passwordProvider, Charset cs, OpenOption... options)
-            throws IOException, GeneralSecurityException {
-        try (InputStream stream = Files.newInputStream(path, options)) {
-            return loadKeyPairs(path.toString(), passwordProvider, stream, cs);
-        }
-    }
-
-    default Collection<KeyPair> loadKeyPairs(URL url, FilePasswordProvider passwordProvider)
-            throws IOException, GeneralSecurityException {
-        return loadKeyPairs(url, passwordProvider, StandardCharsets.UTF_8);
-    }
-
-    default Collection<KeyPair> loadKeyPairs(URL url, FilePasswordProvider passwordProvider, Charset cs)
-            throws IOException, GeneralSecurityException {
-        try (InputStream stream = Objects.requireNonNull(url, "No URL").openStream()) {
-            return loadKeyPairs(url.toExternalForm(), passwordProvider, stream, cs);
-        }
-    }
-
-    default Collection<KeyPair> loadKeyPairs(String resourceKey, FilePasswordProvider passwordProvider, String data)
-            throws IOException, GeneralSecurityException {
-        try (Reader reader = new StringReader((data == null) ? "" : data)) {
-            return loadKeyPairs(resourceKey, passwordProvider, reader);
-        }
-    }
-
-    default Collection<KeyPair> loadKeyPairs(String resourceKey, FilePasswordProvider passwordProvider, InputStream stream)
-            throws IOException, GeneralSecurityException {
-        return loadKeyPairs(resourceKey, passwordProvider, stream, StandardCharsets.UTF_8);
-    }
-
-    default Collection<KeyPair> loadKeyPairs(String resourceKey, FilePasswordProvider passwordProvider, InputStream stream, Charset cs)
-            throws IOException, GeneralSecurityException {
-        try (Reader reader = new InputStreamReader(
-                Objects.requireNonNull(stream, "No stream instance"), Objects.requireNonNull(cs, "No charset"))) {
-            return loadKeyPairs(resourceKey, passwordProvider, reader);
-        }
-    }
-
-    default Collection<KeyPair> loadKeyPairs(String resourceKey, FilePasswordProvider passwordProvider, Reader r)
-            throws IOException, GeneralSecurityException {
-        try (BufferedReader br = new BufferedReader(Objects.requireNonNull(r, "No reader instance"), IoUtils.DEFAULT_COPY_SIZE)) {
-            return loadKeyPairs(resourceKey, passwordProvider, br);
-        }
-    }
-
-    default Collection<KeyPair> loadKeyPairs(String resourceKey, FilePasswordProvider passwordProvider, BufferedReader r)
-            throws IOException, GeneralSecurityException {
-        return loadKeyPairs(resourceKey, passwordProvider, IoUtils.readAllLines(r));
-    }
-
-    /**
-     * Loads key pairs from the given resource text lines
-     *
-     * @param resourceKey A hint as to the origin of the text lines
-     * @param passwordProvider The {@link FilePasswordProvider} to use
-     * in case the data is encrypted - may be {@code null} if no encrypted
-     * data is expected
-     * @param lines The {@link List} of lines as read from the resource
-     * @return The extracted {@link KeyPair}s - may be {@code null}/empty if none.
-     * <B>Note:</B> the resource loader may decide to skip unknown lines if
-     * more than one key pair type is encoded in it
-     * @throws IOException If failed to process the lines
-     * @throws GeneralSecurityException If failed to generate the keys from the
-     * parsed data
-     */
-    Collection<KeyPair> loadKeyPairs(String resourceKey, FilePasswordProvider passwordProvider, List<String> lines)
-            throws IOException, GeneralSecurityException;
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/KeyPairResourceParser.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/KeyPairResourceParser.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/KeyPairResourceParser.java
deleted file mode 100644
index 80fc2c5..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/KeyPairResourceParser.java
+++ /dev/null
@@ -1,192 +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.config.keys.loader;
-
-import java.io.IOException;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.util.AbstractMap.SimpleImmutableEntry;
-import java.util.Arrays;
-import java.util.Base64;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.LinkedList;
-import java.util.List;
-
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface KeyPairResourceParser extends KeyPairResourceLoader {
-    /**
-     * An empty parser that never fails, but always report that it cannot
-     * extract key pairs and returns empty list if asked to load
-     */
-    KeyPairResourceParser EMPTY = new KeyPairResourceParser() {
-        @Override
-        public Collection<KeyPair> loadKeyPairs(String resourceKey, FilePasswordProvider passwordProvider, List<String> lines)
-                throws IOException, GeneralSecurityException {
-            return Collections.emptyList();
-        }
-
-        @Override
-        public boolean canExtractKeyPairs(String resourceKey, List<String> lines) throws IOException, GeneralSecurityException {
-            return false;
-        }
-
-        @Override
-        public String toString() {
-            return "EMPTY";
-        }
-    };
-
-    /**
-     * @param resourceKey A hint as to the origin of the text lines
-     * @param lines The resource lines
-     * @return {@code true} if the parser can extract some key pairs from the lines
-     * @throws IOException If failed to process the lines
-     * @throws GeneralSecurityException If failed to extract information regarding
-     * the possibility to extract the key pairs
-     */
-    boolean canExtractKeyPairs(String resourceKey, List<String> lines)
-            throws IOException, GeneralSecurityException;
-
-    /**
-     * Converts the lines assumed to contain BASE-64 encoded data into
-     * the actual content bytes.
-     *
-     * @param lines The data lines - empty lines and spaces are automatically
-     * deleted <U>before</U> BASE-64 decoding takes place.
-     * @return The decoded data bytes
-     * @see #joinDataLines(Collection)
-     */
-    static byte[] extractDataBytes(Collection<String> lines) {
-        String data = joinDataLines(lines);
-        Base64.Decoder decoder = Base64.getDecoder();
-        return decoder.decode(data);
-    }
-
-    static String joinDataLines(Collection<String> lines) {
-        String data = GenericUtils.join(lines, ' ');
-        data = data.replaceAll("\\s", "");
-        data = data.trim();
-        return data;
-    }
-
-    static boolean containsMarkerLine(List<String> lines, String marker) {
-        return containsMarkerLine(lines, Collections.singletonList(ValidateUtils.checkNotNullAndNotEmpty(marker, "No marker")));
-    }
-
-    static boolean containsMarkerLine(List<String> lines, List<String> markers) {
-        return findMarkerLine(lines, markers) != null;
-    }
-
-    /**
-     * Attempts to locate a line that contains one of the markers
-     *
-     * @param lines The list of lines to scan - ignored if {@code null}/empty
-     * @param markers The markers to match - ignored if {@code null}/empty
-     * @return A {@link SimpleImmutableEntry} whose key is the <U>first</U> line index
-     * that matched and value the matched marker index - {@code null} if no match found
-     * @see #findMarkerLine(List, int, List)
-     */
-    static SimpleImmutableEntry<Integer, Integer> findMarkerLine(List<String> lines, List<String> markers) {
-        return findMarkerLine(lines, 0, markers);
-    }
-
-    /**
-     * Attempts to locate a line that contains one of the markers
-     *
-     * @param lines The list of lines to scan - ignored if {@code null}/empty
-     * @param startLine The scan start line index
-     * @param markers The markers to match - ignored if {@code null}/empty
-     * @return A {@link SimpleImmutableEntry} whose key is the <U>first</U> line index
-     * that matched and value the matched marker index - {@code null} if no match found
-     */
-    static SimpleImmutableEntry<Integer, Integer> findMarkerLine(List<String> lines, int startLine, List<String> markers) {
-        if (GenericUtils.isEmpty(lines) || GenericUtils.isEmpty(markers)) {
-            return null;
-        }
-
-        for (int lineIndex = startLine; lineIndex < lines.size(); lineIndex++) {
-            String l = lines.get(lineIndex);
-            for (int markerIndex = 0; markerIndex < markers.size(); markerIndex++) {
-                String m = markers.get(markerIndex);
-                if (l.contains(m)) {
-                    return new SimpleImmutableEntry<>(lineIndex, markerIndex);
-                }
-            }
-        }
-
-        return null;
-    }
-
-    static KeyPairResourceParser aggregate(KeyPairResourceParser... parsers) {
-        return aggregate(Arrays.asList(ValidateUtils.checkNotNullAndNotEmpty(parsers, "No parsers to aggregate")));
-    }
-
-    static KeyPairResourceParser aggregate(Collection<? extends KeyPairResourceParser> parsers) {
-        ValidateUtils.checkNotNullAndNotEmpty(parsers, "No parsers to aggregate");
-        return new KeyPairResourceParser() {
-            @Override
-            public Collection<KeyPair> loadKeyPairs(String resourceKey, FilePasswordProvider passwordProvider, List<String> lines)
-                    throws IOException, GeneralSecurityException {
-                Collection<KeyPair> keyPairs = Collections.emptyList();
-                for (KeyPairResourceParser p : parsers) {
-                    if (!p.canExtractKeyPairs(resourceKey, lines)) {
-                        continue;
-                    }
-
-                    Collection<KeyPair> kps = p.loadKeyPairs(resourceKey, passwordProvider, lines);
-                    if (GenericUtils.isEmpty(kps)) {
-                        continue;
-                    }
-
-                    if (GenericUtils.isEmpty(keyPairs)) {
-                        keyPairs = new LinkedList<>(kps);
-                    } else {
-                        keyPairs.addAll(kps);
-                    }
-                }
-
-                return keyPairs;
-            }
-
-            @Override
-            public boolean canExtractKeyPairs(String resourceKey, List<String> lines) throws IOException, GeneralSecurityException {
-                for (KeyPairResourceParser p : parsers) {
-                    if (p.canExtractKeyPairs(resourceKey, lines)) {
-                        return true;
-                    }
-                }
-
-                return false;
-            }
-
-            @Override
-            public String toString() {
-                return KeyPairResourceParser.class.getSimpleName() + "[aggregate]";
-            }
-        };
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/PrivateKeyEncryptionContext.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/PrivateKeyEncryptionContext.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/PrivateKeyEncryptionContext.java
deleted file mode 100644
index 5e03cdb..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/PrivateKeyEncryptionContext.java
+++ /dev/null
@@ -1,270 +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.config.keys.loader;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.NavigableSet;
-import java.util.Objects;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class PrivateKeyEncryptionContext implements Cloneable {
-    public static final String  DEFAULT_CIPHER_MODE = "CBC";
-
-    private static final Map<String, PrivateKeyObfuscator> OBFUSCATORS =
-            Stream.of(AESPrivateKeyObfuscator.INSTANCE, DESPrivateKeyObfuscator.INSTANCE)
-                .collect(Collectors.toMap(AbstractPrivateKeyObfuscator::getCipherName, Function.identity()));
-
-    private String  cipherName;
-    private String cipherType;
-    private String cipherMode = DEFAULT_CIPHER_MODE;
-    private String password;
-    private byte[]  initVector;
-    private transient PrivateKeyObfuscator  obfuscator;
-
-    public PrivateKeyEncryptionContext() {
-        super();
-    }
-
-    public PrivateKeyEncryptionContext(String algInfo) {
-        parseAlgorithmInfo(algInfo);
-    }
-
-    public String getCipherName() {
-        return cipherName;
-    }
-
-    public void setCipherName(String value) {
-        cipherName = value;
-    }
-
-    public String getCipherType() {
-        return cipherType;
-    }
-
-    public void setCipherType(String value) {
-        cipherType = value;
-    }
-
-    public String getCipherMode() {
-        return cipherMode;
-    }
-
-    public void setCipherMode(String value) {
-        cipherMode = value;
-    }
-
-    public String getPassword() {
-        return password;
-    }
-
-    public void setPassword(String value) {
-        password = value;
-    }
-
-    public byte[] getInitVector() {
-        return initVector;
-    }
-
-    public void setInitVector(byte... values) {
-        initVector = values;
-    }
-
-    public PrivateKeyObfuscator getPrivateKeyObfuscator() {
-        return obfuscator;
-    }
-
-    public void setPrivateKeyObfuscator(PrivateKeyObfuscator value) {
-        obfuscator = value;
-    }
-
-    public PrivateKeyObfuscator resolvePrivateKeyObfuscator() {
-        PrivateKeyObfuscator value = getPrivateKeyObfuscator();
-        if (value != null) {
-            return value;
-        }
-
-        return getRegisteredPrivateKeyObfuscator(getCipherName());
-    }
-
-    public static PrivateKeyObfuscator registerPrivateKeyObfuscator(PrivateKeyObfuscator o) {
-        return registerPrivateKeyObfuscator(Objects.requireNonNull(o, "No instance provided").getCipherName(), o);
-    }
-
-    public static PrivateKeyObfuscator registerPrivateKeyObfuscator(String cipherName, PrivateKeyObfuscator o) {
-        ValidateUtils.checkNotNullAndNotEmpty(cipherName, "No cipher name");
-        Objects.requireNonNull(o, "No instance provided");
-
-        synchronized (OBFUSCATORS) {
-            return OBFUSCATORS.put(cipherName, o);
-        }
-    }
-
-    public static boolean unregisterPrivateKeyObfuscator(PrivateKeyObfuscator o) {
-        Objects.requireNonNull(o, "No instance provided");
-        String  cipherName = o.getCipherName();
-        ValidateUtils.checkNotNullAndNotEmpty(cipherName, "No cipher name");
-
-        synchronized (OBFUSCATORS) {
-            PrivateKeyObfuscator prev = OBFUSCATORS.get(cipherName);
-            if (prev != o) {
-                return false;
-            }
-
-            OBFUSCATORS.remove(cipherName);
-        }
-
-        return true;
-    }
-
-    public static PrivateKeyObfuscator unregisterPrivateKeyObfuscator(String cipherName) {
-        ValidateUtils.checkNotNullAndNotEmpty(cipherName, "No cipher name");
-
-        synchronized (OBFUSCATORS) {
-            return OBFUSCATORS.remove(cipherName);
-        }
-    }
-
-    public static final PrivateKeyObfuscator getRegisteredPrivateKeyObfuscator(String cipherName) {
-        if (GenericUtils.isEmpty(cipherName)) {
-            return null;
-        }
-
-        synchronized (OBFUSCATORS) {
-            return OBFUSCATORS.get(cipherName);
-        }
-    }
-
-    public static final NavigableSet<String> getRegisteredPrivateKeyObfuscatorCiphers() {
-        synchronized (OBFUSCATORS) {
-            Collection<String> names = OBFUSCATORS.keySet();
-            return GenericUtils.asSortedSet(String.CASE_INSENSITIVE_ORDER, names);
-        }
-    }
-
-    public static final List<PrivateKeyObfuscator> getRegisteredPrivateKeyObfuscators() {
-        synchronized (OBFUSCATORS) {
-            Collection<? extends PrivateKeyObfuscator> l = OBFUSCATORS.values();
-            if (GenericUtils.isEmpty(l)) {
-                return Collections.emptyList();
-            } else {
-                return new ArrayList<>(l);
-            }
-        }
-    }
-
-    /**
-     * @param algInfo The algorithm info - format: <I>{@code name-type-mode}</I>
-     * @return The updated context instance
-     * @see #parseAlgorithmInfo(PrivateKeyEncryptionContext, String)
-     */
-    public PrivateKeyEncryptionContext parseAlgorithmInfo(String algInfo) {
-        return parseAlgorithmInfo(this, algInfo);
-    }
-
-    @Override
-    public PrivateKeyEncryptionContext clone() {
-        try {
-            PrivateKeyEncryptionContext copy = getClass().cast(super.clone());
-            byte[] v = copy.getInitVector();
-            if (v != null) {
-                v = v.clone();
-                copy.setInitVector(v);
-            }
-            return copy;
-        } catch (CloneNotSupportedException e) { // unexpected
-            throw new RuntimeException("Failed to clone: " + toString());
-        }
-    }
-
-    @Override
-    public int hashCode() {
-        return GenericUtils.hashCode(getCipherName(), Boolean.TRUE)
-             + GenericUtils.hashCode(getCipherType(), Boolean.TRUE)
-             + GenericUtils.hashCode(getCipherMode(), Boolean.TRUE)
-             + Objects.hashCode(getPassword())
-             + Arrays.hashCode(getInitVector());
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (obj == null) {
-            return false;
-        }
-        if (this == obj) {
-            return true;
-        }
-        if (getClass() != obj.getClass()) {
-            return false;
-        }
-
-        PrivateKeyEncryptionContext other = (PrivateKeyEncryptionContext) obj;
-        return (GenericUtils.safeCompare(getCipherName(), other.getCipherName(), false) == 0)
-            && (GenericUtils.safeCompare(getCipherType(), other.getCipherType(), false) == 0)
-            && (GenericUtils.safeCompare(getCipherMode(), other.getCipherMode(), false) == 0)
-            && (GenericUtils.safeCompare(getPassword(), other.getPassword(), true) == 0)
-            && Arrays.equals(getInitVector(), other.getInitVector());
-    }
-
-    @Override
-    public String toString() {
-        return GenericUtils.join(new String[]{getCipherName(), getCipherType(), getCipherMode()}, '-');
-    }
-
-    /**
-     * @param <C> Generic context type
-     * @param context The {@link PrivateKeyEncryptionContext} to update
-     * @param algInfo The algorithm info - format: {@code <I>name</I>-<I>type</I>-<I>mode</I>}
-     * @return The updated context
-     */
-    public static final <C extends PrivateKeyEncryptionContext> C parseAlgorithmInfo(C context, String algInfo) {
-        ValidateUtils.checkNotNullAndNotEmpty(algInfo, "No encryption algorithm data");
-
-        String[] cipherData = GenericUtils.split(algInfo, '-');
-        ValidateUtils.checkTrue(cipherData.length == 3, "Bad encryption algorithm data: %s", algInfo);
-
-        context.setCipherName(cipherData[0]);
-        context.setCipherType(cipherData[1]);
-        context.setCipherMode(cipherData[2]);
-        return context;
-    }
-
-    public static final PrivateKeyEncryptionContext newPrivateKeyEncryptionContext(PrivateKeyObfuscator o, String password) {
-        return initializeObfuscator(new PrivateKeyEncryptionContext(), o, password);
-    }
-
-    public static final <C extends PrivateKeyEncryptionContext> C initializeObfuscator(C context, PrivateKeyObfuscator o, String password) {
-        context.setCipherName(o.getCipherName());
-        context.setPrivateKeyObfuscator(o);
-        context.setPassword(password);
-        return context;
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/PrivateKeyObfuscator.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/PrivateKeyObfuscator.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/PrivateKeyObfuscator.java
deleted file mode 100644
index d8d2db5..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/PrivateKeyObfuscator.java
+++ /dev/null
@@ -1,64 +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.config.keys.loader;
-
-import java.io.IOException;
-import java.security.GeneralSecurityException;
-import java.util.List;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface PrivateKeyObfuscator {
-    /**
-     * @return Basic cipher used to obfuscate
-     */
-    String getCipherName();
-
-    /**
-     * @return A {@link List} of the supported key sizes - <B>Note:</B> every
-     * call returns a and <U>un-modifiable</U> instance.
-     */
-    List<Integer> getSupportedKeySizes();
-
-    /**
-     * @param <A> Appendable generic type
-     * @param sb The {@link Appendable} instance to update
-     * @param encContext
-     * @return Same appendable instance
-     * @throws IOException
-     */
-    <A extends Appendable> A appendPrivateKeyEncryptionContext(A sb, PrivateKeyEncryptionContext encContext) throws IOException;
-
-    /**
-     * @param encContext The encryption context
-     * @return An initialization vector suitable to the specified context
-     * @throws GeneralSecurityException
-     */
-    byte[] generateInitializationVector(PrivateKeyEncryptionContext encContext) throws GeneralSecurityException;
-
-    /**
-     * @param bytes Original bytes
-     * @param encContext The encryption context
-     * @param encryptIt If {@code true} then encrypt the original bytes, otherwise decrypt them
-     * @return The result of applying the cipher to the original bytes
-     * @throws GeneralSecurityException If cannot encrypt/decrypt
-     */
-    byte[] applyPrivateKeyCipher(byte[] bytes, PrivateKeyEncryptionContext encContext, boolean encryptIt) throws GeneralSecurityException;
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHDSSPrivateKeyEntryDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHDSSPrivateKeyEntryDecoder.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHDSSPrivateKeyEntryDecoder.java
deleted file mode 100644
index 6188a04..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHDSSPrivateKeyEntryDecoder.java
+++ /dev/null
@@ -1,139 +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.config.keys.loader.openssh;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.math.BigInteger;
-import java.security.GeneralSecurityException;
-import java.security.InvalidKeyException;
-import java.security.KeyFactory;
-import java.security.KeyPairGenerator;
-import java.security.interfaces.DSAParams;
-import java.security.interfaces.DSAPrivateKey;
-import java.security.interfaces.DSAPublicKey;
-import java.security.spec.DSAPrivateKeySpec;
-import java.security.spec.DSAPublicKeySpec;
-import java.security.spec.InvalidKeySpecException;
-import java.util.Collections;
-import java.util.Objects;
-
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.config.keys.KeyEntryResolver;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.config.keys.impl.AbstractPrivateKeyEntryDecoder;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class OpenSSHDSSPrivateKeyEntryDecoder extends AbstractPrivateKeyEntryDecoder<DSAPublicKey, DSAPrivateKey> {
-    public static final OpenSSHDSSPrivateKeyEntryDecoder INSTANCE = new OpenSSHDSSPrivateKeyEntryDecoder();
-
-    public OpenSSHDSSPrivateKeyEntryDecoder() {
-        super(DSAPublicKey.class, DSAPrivateKey.class, Collections.unmodifiableList(Collections.singletonList(KeyPairProvider.SSH_DSS)));
-    }
-
-    @Override
-    public DSAPrivateKey decodePrivateKey(String keyType, FilePasswordProvider passwordProvider, InputStream keyData)
-            throws IOException, GeneralSecurityException {
-        if (!KeyPairProvider.SSH_DSS.equals(keyType)) { // just in case we were invoked directly
-            throw new InvalidKeySpecException("Unexpected key type: " + keyType);
-        }
-
-        BigInteger p = KeyEntryResolver.decodeBigInt(keyData);
-        BigInteger q = KeyEntryResolver.decodeBigInt(keyData);
-        BigInteger g = KeyEntryResolver.decodeBigInt(keyData);
-        BigInteger y = KeyEntryResolver.decodeBigInt(keyData);
-        Objects.requireNonNull(y, "No public key data");   // TODO run some validation on it
-        BigInteger x = KeyEntryResolver.decodeBigInt(keyData);
-
-        return generatePrivateKey(new DSAPrivateKeySpec(x, p, q, g));
-    }
-
-    @Override
-    public String encodePrivateKey(OutputStream s, DSAPrivateKey key) throws IOException {
-        Objects.requireNonNull(key, "No private key provided");
-
-        DSAParams keyParams = Objects.requireNonNull(key.getParams(), "No DSA params available");
-        BigInteger p = keyParams.getP();
-        KeyEntryResolver.encodeBigInt(s, p);
-        KeyEntryResolver.encodeBigInt(s, keyParams.getQ());
-
-        BigInteger g = keyParams.getG();
-        KeyEntryResolver.encodeBigInt(s, g);
-
-        BigInteger x = key.getX();
-        BigInteger y = g.modPow(x, p);
-        KeyEntryResolver.encodeBigInt(s, y);
-        KeyEntryResolver.encodeBigInt(s, x);
-        return KeyPairProvider.SSH_DSS;
-    }
-
-    @Override
-    public boolean isPublicKeyRecoverySupported() {
-        return true;
-    }
-
-    @Override
-    public DSAPublicKey recoverPublicKey(DSAPrivateKey privateKey) throws GeneralSecurityException {
-        return KeyUtils.recoverDSAPublicKey(privateKey);
-    }
-
-    @Override
-    public DSAPublicKey clonePublicKey(DSAPublicKey key) throws GeneralSecurityException {
-        if (key == null) {
-            return null;
-        }
-
-        DSAParams params = key.getParams();
-        if (params == null) {
-            throw new InvalidKeyException("Missing parameters in key");
-        }
-
-        return generatePublicKey(new DSAPublicKeySpec(key.getY(), params.getP(), params.getQ(), params.getG()));
-    }
-
-    @Override
-    public DSAPrivateKey clonePrivateKey(DSAPrivateKey key) throws GeneralSecurityException {
-        if (key == null) {
-            return null;
-        }
-
-        DSAParams params = key.getParams();
-        if (params == null) {
-            throw new InvalidKeyException("Missing parameters in key");
-        }
-
-        return generatePrivateKey(new DSAPrivateKeySpec(key.getX(), params.getP(), params.getQ(), params.getG()));
-    }
-
-    @Override
-    public KeyPairGenerator getKeyPairGenerator() throws GeneralSecurityException {
-        return SecurityUtils.getKeyPairGenerator(KeyUtils.DSS_ALGORITHM);
-    }
-
-    @Override
-    public KeyFactory getKeyFactoryInstance() throws GeneralSecurityException {
-        return SecurityUtils.getKeyFactory(KeyUtils.DSS_ALGORITHM);
-    }
-}


[43/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common utilities code from sshd-core into sshd-common (new artifact)

Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParser.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParser.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParser.java
new file mode 100644
index 0000000..95db256
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParser.java
@@ -0,0 +1,358 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.config.keys.loader.openssh;
+
+import java.io.ByteArrayInputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StreamCorruptedException;
+import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.KeyPair;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.TreeMap;
+
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.KeyEntryResolver;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.config.keys.PrivateKeyEntryDecoder;
+import org.apache.sshd.common.config.keys.PublicKeyEntryDecoder;
+import org.apache.sshd.common.config.keys.loader.AbstractKeyPairResourceParser;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.buffer.BufferUtils;
+import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * Basic support for <A HREF="http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.key?rev=1.1&content-type=text/x-cvsweb-markup">OpenSSH key file(s)</A>
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class OpenSSHKeyPairResourceParser extends AbstractKeyPairResourceParser {
+    public static final String BEGIN_MARKER = "BEGIN OPENSSH PRIVATE KEY";
+    public static final List<String> BEGINNERS =
+            Collections.unmodifiableList(Collections.singletonList(BEGIN_MARKER));
+
+    public static final String END_MARKER = "END OPENSSH PRIVATE KEY";
+    public static final List<String> ENDERS =
+            Collections.unmodifiableList(Collections.singletonList(END_MARKER));
+
+    public static final String AUTH_MAGIC = "openssh-key-v1";
+    public static final OpenSSHKeyPairResourceParser INSTANCE = new OpenSSHKeyPairResourceParser();
+
+    private static final byte[] AUTH_MAGIC_BYTES = AUTH_MAGIC.getBytes(StandardCharsets.UTF_8);
+    private static final Map<String, PrivateKeyEntryDecoder<?, ?>> BY_KEY_TYPE_DECODERS_MAP =
+            new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+
+    private static final Map<Class<?>, PrivateKeyEntryDecoder<?, ?>> BY_KEY_CLASS_DECODERS_MAP =
+            new HashMap<>();
+
+    static {
+        registerPrivateKeyEntryDecoder(OpenSSHRSAPrivateKeyDecoder.INSTANCE);
+        registerPrivateKeyEntryDecoder(OpenSSHDSSPrivateKeyEntryDecoder.INSTANCE);
+
+        if (SecurityUtils.isECCSupported()) {
+            registerPrivateKeyEntryDecoder(OpenSSHECDSAPrivateKeyEntryDecoder.INSTANCE);
+        }
+        if (SecurityUtils.isEDDSACurveSupported()) {
+            registerPrivateKeyEntryDecoder(SecurityUtils.getOpenSSHEDDSAPrivateKeyEntryDecoder());
+        }
+    }
+
+    public OpenSSHKeyPairResourceParser() {
+        super(BEGINNERS, ENDERS);
+    }
+
+    @Override
+    public Collection<KeyPair> extractKeyPairs(
+            String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, InputStream stream)
+                    throws IOException, GeneralSecurityException {
+        stream = validateStreamMagicMarker(resourceKey, stream);
+
+        String cipher = KeyEntryResolver.decodeString(stream);
+        if (!OpenSSHParserContext.IS_NONE_CIPHER.test(cipher)) {
+            throw new NoSuchAlgorithmException("Unsupported cipher: " + cipher);
+        }
+
+        boolean debugEnabled = log.isDebugEnabled();
+        if (debugEnabled) {
+            log.debug("extractKeyPairs({}) cipher={}", resourceKey, cipher);
+        }
+
+        String kdfName = KeyEntryResolver.decodeString(stream);
+        if (!OpenSSHParserContext.IS_NONE_KDF.test(kdfName)) {
+            throw new NoSuchAlgorithmException("Unsupported KDF: " + kdfName);
+        }
+
+        byte[] kdfOptions = KeyEntryResolver.readRLEBytes(stream);
+        if (debugEnabled) {
+            log.debug("extractKeyPairs({}) KDF={}, options={}",
+                      resourceKey, kdfName, BufferUtils.toHex(':', kdfOptions));
+        }
+
+        int numKeys = KeyEntryResolver.decodeInt(stream);
+        if (numKeys <= 0) {
+            if (debugEnabled) {
+                log.debug("extractKeyPairs({}) no encoded keys", resourceKey);
+            }
+            return Collections.emptyList();
+        }
+
+        List<PublicKey> publicKeys = new ArrayList<>(numKeys);
+        OpenSSHParserContext context = new OpenSSHParserContext(cipher, kdfName, kdfOptions);
+        boolean traceEnabled = log.isTraceEnabled();
+        for (int index = 1; index <= numKeys; index++) {
+            PublicKey pubKey = readPublicKey(resourceKey, context, stream);
+            ValidateUtils.checkNotNull(pubKey, "Empty public key #%d in %s", index, resourceKey);
+            if (traceEnabled) {
+                log.trace("extractKeyPairs({}) read public key #{}: {} {}",
+                          resourceKey, index, KeyUtils.getKeyType(pubKey), KeyUtils.getFingerPrint(pubKey));
+            }
+            publicKeys.add(pubKey);
+        }
+
+        byte[] privateData = KeyEntryResolver.readRLEBytes(stream);
+        try (InputStream bais = new ByteArrayInputStream(privateData)) {
+            return readPrivateKeys(resourceKey, context, publicKeys, passwordProvider, bais);
+        }
+    }
+
+    protected PublicKey readPublicKey(
+            String resourceKey, OpenSSHParserContext context, InputStream stream)
+                    throws IOException, GeneralSecurityException {
+        byte[] keyData = KeyEntryResolver.readRLEBytes(stream);
+        try (InputStream bais = new ByteArrayInputStream(keyData)) {
+            String keyType = KeyEntryResolver.decodeString(bais);
+            PublicKeyEntryDecoder<?, ?> decoder = KeyUtils.getPublicKeyEntryDecoder(keyType);
+            if (decoder == null) {
+                throw new NoSuchAlgorithmException("Unsupported key type (" + keyType + ") in " + resourceKey);
+            }
+
+            return decoder.decodePublicKey(keyType, bais);
+        }
+    }
+
+    protected List<KeyPair> readPrivateKeys(
+            String resourceKey, OpenSSHParserContext context, Collection<? extends PublicKey> publicKeys,
+            FilePasswordProvider passwordProvider, InputStream stream)
+                    throws IOException, GeneralSecurityException {
+        if (GenericUtils.isEmpty(publicKeys)) {
+            return Collections.emptyList();
+        }
+
+        boolean traceEnabled = log.isTraceEnabled();
+        int check1 = KeyEntryResolver.decodeInt(stream);
+        int check2 = KeyEntryResolver.decodeInt(stream);
+        if (traceEnabled) {
+            log.trace("readPrivateKeys({}) check1=0x{}, check2=0x{}",
+                      resourceKey, Integer.toHexString(check1), Integer.toHexString(check2));
+        }
+
+        List<KeyPair> keyPairs = new ArrayList<>(publicKeys.size());
+        for (PublicKey pubKey : publicKeys) {
+            String pubType = KeyUtils.getKeyType(pubKey);
+            int keyIndex = keyPairs.size() + 1;
+            if (traceEnabled) {
+                log.trace("extractKeyPairs({}) read private key #{}: {}",
+                        resourceKey, keyIndex, pubType);
+            }
+
+            Map.Entry<PrivateKey, String> prvData = readPrivateKey(resourceKey, context, pubType, passwordProvider, stream);
+            PrivateKey prvKey = (prvData == null) ? null : prvData.getKey();
+            ValidateUtils.checkNotNull(prvKey, "Empty private key #%d in %s", keyIndex, resourceKey);
+
+            String prvType = KeyUtils.getKeyType(prvKey);
+            ValidateUtils.checkTrue(Objects.equals(pubType, prvType),
+                    "Mismatched public (%s) vs. private (%s) key type #%d in %s",
+                    pubType, prvType, keyIndex, resourceKey);
+
+            if (traceEnabled) {
+                log.trace("extractKeyPairs({}) add private key #{}: {} {}",
+                        resourceKey, keyIndex, prvType, prvData.getValue());
+            }
+            keyPairs.add(new KeyPair(pubKey, prvKey));
+        }
+
+        return keyPairs;
+    }
+
+    protected SimpleImmutableEntry<PrivateKey, String> readPrivateKey(
+            String resourceKey, OpenSSHParserContext context, String keyType, FilePasswordProvider passwordProvider, InputStream stream)
+                    throws IOException, GeneralSecurityException {
+        String prvType = KeyEntryResolver.decodeString(stream);
+        if (!Objects.equals(keyType, prvType)) {
+            throw new StreamCorruptedException("Mismatched private key type: "
+                    + ", expected=" + keyType + ", actual=" + prvType
+                    + " in " + resourceKey);
+        }
+
+        PrivateKeyEntryDecoder<?, ?> decoder = getPrivateKeyEntryDecoder(prvType);
+        if (decoder == null) {
+            throw new NoSuchAlgorithmException("Unsupported key type (" + prvType + ") in " + resourceKey);
+        }
+
+        PrivateKey prvKey = decoder.decodePrivateKey(prvType, passwordProvider, stream);
+        if (prvKey == null) {
+            throw new InvalidKeyException("Cannot parse key type (" + prvType + ") in " + resourceKey);
+        }
+
+        String comment = KeyEntryResolver.decodeString(stream);
+        return new SimpleImmutableEntry<>(prvKey, comment);
+    }
+
+    protected <S extends InputStream> S validateStreamMagicMarker(String resourceKey, S stream) throws IOException {
+        byte[] actual = new byte[AUTH_MAGIC_BYTES.length];
+        IoUtils.readFully(stream, actual);
+        if (!Arrays.equals(AUTH_MAGIC_BYTES, actual)) {
+            throw new StreamCorruptedException(resourceKey + ": Mismatched magic marker value: " + BufferUtils.toHex(':', actual));
+        }
+
+        int eos = stream.read();
+        if (eos == -1) {
+            throw new EOFException(resourceKey + ": Premature EOF after magic marker value");
+        }
+
+        if (eos != 0) {
+            throw new StreamCorruptedException(resourceKey + ": Missing EOS after magic marker value: 0x" + Integer.toHexString(eos));
+        }
+
+        return stream;
+    }
+
+    /**
+     * @param decoder The decoder to register
+     * @throws IllegalArgumentException if no decoder or not key type or no
+     *                                  supported names for the decoder
+     * @see PrivateKeyEntryDecoder#getPublicKeyType()
+     * @see PrivateKeyEntryDecoder#getSupportedTypeNames()
+     */
+    public static void registerPrivateKeyEntryDecoder(PrivateKeyEntryDecoder<?, ?> decoder) {
+        Objects.requireNonNull(decoder, "No decoder specified");
+
+        Class<?> pubType = Objects.requireNonNull(decoder.getPublicKeyType(), "No public key type declared");
+        Class<?> prvType = Objects.requireNonNull(decoder.getPrivateKeyType(), "No private key type declared");
+        synchronized (BY_KEY_CLASS_DECODERS_MAP) {
+            BY_KEY_CLASS_DECODERS_MAP.put(pubType, decoder);
+            BY_KEY_CLASS_DECODERS_MAP.put(prvType, decoder);
+        }
+
+        Collection<String> names = ValidateUtils.checkNotNullAndNotEmpty(decoder.getSupportedTypeNames(), "No supported key type");
+        synchronized (BY_KEY_TYPE_DECODERS_MAP) {
+            for (String n : names) {
+                PrivateKeyEntryDecoder<?, ?> prev = BY_KEY_TYPE_DECODERS_MAP.put(n, decoder);
+                if (prev != null) {
+                    //noinspection UnnecessaryContinue
+                    continue;   // debug breakpoint
+                }
+            }
+        }
+    }
+
+    /**
+     * @param keyType The {@code OpenSSH} key type string -  e.g., {@code ssh-rsa, ssh-dss}
+     *                - ignored if {@code null}/empty
+     * @return The registered {@link PrivateKeyEntryDecoder} or {code null} if not found
+     */
+    public static PrivateKeyEntryDecoder<?, ?> getPrivateKeyEntryDecoder(String keyType) {
+        if (GenericUtils.isEmpty(keyType)) {
+            return null;
+        }
+
+        synchronized (BY_KEY_TYPE_DECODERS_MAP) {
+            return BY_KEY_TYPE_DECODERS_MAP.get(keyType);
+        }
+    }
+
+    /**
+     * @param kp The {@link KeyPair} to examine - ignored if {@code null}
+     * @return The matching {@link PrivateKeyEntryDecoder} provided <U>both</U>
+     * the public and private keys have the same decoder - {@code null} if no
+     * match found
+     * @see #getPrivateKeyEntryDecoder(Key)
+     */
+    public static PrivateKeyEntryDecoder<?, ?> getPrivateKeyEntryDecoder(KeyPair kp) {
+        if (kp == null) {
+            return null;
+        }
+
+        PrivateKeyEntryDecoder<?, ?> d1 = getPrivateKeyEntryDecoder(kp.getPublic());
+        PrivateKeyEntryDecoder<?, ?> d2 = getPrivateKeyEntryDecoder(kp.getPrivate());
+        if (d1 == d2) {
+            return d1;
+        } else {
+            return null;    // some kind of mixed keys...
+        }
+    }
+
+    /**
+     * @param key The {@link Key} (public or private) - ignored if {@code null}
+     * @return The registered {@link PrivateKeyEntryDecoder} for this key or {code null} if no match found
+     * @see #getPrivateKeyEntryDecoder(Class)
+     */
+    public static PrivateKeyEntryDecoder<?, ?> getPrivateKeyEntryDecoder(Key key) {
+        if (key == null) {
+            return null;
+        } else {
+            return getPrivateKeyEntryDecoder(key.getClass());
+        }
+    }
+
+    /**
+     * @param keyType The key {@link Class} - ignored if {@code null} or not a {@link Key}
+     *                compatible type
+     * @return The registered {@link PrivateKeyEntryDecoder} or {code null} if no match found
+     */
+    public static PrivateKeyEntryDecoder<?, ?> getPrivateKeyEntryDecoder(Class<?> keyType) {
+        if ((keyType == null) || (!Key.class.isAssignableFrom(keyType))) {
+            return null;
+        }
+
+        synchronized (BY_KEY_TYPE_DECODERS_MAP) {
+            PrivateKeyEntryDecoder<?, ?> decoder = BY_KEY_CLASS_DECODERS_MAP.get(keyType);
+            if (decoder != null) {
+                return decoder;
+            }
+
+            // in case it is a derived class
+            for (PrivateKeyEntryDecoder<?, ?> dec : BY_KEY_CLASS_DECODERS_MAP.values()) {
+                Class<?> pubType = dec.getPublicKeyType();
+                Class<?> prvType = dec.getPrivateKeyType();
+                if (pubType.isAssignableFrom(keyType) || prvType.isAssignableFrom(keyType)) {
+                    return dec;
+                }
+            }
+        }
+
+        return null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHParserContext.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHParserContext.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHParserContext.java
new file mode 100644
index 0000000..07f2a9a
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHParserContext.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config.keys.loader.openssh;
+
+import java.util.function.Predicate;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.buffer.BufferUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class OpenSSHParserContext {
+    public static final String NONE_CIPHER = "none";
+    public static final Predicate<String> IS_NONE_CIPHER = c -> GenericUtils.isEmpty(c) || NONE_CIPHER.equalsIgnoreCase(c);
+
+    public static final String NONE_KDF = "none";
+    public static final Predicate<String> IS_NONE_KDF = c -> GenericUtils.isEmpty(c) || NONE_KDF.equalsIgnoreCase(c);
+
+    private String cipherName;
+    private String kdfName;
+    private byte[] kdfOptions;
+
+    public OpenSSHParserContext() {
+        super();
+    }
+
+    public OpenSSHParserContext(String cipherName, String kdfName, byte... kdfOptions) {
+        this.cipherName = cipherName;
+        this.kdfName = kdfName;
+        this.kdfOptions = kdfOptions;
+    }
+
+    public String getCipherName() {
+        return cipherName;
+    }
+
+    public void setCipherName(String cipherName) {
+        this.cipherName = cipherName;
+    }
+
+    public String getKdfName() {
+        return kdfName;
+    }
+
+    public void setKdfName(String kdfName) {
+        this.kdfName = kdfName;
+    }
+
+    public byte[] getKdfOptions() {
+        return kdfOptions;
+    }
+
+    public void setKdfOptions(byte[] kdfOptions) {
+        this.kdfOptions = kdfOptions;
+    }
+
+    @Override
+    public String toString() {
+        return getClass().getSimpleName()
+            + "[cipher=" + getCipherName()
+            + ", kdfName=" + getKdfName()
+            + ", kdfOptions=" + BufferUtils.toHex(':', getKdfOptions())
+            + "]";
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHRSAPrivateKeyDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHRSAPrivateKeyDecoder.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHRSAPrivateKeyDecoder.java
new file mode 100644
index 0000000..72e003f
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHRSAPrivateKeyDecoder.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config.keys.loader.openssh;
+
+import java.io.IOException;
+import java.io.InputStream;
+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.RSAPrivateKeySpec;
+import java.security.spec.RSAPublicKeySpec;
+import java.util.Collections;
+import java.util.Objects;
+
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.KeyEntryResolver;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.config.keys.impl.AbstractPrivateKeyEntryDecoder;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class OpenSSHRSAPrivateKeyDecoder extends AbstractPrivateKeyEntryDecoder<RSAPublicKey, RSAPrivateKey> {
+    public static final BigInteger DEFAULT_PUBLIC_EXPONENT = new BigInteger("65537");
+    public static final OpenSSHRSAPrivateKeyDecoder INSTANCE = new OpenSSHRSAPrivateKeyDecoder();
+
+    public OpenSSHRSAPrivateKeyDecoder() {
+        super(RSAPublicKey.class, RSAPrivateKey.class, Collections.unmodifiableList(Collections.singletonList(KeyPairProvider.SSH_RSA)));
+    }
+
+    @Override
+    public RSAPrivateKey decodePrivateKey(String keyType, FilePasswordProvider passwordProvider, InputStream keyData)
+            throws IOException, GeneralSecurityException {
+        if (!KeyPairProvider.SSH_RSA.equals(keyType)) { // just in case we were invoked directly
+            throw new InvalidKeySpecException("Unexpected key type: " + keyType);
+        }
+
+        BigInteger n = KeyEntryResolver.decodeBigInt(keyData);
+        BigInteger e = KeyEntryResolver.decodeBigInt(keyData);
+        if (!Objects.equals(e, DEFAULT_PUBLIC_EXPONENT)) {
+            log.warn("decodePrivateKey({}) non-standard RSA exponent found: {}", keyType, e);
+        }
+
+        BigInteger d = KeyEntryResolver.decodeBigInt(keyData);
+        BigInteger inverseQmodP = KeyEntryResolver.decodeBigInt(keyData);
+        Objects.requireNonNull(inverseQmodP, "Missing iqmodp"); // TODO run some validation on it
+        BigInteger p = KeyEntryResolver.decodeBigInt(keyData);
+        BigInteger q = KeyEntryResolver.decodeBigInt(keyData);
+        BigInteger modulus = p.multiply(q);
+        if (!Objects.equals(n, modulus)) {
+            log.warn("decodePrivateKey({}) mismatched modulus values: encoded={}, calculated={}",
+                     keyType, n, modulus);
+        }
+
+        return generatePrivateKey(new RSAPrivateKeySpec(n, d));
+    }
+
+    @Override
+    public boolean isPublicKeyRecoverySupported() {
+        return true;
+    }
+
+    @Override
+    public RSAPublicKey recoverPublicKey(RSAPrivateKey privateKey) throws GeneralSecurityException {
+        return KeyUtils.recoverRSAPublicKey(privateKey);
+    }
+
+    @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(KeyUtils.RSA_ALGORITHM);
+    }
+
+    @Override
+    public KeyFactory getKeyFactoryInstance() throws GeneralSecurityException {
+        return SecurityUtils.getKeyFactory(KeyUtils.RSA_ALGORITHM);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/AbstractPEMResourceKeyPairParser.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/AbstractPEMResourceKeyPairParser.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/AbstractPEMResourceKeyPairParser.java
new file mode 100644
index 0000000..bee13d6
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/AbstractPEMResourceKeyPairParser.java
@@ -0,0 +1,167 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.config.keys.loader.pem;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StreamCorruptedException;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.security.NoSuchAlgorithmException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import javax.security.auth.login.CredentialException;
+import javax.security.auth.login.FailedLoginException;
+
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.loader.AbstractKeyPairResourceParser;
+import org.apache.sshd.common.config.keys.loader.KeyPairResourceParser;
+import org.apache.sshd.common.config.keys.loader.PrivateKeyEncryptionContext;
+import org.apache.sshd.common.config.keys.loader.PrivateKeyObfuscator;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.buffer.BufferUtils;
+
+/**
+ * Base class for PEM file key-pair loaders
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractPEMResourceKeyPairParser
+        extends AbstractKeyPairResourceParser
+        implements KeyPairPEMResourceParser {
+    private final String algo;
+    private final String algId;
+
+    protected AbstractPEMResourceKeyPairParser(String algo, String algId, List<String> beginners, List<String> enders) {
+        super(beginners, enders);
+        this.algo = ValidateUtils.checkNotNullAndNotEmpty(algo, "No encryption algorithm provided");
+        this.algId = ValidateUtils.checkNotNullAndNotEmpty(algId, "No algorithm identifier provided");
+    }
+
+    @Override
+    public String getAlgorithm() {
+        return algo;
+    }
+
+    @Override
+    public String getAlgorithmIdentifier() {
+        return algId;
+    }
+
+    @Override
+    public Collection<KeyPair> extractKeyPairs(
+            String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, List<String> lines)
+                    throws IOException, GeneralSecurityException {
+        if (GenericUtils.isEmpty(lines)) {
+            return Collections.emptyList();
+        }
+
+        Boolean encrypted = null;
+        byte[] initVector = null;
+        String algInfo = null;
+        int dataStartIndex = -1;
+        for (int index = 0; index < lines.size(); index++) {
+            String line = GenericUtils.trimToEmpty(lines.get(index));
+            if (GenericUtils.isEmpty(line)) {
+                continue;
+            }
+
+            // check if header line - if not, assume data lines follow
+            int headerPos = line.indexOf(':');
+            if (headerPos < 0) {
+                dataStartIndex = index;
+                break;
+            }
+
+            if (line.startsWith("Proc-Type:")) {
+                if (encrypted != null) {
+                    throw new StreamCorruptedException("Multiple encryption indicators in " + resourceKey);
+                }
+
+                line = line.substring(headerPos + 1).trim();
+                line = line.toUpperCase();
+                encrypted = Boolean.valueOf(line.contains("ENCRYPTED"));
+            } else if (line.startsWith("DEK-Info:")) {
+                if ((initVector != null) || (algInfo != null)) {
+                    throw new StreamCorruptedException("Multiple encryption settings in " + resourceKey);
+                }
+
+                line = line.substring(headerPos + 1).trim();
+                headerPos = line.indexOf(',');
+                if (headerPos < 0) {
+                    throw new StreamCorruptedException(resourceKey + ": Missing encryption data values separator in line '" + line + "'");
+                }
+
+                algInfo = line.substring(0, headerPos).trim();
+
+                String algInitVector = line.substring(headerPos + 1).trim();
+                initVector = BufferUtils.decodeHex(BufferUtils.EMPTY_HEX_SEPARATOR, algInitVector);
+            }
+        }
+
+        if (dataStartIndex < 0) {
+            throw new StreamCorruptedException("No data lines (only headers or empty) found in " + resourceKey);
+        }
+
+        List<String> dataLines = lines.subList(dataStartIndex, lines.size());
+        if ((encrypted != null) || (algInfo != null) || (initVector != null)) {
+            if (passwordProvider == null) {
+                throw new CredentialException("Missing password provider for encrypted resource=" + resourceKey);
+            }
+
+            String password = passwordProvider.getPassword(resourceKey);
+            if (GenericUtils.isEmpty(password)) {
+                throw new FailedLoginException("No password data for encrypted resource=" + resourceKey);
+            }
+
+            PrivateKeyEncryptionContext encContext = new PrivateKeyEncryptionContext(algInfo);
+            encContext.setPassword(password);
+            encContext.setInitVector(initVector);
+            byte[] encryptedData = KeyPairResourceParser.extractDataBytes(dataLines);
+            byte[] decodedData = applyPrivateKeyCipher(encryptedData, encContext, false);
+            try (InputStream bais = new ByteArrayInputStream(decodedData)) {
+                return extractKeyPairs(resourceKey, beginMarker, endMarker, passwordProvider, bais);
+            }
+        }
+
+        return super.extractKeyPairs(resourceKey, beginMarker, endMarker, passwordProvider, dataLines);
+    }
+
+    protected byte[] applyPrivateKeyCipher(byte[] bytes, PrivateKeyEncryptionContext encContext, boolean encryptIt) throws GeneralSecurityException {
+        String cipherName = encContext.getCipherName();
+        PrivateKeyObfuscator o = encContext.resolvePrivateKeyObfuscator();
+        if (o == null) {
+            throw new NoSuchAlgorithmException("decryptPrivateKeyData(" + encContext + ")[encrypt=" + encryptIt + "] unknown cipher: " + cipherName);
+        }
+
+        if (encryptIt) {
+            byte[]  initVector = encContext.getInitVector();
+            if (GenericUtils.isEmpty(initVector)) {
+                initVector = o.generateInitializationVector(encContext);
+                encContext.setInitVector(initVector);
+            }
+        }
+
+        return o.applyPrivateKeyCipher(bytes, encContext, encryptIt);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/DSSPEMResourceKeyPairParser.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/DSSPEMResourceKeyPairParser.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/DSSPEMResourceKeyPairParser.java
new file mode 100644
index 0000000..0c357af
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/DSSPEMResourceKeyPairParser.java
@@ -0,0 +1,126 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config.keys.loader.pem;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StreamCorruptedException;
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.spec.DSAPrivateKeySpec;
+import java.security.spec.DSAPublicKeySpec;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.util.io.NoCloseInputStream;
+import org.apache.sshd.common.util.io.der.ASN1Object;
+import org.apache.sshd.common.util.io.der.ASN1Type;
+import org.apache.sshd.common.util.io.der.DERParser;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class DSSPEMResourceKeyPairParser extends AbstractPEMResourceKeyPairParser {
+    // Not exactly according to standard but good enough
+    public static final String BEGIN_MARKER = "BEGIN DSA PRIVATE KEY";
+    public static final List<String> BEGINNERS =
+            Collections.unmodifiableList(Collections.singletonList(BEGIN_MARKER));
+
+    public static final String END_MARKER = "END DSA PRIVATE KEY";
+    public static final List<String> ENDERS =
+            Collections.unmodifiableList(Collections.singletonList(END_MARKER));
+
+    /**
+     * @see <A HREF="https://tools.ietf.org/html/rfc3279#section-2.3.2">RFC-3279 section 2.3.2</A>
+     */
+    public static final String DSS_OID = "1.2.840.10040.4.1";
+
+    public static final DSSPEMResourceKeyPairParser INSTANCE = new DSSPEMResourceKeyPairParser();
+
+    public DSSPEMResourceKeyPairParser() {
+        super(KeyUtils.DSS_ALGORITHM, DSS_OID, BEGINNERS, ENDERS);
+    }
+
+    @Override
+    public Collection<KeyPair> extractKeyPairs(
+            String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, InputStream stream)
+                    throws IOException, GeneralSecurityException {
+        KeyPair kp = decodeDSSKeyPair(SecurityUtils.getKeyFactory(KeyUtils.DSS_ALGORITHM), stream, false);
+        return Collections.singletonList(kp);
+    }
+
+    /**
+     * <p>The ASN.1 syntax for the private key:</P>
+     * <pre><code>
+     * DSAPrivateKey ::= SEQUENCE {
+     *      version Version,
+     *      p       INTEGER,
+     *      q       INTEGER,
+     *      g       INTEGER,
+     *      y       INTEGER,
+     *      x       INTEGER
+     * }
+     * </code></pre>
+     * @param kf The {@link KeyFactory} To use to generate the keys
+     * @param s The {@link InputStream} containing the encoded bytes
+     * @param okToClose <code>true</code> if the method may close the input
+     * stream regardless of success or failure
+     * @return The recovered {@link KeyPair}
+     * @throws IOException If failed to read or decode the bytes
+     * @throws GeneralSecurityException If failed to generate the keys
+     */
+    public static KeyPair decodeDSSKeyPair(KeyFactory kf, InputStream s, boolean okToClose)
+            throws IOException, GeneralSecurityException {
+        ASN1Object sequence;
+        try (DERParser parser = new DERParser(NoCloseInputStream.resolveInputStream(s, okToClose))) {
+            sequence = parser.readObject();
+        }
+
+        if (!ASN1Type.SEQUENCE.equals(sequence.getObjType())) {
+            throw new IOException("Invalid DER: not a sequence: " + sequence.getObjType());
+        }
+
+        // Parse inside the sequence
+        try (DERParser parser = sequence.createParser()) {
+            // Skip version
+            ASN1Object version = parser.readObject();
+            if (version == null) {
+                throw new StreamCorruptedException("No version");
+            }
+
+            BigInteger p = parser.readObject().asInteger();
+            BigInteger q = parser.readObject().asInteger();
+            BigInteger g = parser.readObject().asInteger();
+            BigInteger y = parser.readObject().asInteger();
+            BigInteger x = parser.readObject().asInteger();
+            PublicKey pubKey = kf.generatePublic(new DSAPublicKeySpec(y, p, q, g));
+            PrivateKey prvKey = kf.generatePrivate(new DSAPrivateKeySpec(x, p, q, g));
+            return new KeyPair(pubKey, prvKey);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/ECDSAPEMResourceKeyPairParser.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/ECDSAPEMResourceKeyPairParser.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/ECDSAPEMResourceKeyPairParser.java
new file mode 100644
index 0000000..dd8d2ea
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/ECDSAPEMResourceKeyPairParser.java
@@ -0,0 +1,220 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config.keys.loader.pem;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StreamCorruptedException;
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.NoSuchProviderException;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.spec.ECPoint;
+import java.security.spec.ECPrivateKeySpec;
+import java.security.spec.ECPublicKeySpec;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.sshd.common.cipher.ECCurves;
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.util.io.NoCloseInputStream;
+import org.apache.sshd.common.util.io.der.ASN1Object;
+import org.apache.sshd.common.util.io.der.ASN1Type;
+import org.apache.sshd.common.util.io.der.DERParser;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class ECDSAPEMResourceKeyPairParser extends AbstractPEMResourceKeyPairParser {
+    public static final String BEGIN_MARKER = "BEGIN EC PRIVATE KEY";
+    public static final List<String> BEGINNERS =
+            Collections.unmodifiableList(Collections.singletonList(BEGIN_MARKER));
+
+    public static final String END_MARKER = "END EC PRIVATE KEY";
+    public static final List<String> ENDERS =
+            Collections.unmodifiableList(Collections.singletonList(END_MARKER));
+
+    /**
+     * @see <A HREF="https://tools.ietf.org/html/rfc3279#section-2.3.5">RFC-3279 section 2.3.5</A>
+     */
+    public static final String ECDSA_OID = "1.2.840.10045.2.1";
+
+    public static final ECDSAPEMResourceKeyPairParser INSTANCE = new ECDSAPEMResourceKeyPairParser();
+
+    public ECDSAPEMResourceKeyPairParser() {
+        super(KeyUtils.EC_ALGORITHM, ECDSA_OID, BEGINNERS, ENDERS);
+    }
+
+    @Override
+    public Collection<KeyPair> extractKeyPairs(
+            String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, InputStream stream)
+                    throws IOException, GeneralSecurityException {
+        Map.Entry<ECPublicKeySpec, ECPrivateKeySpec> spec = decodeECPrivateKeySpec(stream, false);
+        if (!SecurityUtils.isECCSupported()) {
+            throw new NoSuchProviderException("ECC not supported");
+        }
+
+        KeyFactory kf = SecurityUtils.getKeyFactory(KeyUtils.EC_ALGORITHM);
+        ECPublicKey pubKey = (ECPublicKey) kf.generatePublic(spec.getKey());
+        ECPrivateKey prvKey = (ECPrivateKey) kf.generatePrivate(spec.getValue());
+        KeyPair kp = new KeyPair(pubKey, prvKey);
+        return Collections.singletonList(kp);
+    }
+
+    /**
+     * <P>ASN.1 syntax according to rfc5915 is:</P></BR>
+     * <PRE><CODE>
+     * ECPrivateKey ::= SEQUENCE {
+     *      version        INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1),
+     *      privateKey     OCTET STRING,
+     *      parameters [0] ECParameters {{ NamedCurve }} OPTIONAL,
+     *      publicKey  [1] BIT STRING OPTIONAL
+     * }
+     * </CODE></PRE>
+     * <P><I>ECParameters</I> syntax according to RFC5480:</P></BR>
+     * <PRE><CODE>
+     * ECParameters ::= CHOICE {
+     *      namedCurve         OBJECT IDENTIFIER
+     *      -- implicitCurve   NULL
+     *      -- specifiedCurve  SpecifiedECDomain
+     * }
+     * </CODE></PRE>
+     * @param inputStream The {@link InputStream} containing the DER encoded data
+     * @param okToClose {@code true} if OK to close the DER stream once parsing complete
+     * @return The decoded {@link SimpleImmutableEntry} of {@link ECPublicKeySpec} and {@link ECPrivateKeySpec}
+     * @throws IOException If failed to to decode the DER stream
+     */
+    public static SimpleImmutableEntry<ECPublicKeySpec, ECPrivateKeySpec> decodeECPrivateKeySpec(InputStream inputStream, boolean okToClose) throws IOException {
+        ASN1Object sequence;
+        try (DERParser parser = new DERParser(NoCloseInputStream.resolveInputStream(inputStream, okToClose))) {
+            sequence = parser.readObject();
+        }
+
+        if (!ASN1Type.SEQUENCE.equals(sequence.getObjType())) {
+            throw new IOException("Invalid DER: not a sequence: " + sequence.getObjType());
+        }
+
+        // Parse inside the sequence
+        try (DERParser parser = sequence.createParser()) {
+            ECPrivateKeySpec prvSpec = decodeECPrivateKeySpec(parser);
+            ECCurves curve = ECCurves.fromCurveParameters(prvSpec.getParams());
+            if (curve == null) {
+                throw new StreamCorruptedException("Unknown curve");
+            }
+
+            ECPoint w = decodeECPublicKeyValue(curve, parser);
+            ECPublicKeySpec pubSpec = new ECPublicKeySpec(w, prvSpec.getParams());
+            return new SimpleImmutableEntry<>(pubSpec, prvSpec);
+        }
+    }
+
+    public static final ECPrivateKeySpec decodeECPrivateKeySpec(DERParser parser) throws IOException {
+        // see openssl asn1parse -inform PEM -in ...file... -dump
+        ASN1Object versionObject = parser.readObject(); // Skip version
+        if (versionObject == null) {
+            throw new StreamCorruptedException("No version");
+        }
+
+        // as per RFC-5915 section 3
+        BigInteger version = versionObject.asInteger();
+        if (!BigInteger.ONE.equals(version)) {
+            throw new StreamCorruptedException("Bad version value: " + version);
+        }
+
+        ASN1Object keyObject = parser.readObject();
+        if (keyObject == null) {
+            throw new StreamCorruptedException("No private key value");
+        }
+
+        ASN1Type objType = keyObject.getObjType();
+        if (!ASN1Type.OCTET_STRING.equals(objType)) {
+            throw new StreamCorruptedException("Non-matching private key object type: " + objType);
+        }
+
+        ASN1Object paramsObject = parser.readObject();
+        if (paramsObject == null) {
+            throw new StreamCorruptedException("No parameters value");
+        }
+
+        // TODO make sure params object tag is 0xA0
+
+        final List<Integer> curveOID;
+        try (DERParser paramsParser = paramsObject.createParser()) {
+            ASN1Object namedCurve = paramsParser.readObject();
+            if (namedCurve == null) {
+                throw new StreamCorruptedException("Missing named curve parameter");
+            }
+
+            curveOID = namedCurve.asOID();
+        }
+
+        ECCurves curve = ECCurves.fromOIDValue(curveOID);
+        if (curve == null) {
+            throw new StreamCorruptedException("Unknown curve OID: " + curveOID);
+        }
+
+        BigInteger s = ECCurves.octetStringToInteger(keyObject.getPureValueBytes());
+        return new ECPrivateKeySpec(s, curve.getParameters());
+    }
+
+    /**
+     * <P>ASN.1 syntax according to rfc5915 is:</P></BR>
+     * <PRE>
+     *      publicKey  [1] BIT STRING OPTIONAL
+     * </PRE>
+     * @param curve The {@link ECCurves} curve
+     * @param parser The {@link DERParser} assumed to be positioned at the
+     * start of the data
+     * @return The encoded {@link ECPoint}
+     * @throws IOException If failed to create the point
+     */
+    public static final ECPoint decodeECPublicKeyValue(ECCurves curve, DERParser parser) throws IOException {
+        // see openssl asn1parse -inform PEM -in ...file... -dump
+        ASN1Object dataObject = parser.readObject();
+        if (dataObject == null) {
+            throw new StreamCorruptedException("No public key data bytes");
+        }
+
+        try (DERParser dataParser = dataObject.createParser()) {
+            ASN1Object pointData = dataParser.readObject();
+            if (pointData == null) {
+                throw new StreamCorruptedException("Missing public key data parameter");
+            }
+
+            ASN1Type objType = pointData.getObjType();
+            if (!ASN1Type.BIT_STRING.equals(objType)) {
+                throw new StreamCorruptedException("Non-matching public key object type: " + objType);
+            }
+
+            // see https://tools.ietf.org/html/rfc5480#section-2.2
+            byte[] octets = pointData.getValue();
+            return ECCurves.octetStringToEcPoint(octets);
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/KeyPairPEMResourceParser.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/KeyPairPEMResourceParser.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/KeyPairPEMResourceParser.java
new file mode 100644
index 0000000..07c7e44
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/KeyPairPEMResourceParser.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config.keys.loader.pem;
+
+import org.apache.sshd.common.config.keys.loader.KeyPairResourceParser;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface KeyPairPEMResourceParser extends KeyPairResourceParser {
+    /**
+     * @return The encryption algorithm name - e.g., &quot;RSA&quot;, &quot;DSA&quot;
+     */
+    String getAlgorithm();
+
+    /**
+     * @return The OID used to identify this algorithm in DER encodings - e.g., RSA=1.2.840.113549.1.1.1
+     */
+    String getAlgorithmIdentifier();
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PEMResourceParserUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PEMResourceParserUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PEMResourceParserUtils.java
new file mode 100644
index 0000000..b6749da
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PEMResourceParserUtils.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config.keys.loader.pem;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.TreeMap;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.loader.KeyPairResourceParser;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class PEMResourceParserUtils {
+    public static final KeyPairResourceParser PROXY = new KeyPairResourceParser() {
+        @Override
+        public Collection<KeyPair> loadKeyPairs(
+                String resourceKey, FilePasswordProvider passwordProvider, List<String> lines)
+                        throws IOException, GeneralSecurityException {
+            @SuppressWarnings("synthetic-access")
+            KeyPairResourceParser proxy = PROXY_HOLDER.get();
+            return (proxy == null) ? Collections.<KeyPair>emptyList() : proxy.loadKeyPairs(resourceKey, passwordProvider, lines);
+        }
+
+        @Override
+        public boolean canExtractKeyPairs(String resourceKey, List<String> lines)
+                throws IOException, GeneralSecurityException {
+            @SuppressWarnings("synthetic-access")
+            KeyPairResourceParser proxy = PROXY_HOLDER.get();
+            return (proxy != null) && proxy.canExtractKeyPairs(resourceKey, lines);
+        }
+    };
+
+    private static final Map<String, KeyPairPEMResourceParser> BY_OID_MAP = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+    private static final Map<String, KeyPairPEMResourceParser> BY_ALGORITHM_MAP = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+    private static final AtomicReference<KeyPairResourceParser> PROXY_HOLDER = new AtomicReference<>(KeyPairResourceParser.EMPTY);
+
+    static {
+        registerPEMResourceParser(RSAPEMResourceKeyPairParser.INSTANCE);
+        registerPEMResourceParser(DSSPEMResourceKeyPairParser.INSTANCE);
+        registerPEMResourceParser(ECDSAPEMResourceKeyPairParser.INSTANCE);
+        registerPEMResourceParser(PKCS8PEMResourceKeyPairParser.INSTANCE);
+    }
+
+    private PEMResourceParserUtils() {
+        throw new UnsupportedOperationException("No instance");
+    }
+
+    public static void registerPEMResourceParser(KeyPairPEMResourceParser parser) {
+        Objects.requireNonNull(parser, "No parser to register");
+        synchronized (BY_OID_MAP) {
+            BY_OID_MAP.put(ValidateUtils.checkNotNullAndNotEmpty(parser.getAlgorithmIdentifier(), "No OID value"), parser);
+        }
+
+        synchronized (BY_ALGORITHM_MAP) {
+            BY_ALGORITHM_MAP.put(ValidateUtils.checkNotNullAndNotEmpty(parser.getAlgorithm(), "No algorithm value"), parser);
+            // Use a copy in order to avoid concurrent modifications
+            PROXY_HOLDER.set(KeyPairResourceParser.aggregate(new ArrayList<>(BY_ALGORITHM_MAP.values())));
+        }
+    }
+
+    public static KeyPairPEMResourceParser getPEMResourceParserByOid(String oid) {
+        if (GenericUtils.isEmpty(oid)) {
+            return null;
+        }
+
+        synchronized (BY_OID_MAP) {
+            return BY_OID_MAP.get(oid);
+        }
+    }
+
+    public static KeyPairPEMResourceParser getPEMResourceParserByAlgorithm(String algorithm) {
+        if (GenericUtils.isEmpty(algorithm)) {
+            return null;
+        }
+
+        synchronized (BY_ALGORITHM_MAP) {
+            return BY_ALGORITHM_MAP.get(algorithm);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParser.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParser.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParser.java
new file mode 100644
index 0000000..b333f23
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParser.java
@@ -0,0 +1,156 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config.keys.loader.pem;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StreamCorruptedException;
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.common.util.io.der.ASN1Object;
+import org.apache.sshd.common.util.io.der.DERParser;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class PKCS8PEMResourceKeyPairParser extends AbstractPEMResourceKeyPairParser {
+    // Not exactly according to standard but good enough
+    public static final String BEGIN_MARKER = "BEGIN PRIVATE KEY";
+    public static final List<String> BEGINNERS =
+            Collections.unmodifiableList(Collections.singletonList(BEGIN_MARKER));
+
+    public static final String END_MARKER = "END PRIVATE KEY";
+    public static final List<String> ENDERS =
+            Collections.unmodifiableList(Collections.singletonList(END_MARKER));
+
+    public static final String PKCS8_FORMAT = "PKCS#8";
+
+    public static final PKCS8PEMResourceKeyPairParser INSTANCE = new PKCS8PEMResourceKeyPairParser();
+
+    public PKCS8PEMResourceKeyPairParser() {
+        super(PKCS8_FORMAT, PKCS8_FORMAT, BEGINNERS, ENDERS);
+    }
+
+    @Override
+    public Collection<KeyPair> extractKeyPairs(
+            String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, InputStream stream)
+                    throws IOException, GeneralSecurityException {
+        // Save the data before getting the algorithm OID since we will need it
+        byte[] encBytes = IoUtils.toByteArray(stream);
+        List<Integer> oidAlgorithm = getPKCS8AlgorithmIdentifier(encBytes);
+        PrivateKey prvKey = decodePEMPrivateKeyPKCS8(oidAlgorithm, encBytes, passwordProvider);
+        PublicKey pubKey = ValidateUtils.checkNotNull(KeyUtils.recoverPublicKey(prvKey),
+                "Failed to recover public key of OID=%s", oidAlgorithm);
+        KeyPair kp = new KeyPair(pubKey, prvKey);
+        return Collections.singletonList(kp);
+    }
+
+    public static PrivateKey decodePEMPrivateKeyPKCS8(
+            List<Integer> oidAlgorithm, byte[] keyBytes, FilePasswordProvider passwordProvider)
+                    throws GeneralSecurityException {
+        ValidateUtils.checkNotNullAndNotEmpty(oidAlgorithm, "No PKCS8 algorithm OID");
+        return decodePEMPrivateKeyPKCS8(GenericUtils.join(oidAlgorithm, '.'), keyBytes, passwordProvider);
+    }
+
+    public static PrivateKey decodePEMPrivateKeyPKCS8(
+            String oid, byte[] keyBytes, FilePasswordProvider passwordProvider)
+                    throws GeneralSecurityException {
+        KeyPairPEMResourceParser parser =
+            PEMResourceParserUtils.getPEMResourceParserByOid(
+                ValidateUtils.checkNotNullAndNotEmpty(oid, "No PKCS8 algorithm OID"));
+        if (parser == null) {
+            throw new NoSuchAlgorithmException("decodePEMPrivateKeyPKCS8(" + oid + ") unknown algorithm identifier");
+        }
+
+        String algorithm = ValidateUtils.checkNotNullAndNotEmpty(parser.getAlgorithm(), "No parser algorithm");
+        KeyFactory factory = SecurityUtils.getKeyFactory(algorithm);
+        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
+        return factory.generatePrivate(keySpec);
+    }
+
+    public static List<Integer> getPKCS8AlgorithmIdentifier(byte[] input) throws IOException {
+        try (DERParser parser = new DERParser(input)) {
+            return getPKCS8AlgorithmIdentifier(parser);
+        }
+    }
+
+    /**
+     * According to the standard:
+     * <PRE><CODE>
+     * PrivateKeyInfo ::= SEQUENCE {
+     *          version Version,
+     *          privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
+     *          privateKey PrivateKey,
+     *          attributes [0] IMPLICIT Attributes OPTIONAL
+     *  }
+     *
+     * Version ::= INTEGER
+     * PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
+     * PrivateKey ::= OCTET STRING
+     * Attributes ::= SET OF Attribute
+     * AlgorithmIdentifier ::= SEQUENCE {
+     *      algorithm       OBJECT IDENTIFIER,
+     *      parameters      ANY DEFINED BY algorithm OPTIONAL
+     * }
+     * </CODE></PRE>
+     * @param parser The {@link DERParser} to use
+     * @return The PKCS8 algorithm OID
+     * @throws IOException If malformed data
+     * @see #getPKCS8AlgorithmIdentifier(ASN1Object)
+     */
+    public static List<Integer> getPKCS8AlgorithmIdentifier(DERParser parser) throws IOException {
+        return getPKCS8AlgorithmIdentifier(parser.readObject());
+    }
+
+    public static List<Integer> getPKCS8AlgorithmIdentifier(ASN1Object privateKeyInfo) throws IOException {
+        try (DERParser parser = privateKeyInfo.createParser()) {
+            // Skip version
+            ASN1Object versionObject = parser.readObject();
+            if (versionObject == null) {
+                throw new StreamCorruptedException("No version");
+            }
+
+            ASN1Object privateKeyAlgorithm = parser.readObject();
+            if (privateKeyAlgorithm == null) {
+                throw new StreamCorruptedException("No private key algorithm");
+            }
+
+            try (DERParser oidParser = privateKeyAlgorithm.createParser()) {
+                ASN1Object oid = oidParser.readObject();
+                return oid.asOID();
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/RSAPEMResourceKeyPairParser.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/RSAPEMResourceKeyPairParser.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/RSAPEMResourceKeyPairParser.java
new file mode 100644
index 0000000..d760aaf
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/RSAPEMResourceKeyPairParser.java
@@ -0,0 +1,142 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config.keys.loader.pem;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StreamCorruptedException;
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.spec.RSAPrivateCrtKeySpec;
+import java.security.spec.RSAPrivateKeySpec;
+import java.security.spec.RSAPublicKeySpec;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.util.io.NoCloseInputStream;
+import org.apache.sshd.common.util.io.der.ASN1Object;
+import org.apache.sshd.common.util.io.der.ASN1Type;
+import org.apache.sshd.common.util.io.der.DERParser;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class RSAPEMResourceKeyPairParser extends AbstractPEMResourceKeyPairParser {
+    // Not exactly according to standard but good enough
+    public static final String BEGIN_MARKER = "BEGIN RSA PRIVATE KEY";
+    public static final List<String> BEGINNERS =
+            Collections.unmodifiableList(Collections.singletonList(BEGIN_MARKER));
+
+    public static final String END_MARKER = "END RSA PRIVATE KEY";
+    public static final List<String> ENDERS =
+            Collections.unmodifiableList(Collections.singletonList(END_MARKER));
+
+    /**
+     * @see <A HREF="https://tools.ietf.org/html/rfc3279#section-2.3.1">RFC-3279 section 2.3.1</A>
+     */
+    public static final String RSA_OID = "1.2.840.113549.1.1.1";
+
+    public static final RSAPEMResourceKeyPairParser INSTANCE = new RSAPEMResourceKeyPairParser();
+
+    public RSAPEMResourceKeyPairParser() {
+        super(KeyUtils.RSA_ALGORITHM, RSA_OID, BEGINNERS, ENDERS);
+    }
+
+    @Override
+    public Collection<KeyPair> extractKeyPairs(
+            String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, InputStream stream)
+                    throws IOException, GeneralSecurityException {
+        KeyPair kp = decodeRSAKeyPair(SecurityUtils.getKeyFactory(KeyUtils.RSA_ALGORITHM), stream, false);
+        return Collections.singletonList(kp);
+    }
+
+    /**
+     * <p>The ASN.1 syntax for the private key as per RFC-3447 section A.1.2:</P>
+     * <pre><code>
+     * RSAPrivateKey ::= SEQUENCE {
+     *   version           Version,
+     *   modulus           INTEGER,  -- n
+     *   publicExponent    INTEGER,  -- e
+     *   privateExponent   INTEGER,  -- d
+     *   prime1            INTEGER,  -- p
+     *   prime2            INTEGER,  -- q
+     *   exponent1         INTEGER,  -- d mod (p-1)
+     *   exponent2         INTEGER,  -- d mod (q-1)
+     *   coefficient       INTEGER,  -- (inverse of q) mod p
+     *   otherPrimeInfos   OtherPrimeInfos OPTIONAL
+     * }
+     * </code></pre>
+     * @param kf The {@link KeyFactory} To use to generate the keys
+     * @param s The {@link InputStream} containing the encoded bytes
+     * @param okToClose <code>true</code> if the method may close the input
+     * stream regardless of success or failure
+     * @return The recovered {@link KeyPair}
+     * @throws IOException If failed to read or decode the bytes
+     * @throws GeneralSecurityException If failed to generate the keys
+     */
+    public static KeyPair decodeRSAKeyPair(KeyFactory kf, InputStream s, boolean okToClose)
+            throws IOException, GeneralSecurityException {
+        ASN1Object sequence;
+        try (DERParser parser = new DERParser(NoCloseInputStream.resolveInputStream(s, okToClose))) {
+            sequence = parser.readObject();
+        }
+
+        if (!ASN1Type.SEQUENCE.equals(sequence.getObjType())) {
+            throw new IOException("Invalid DER: not a sequence: " + sequence.getObjType());
+        }
+
+        try (DERParser parser = sequence.createParser()) {
+            // Skip version
+            ASN1Object versionObject = parser.readObject();
+            if (versionObject == null) {
+                throw new StreamCorruptedException("No version");
+            }
+
+            // as per RFC-3447 section A.1.2
+            BigInteger version = versionObject.asInteger();
+            if (!BigInteger.ZERO.equals(version)) {
+                throw new StreamCorruptedException("Multi-primes N/A");
+            }
+
+            BigInteger modulus = parser.readObject().asInteger();
+            BigInteger publicExp = parser.readObject().asInteger();
+            PublicKey pubKey = kf.generatePublic(new RSAPublicKeySpec(modulus, publicExp));
+
+            BigInteger privateExp = parser.readObject().asInteger();
+            BigInteger primeP = parser.readObject().asInteger();
+            BigInteger primeQ = parser.readObject().asInteger();
+            BigInteger primeExponentP = parser.readObject().asInteger();
+            BigInteger primeExponentQ = parser.readObject().asInteger();
+            BigInteger crtCoef = parser.readObject().asInteger();
+            RSAPrivateKeySpec prvSpec = new RSAPrivateCrtKeySpec(
+                    modulus, publicExp, privateExp, primeP, primeQ, primeExponentP, primeExponentQ, crtCoef);
+            PrivateKey prvKey = kf.generatePrivate(prvSpec);
+            return new KeyPair(pubKey, prvKey);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/digest/BaseDigest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/digest/BaseDigest.java b/sshd-common/src/main/java/org/apache/sshd/common/digest/BaseDigest.java
new file mode 100644
index 0000000..a5ef6f9
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/digest/BaseDigest.java
@@ -0,0 +1,156 @@
+/*
+ * 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.digest;
+
+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.ValidateUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * Base class for Digest algorithms based on the JCE provider.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class BaseDigest implements Digest {
+
+    private final String algorithm;
+    private final int bsize;
+    private int h;
+    private String s;
+    private MessageDigest md;
+
+    /**
+     * Create a new digest using the given algorithm and block size.
+     * The initialization and creation of the underlying {@link MessageDigest}
+     * object will be done in the {@link #init()} method.
+     *
+     * @param algorithm the JCE algorithm to use for this digest
+     * @param bsize     the block size of this digest
+     */
+    public BaseDigest(String algorithm, int bsize) {
+        this.algorithm = ValidateUtils.checkNotNullAndNotEmpty(algorithm, "No algorithm");
+        ValidateUtils.checkTrue(bsize > 0, "Invalid block size: %d", bsize);
+        this.bsize = bsize;
+    }
+
+    @Override
+    public final String getAlgorithm() {
+        return algorithm;
+    }
+
+    @Override
+    public int getBlockSize() {
+        return bsize;
+    }
+
+    @Override
+    public void init() throws Exception {
+        this.md = SecurityUtils.getMessageDigest(getAlgorithm());
+    }
+
+    @Override
+    public void update(byte[] data) throws Exception {
+        update(data, 0, NumberUtils.length(data));
+    }
+
+    @Override
+    public void update(byte[] data, int start, int len) throws Exception {
+        Objects.requireNonNull(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 Objects.requireNonNull(md, "Digest not initialized").digest();
+    }
+
+    @Override
+    public int hashCode() {
+        synchronized (this) {
+            if (h == 0) {
+                h = Objects.hashCode(getAlgorithm()) + getBlockSize();
+                if (h == 0) {
+                    h = 1;
+                }
+            }
+        }
+
+        return h;
+    }
+
+    @Override
+    public int compareTo(Digest that) {
+        if (that == null) {
+            return -1;    // push null(s) to end
+        } else if (this == that) {
+            return 0;
+        }
+
+        String thisAlg = getAlgorithm();
+        String thatAlg = that.getAlgorithm();
+        int nRes = GenericUtils.safeCompare(thisAlg, thatAlg, false);
+        if (nRes != 0) {
+            return nRes;    // debug breakpoint
+        }
+
+        nRes = Integer.compare(this.getBlockSize(), that.getBlockSize());
+        if (nRes != 0) {
+            return nRes;    // debug breakpoint
+        }
+
+        return 0;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (obj == this) {
+            return true;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+
+        int nRes = compareTo((Digest) obj);
+        return nRes == 0;
+    }
+
+    @Override
+    public String toString() {
+        synchronized (this) {
+            if (s == null) {
+                s = getClass().getSimpleName() + "[" + getAlgorithm() + ":" + getBlockSize() + "]";
+            }
+        }
+
+        return s;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/digest/BuiltinDigests.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/digest/BuiltinDigests.java b/sshd-common/src/main/java/org/apache/sshd/common/digest/BuiltinDigests.java
new file mode 100644
index 0000000..f469583
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/digest/BuiltinDigests.java
@@ -0,0 +1,166 @@
+/*
+ * 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.digest;
+
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.Set;
+
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * 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 DigestFactory {
+    md5(Constants.MD5, "MD5", 16),
+    sha1(Constants.SHA1, "SHA-1", 20),
+    sha224(Constants.SHA224, "SHA-224", 28),
+    sha256(Constants.SHA256, "SHA-256", 32),
+    sha384(Constants.SHA384, "SHA-384", 48),
+    sha512(Constants.SHA512, "SHA-512", 64);
+
+    public static final Set<BuiltinDigests> VALUES =
+            Collections.unmodifiableSet(EnumSet.allOf(BuiltinDigests.class));
+
+    private final String algorithm;
+    private final int blockSize;
+    private final String factoryName;
+    private final boolean supported;
+
+    BuiltinDigests(String factoryName, String algorithm, int blockSize) {
+        this.factoryName = factoryName;
+        this.algorithm = algorithm;
+        this.blockSize = blockSize;
+        /*
+         * This can be done once since in order to change the support the JVM
+         * needs to be stopped, some unlimited-strength files need be installed
+         * and then the JVM re-started. Therefore, the answer is not going to
+         * change while the JVM is running
+         */
+        this.supported = DigestUtils.checkSupported(algorithm);
+    }
+
+    @Override
+    public final String getName() {
+        return factoryName;
+    }
+
+    @Override
+    public final String getAlgorithm() {
+        return algorithm;
+    }
+
+    @Override
+    public final int getBlockSize() {
+        return blockSize;
+    }
+
+    @Override
+    public final String toString() {
+        return getName();
+    }
+
+    @Override
+    public final Digest create() {
+        return new BaseDigest(getAlgorithm(), getBlockSize());
+    }
+
+    @Override
+    public final boolean isSupported() {
+        return supported;
+    }
+
+    /**
+     * @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
+     * (case <U>insensitive</U>) the provided argument - {@code null} if no match
+     */
+    public static BuiltinDigests fromString(String s) {
+        if (GenericUtils.isEmpty(s)) {
+            return null;
+        }
+
+        for (BuiltinDigests c : VALUES) {
+            if (s.equalsIgnoreCase(c.name())) {
+                return c;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * @param factory The {@link org.apache.sshd.common.NamedFactory} for the cipher - ignored if {@code null}
+     * @return The matching {@link org.apache.sshd.common.digest.BuiltinDigests} whose factory name matches
+     * (case <U>insensitive</U>) the digest factory name
+     * @see #fromFactoryName(String)
+     */
+    public static BuiltinDigests fromFactory(NamedFactory<? extends Digest> factory) {
+        if (factory == null) {
+            return null;
+        } else {
+            return fromFactoryName(factory.getName());
+        }
+    }
+
+    /**
+     * @param name The factory name - ignored if {@code null}/empty
+     * @return The matching {@link org.apache.sshd.common.digest.BuiltinDigests} whose factory name matches
+     * (case <U>insensitive</U>) the provided name - {@code null} if no match
+     */
+    public static BuiltinDigests fromFactoryName(String name) {
+        return NamedResource.findByName(name, String.CASE_INSENSITIVE_ORDER, VALUES);
+    }
+
+    /**
+     * @param d The {@link Digest} instance - ignored if {@code null}
+     * @return The matching {@link org.apache.sshd.common.digest.BuiltinDigests} whose algorithm matches
+     * (case <U>insensitive</U>) the digets's algorithm - {@code null} if no match
+     */
+    public static BuiltinDigests fromDigest(Digest d) {
+        return fromAlgorithm((d == null) ? null : d.getAlgorithm());
+    }
+
+    /**
+     * @param algo The algorithm to find - ignored if {@code null}/empty
+     * @return The matching {@link org.apache.sshd.common.digest.BuiltinDigests} whose algorithm matches
+     * (case <U>insensitive</U>) the provided name - {@code null} if no match
+     */
+    public static BuiltinDigests fromAlgorithm(String algo) {
+        return DigestUtils.findFactoryByAlgorithm(algo, String.CASE_INSENSITIVE_ORDER, VALUES);
+    }
+
+    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";
+
+        private Constants() {
+            throw new UnsupportedOperationException("No instance allowed");
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/digest/Digest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/digest/Digest.java b/sshd-common/src/main/java/org/apache/sshd/common/digest/Digest.java
new file mode 100644
index 0000000..27204ff
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/digest/Digest.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.digest;
+
+/**
+ * Interface used to compute digests, based on algorithms such as MD5 or SHA1.
+ * The digest implementation are compared first by the algorithm name (case
+ * <U>insensitive</U> and second according to the block size
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface Digest extends DigestInformation, Comparable<Digest> {
+    void init() throws Exception;
+
+    void update(byte[] data) throws Exception;
+
+    void update(byte[] data, int start, int len) throws Exception;
+
+    byte[] digest() throws Exception;
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/digest/DigestFactory.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/digest/DigestFactory.java b/sshd-common/src/main/java/org/apache/sshd/common/digest/DigestFactory.java
new file mode 100644
index 0000000..f00e7ba
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/digest/DigestFactory.java
@@ -0,0 +1,32 @@
+/*
+ * 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.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>
+ */
+// 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/10de190e/sshd-common/src/main/java/org/apache/sshd/common/digest/DigestInformation.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/digest/DigestInformation.java b/sshd-common/src/main/java/org/apache/sshd/common/digest/DigestInformation.java
new file mode 100644
index 0000000..ba181dc
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/digest/DigestInformation.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.digest;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface DigestInformation {
+    /**
+     * @return The digest algorithm name
+     */
+    String getAlgorithm();
+
+    /**
+     * @return The number of bytes in the digest's output
+     */
+    int getBlockSize();
+
+}


[32/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common utilities code from sshd-core into sshd-common (new artifact)

Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsCloneTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsCloneTest.java b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsCloneTest.java
new file mode 100644
index 0000000..48f7ec4
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsCloneTest.java
@@ -0,0 +1,120 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config.keys;
+
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.sshd.common.cipher.ECCurves;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.junit.runners.Parameterized.UseParametersRunnerFactory;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RunWith(Parameterized.class)   // see https://github.com/junit-team/junit/wiki/Parameterized-tests
+@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
+@Category({ NoIoTestCase.class })
+public class KeyUtilsCloneTest extends JUnitTestSupport {
+    private final String keyType;
+    private final int keySize;
+
+    public KeyUtilsCloneTest(String keyType, int keySize) {
+        this.keyType = ValidateUtils.checkNotNullAndNotEmpty(keyType, "No key type specified");
+        this.keySize = keySize;
+    }
+
+    @Parameters(name = "type={0}, size={1}")
+    public static List<Object[]> parameters() {
+        List<Object[]> list = new ArrayList<>();
+        addTests(list, KeyPairProvider.SSH_DSS, DSS_SIZES);
+        addTests(list, KeyPairProvider.SSH_RSA, RSA_SIZES);
+        if (SecurityUtils.isECCSupported()) {
+            for (ECCurves curve : ECCurves.VALUES) {
+                if (!curve.isSupported()) {
+                    continue;
+                }
+                addTests(list, curve.getKeyType(), Collections.singletonList(curve.getKeySize()));
+            }
+        }
+        if (SecurityUtils.isEDDSACurveSupported()) {
+            addTests(list, KeyPairProvider.SSH_ED25519, ED25519_SIZES);
+        }
+        return Collections.unmodifiableList(list);
+    }
+
+    private static void addTests(List<Object[]> list, String keyType, Collection<Integer> sizes) {
+        for (Integer keySize : sizes) {
+            list.add(new Object[]{keyType, keySize});
+        }
+    }
+
+    @Test
+    @SuppressWarnings("checkstyle:avoidnestedblocks")
+    public void testKeyPairCloning() throws GeneralSecurityException {
+        outputDebugMessage("generateKeyPair(%s)[%d]", keyType, keySize);
+        KeyPair original = KeyUtils.generateKeyPair(keyType, keySize);
+
+        outputDebugMessage("cloneKeyPair(%s)[%d]", keyType, keySize);
+        KeyPair cloned = KeyUtils.cloneKeyPair(keyType, original);
+
+        String prefix = keyType + "[" + keySize + "]";
+        assertNotSame(prefix + ": Key pair not cloned", original, cloned);
+        assertTrue(prefix + ": Cloned pair not equals", KeyUtils.compareKeyPairs(original, cloned));
+
+        {
+            PublicKey k1 = original.getPublic();
+            PublicKey k2 = cloned.getPublic();
+            assertNotSame(prefix + ": Public key not cloned", k1, k2);
+            assertTrue(prefix + ": Cloned public key not equals", KeyUtils.compareKeys(k1, k2));
+
+            String f1 = KeyUtils.getFingerPrint(k1);
+            String f2 = KeyUtils.getFingerPrint(k2);
+            assertEquals(prefix + ": Mismatched fingerprints", f1, f2);
+        }
+
+        {
+            PrivateKey k1 = original.getPrivate();
+            PrivateKey k2 = cloned.getPrivate();
+            assertNotSame(prefix + ": Private key not cloned", k1, k2);
+            assertTrue(prefix + ": Cloned private key not equals", KeyUtils.compareKeys(k1, k2));
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsFingerprintCaseSensitivityTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsFingerprintCaseSensitivityTest.java b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsFingerprintCaseSensitivityTest.java
new file mode 100644
index 0000000..ae0b86d
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsFingerprintCaseSensitivityTest.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config.keys;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.PublicKey;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.Arrays;
+import java.util.Collection;
+
+import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.BeforeClass;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.junit.runners.Parameterized.UseParametersRunnerFactory;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RunWith(Parameterized.class)   // see https://github.com/junit-team/junit/wiki/Parameterized-tests
+@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
+@Category({ NoIoTestCase.class })
+public class KeyUtilsFingerprintCaseSensitivityTest extends JUnitTestSupport {
+
+    // CHECKSTYLE:OFF
+    private static final String KEY_STRING = "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAxr3N5fkt966xJINl0hH7Q6lLDRR1D0yMjcXCE5roE9VFut2ctGFuo90TCOxkPOMnwzwConeyScVF4ConZeWsxbG9VtRh61IeZ6R5P5ZTvE9xPdZBgIEWvU1bRfrrOfSMihqF98pODspE6NoTtND2eglwSGwxcYFmpdTAmu+8qgxgGxlEaaCjqwdiNPZhygrH81Mv2ruolNeZkn4Bj+wFFmZTD/waN1pQaMf+SO1+kEYIYFNl5+8JRGuUcr8MhHHJB+gwqMTF2BSBVITJzZUiQR0TMtkK6Vbs7yt1F9hhzDzAFDwhV+rsfNQaOHpl3zP07qH+/99A0XG1CVcEdHqVMw== lgoldstein@LGOLDSTEIN-WIN7";
+    // CHECKSTYLE:ON
+    private static final String MD5_PREFIX = "MD5:";
+    private static final String MD5 = "24:32:3c:80:01:b3:e1:fa:7c:53:ca:e3:e8:4e:c6:8e";
+    private static final String MD5_FULL = MD5_PREFIX + MD5;
+    private static final String SHA1_PREFIX = "SHA1:";
+    private static final String SHA1 = "ZNLzC6u+F37oq8BpEAwP69EQtoA";
+    private static final String SHA1_FULL = SHA1_PREFIX + SHA1;
+
+    private static PublicKey key;
+
+    private String expected;
+    private String test;
+
+    public KeyUtilsFingerprintCaseSensitivityTest(String expected, String test) {
+        this.expected = expected;
+        this.test = test;
+    }
+
+    @BeforeClass
+    public static void beforeClass() throws GeneralSecurityException, IOException {
+        key = PublicKeyEntry.parsePublicKeyEntry(KEY_STRING).resolvePublicKey(PublicKeyEntryResolver.FAILING);
+    }
+
+    @Parameters(name = "expected={0}, test={1}")
+    public static Collection<Object[]> parameters() {
+        return Arrays.asList(
+            new Object[] {MD5_FULL, MD5_FULL},
+            new Object[] {MD5_FULL, MD5_FULL.toUpperCase()},
+            new Object[] {MD5_FULL, MD5_FULL.toLowerCase()},
+            new Object[] {MD5_FULL, MD5_PREFIX.toUpperCase() + MD5},
+            new Object[] {MD5_FULL, MD5_PREFIX.toLowerCase() + MD5},
+            new Object[] {MD5_FULL, MD5.toLowerCase()},
+            new Object[] {MD5_FULL, MD5.toUpperCase()},
+            new Object[] {SHA1_FULL, SHA1_FULL},
+            new Object[] {SHA1_FULL, SHA1_PREFIX.toUpperCase() + SHA1},
+            new Object[] {SHA1_FULL, SHA1_PREFIX.toLowerCase() + SHA1}
+        );
+    }
+
+    @Test
+    public void testCase() throws Exception {
+        assertEquals("Check failed", new SimpleImmutableEntry<>(true, expected), KeyUtils.checkFingerPrint(test, key));
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsFingerprintGenerationTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsFingerprintGenerationTest.java b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsFingerprintGenerationTest.java
new file mode 100644
index 0000000..9a8fb7a
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsFingerprintGenerationTest.java
@@ -0,0 +1,159 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config.keys;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.PublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.sshd.common.digest.BuiltinDigests;
+import org.apache.sshd.common.digest.DigestFactory;
+import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.junit.runners.Parameterized.UseParametersRunnerFactory;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RunWith(Parameterized.class)   // see https://github.com/junit-team/junit/wiki/Parameterized-tests
+@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
+@Category({ NoIoTestCase.class })
+public class KeyUtilsFingerprintGenerationTest extends JUnitTestSupport {
+    private final PublicKey key;
+    private final DigestFactory digestFactory;
+    private final String expected;
+
+    public KeyUtilsFingerprintGenerationTest(PublicKey key, DigestFactory digestFactory, String expected) {
+        this.key = key;
+        this.digestFactory = digestFactory;
+        this.expected = expected;
+    }
+
+    @Parameters(name = "key={0}, digestFactory={1}, expected={2}")
+    public static Collection<Object[]> parameters() throws IOException, GeneralSecurityException {
+        List<? extends Map.Entry<String, List<? extends Map.Entry<DigestFactory, String>>>> keyEntries =
+            Collections.unmodifiableList(Arrays.asList(
+                new SimpleImmutableEntry<>(
+                    // CHECKSTYLE:OFF
+                    "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAxr3N5fkt966xJINl0hH7Q6lLDRR1D0yMjcXCE5roE9VFut2ctGFuo90TCOxkPOMnwzwConeyScVF4ConZeWsxbG9VtRh61IeZ6R5P5ZTvE9xPdZBgIEWvU1bRfrrOfSMihqF98pODspE6NoTtND2eglwSGwxcYFmpdTAmu+8qgxgGxlEaaCjqwdiNPZhygrH81Mv2ruolNeZkn4Bj+wFFmZTD/waN1pQaMf+SO1+kEYIYFNl5+8JRGuUcr8MhHHJB+gwqMTF2BSBVITJzZUiQR0TMtkK6Vbs7yt1F9hhzDzAFDwhV+rsfNQaOHpl3zP07qH+/99A0XG1CVcEdHqVMw== lgoldstein@LGOLDSTEIN-WIN7",
+                    // CHECKSTYLE:ON
+                    Arrays.asList(
+                        new SimpleImmutableEntry<>(
+                            BuiltinDigests.md5,
+                            "MD5:24:32:3c:80:01:b3:e1:fa:7c:53:ca:e3:e8:4e:c6:8e"
+                        ),
+                        new SimpleImmutableEntry<>(
+                            BuiltinDigests.sha256,
+                            "SHA256:1wNOZO+/XgNGJMx8UUJst33V+bBMTz5EcL0B6y2iRv0"
+                        )
+                    )
+                ),
+                new SimpleImmutableEntry<>(
+                    // CHECKSTYLE:OFF
+                    "ssh-dss AAAAB3NzaC1kc3MAAACBAMg/IxsG5BxnF5gM7IKqqR0rftxZC+n5GlbO+J4H+iIb/KR8NBehkxG3CrBZMF96M2K1sEGYLob+3k4r71oWaPul8n5rt9kpd+JSq4iD2ygOyg6Kd1/YDBHoxneizy6I/bGsLwhAAKWcRNrXmYVKGzhrhvZWN12AJDq2mGdj3szLAAAAFQD7a2MltdUSF7FU3//SpW4WGjZbeQAAAIBf0nNsfKQL/TEMo7IpTrEMg5V0RnSigCX0+yUERS42GW/ZeCZBJw7oL2XZbuBtu63vMjDgVpnb92BdrcPgjJ7EFW6DlcyeuywStmg1ygXmDR2AQCxv0eX2CQgrdUczmRa155SDVUTvTQlO1IyKx0vwKAh1H7E3yJUfkTAJstbGYQAAAIEAtv+cdRfNevYFkp55jVqazc8zRLvfb64jzgc5oSJVc64kFs4yx+abYpGX9WxNxDlG6g2WiY8voDBB0YnUJsn0kVRjBKX9OceROxrfT4K4dVbQZsdt+SLaXWL4lGJFrFZL3LZqvySvq6xfhJfakQDDivW4hUOhFPXPHrE5/Ia3T7A= dsa-key-20130709",
+                    // CHECKSTYLE:ON
+                    Arrays.asList(
+                        new SimpleImmutableEntry<>(
+                            BuiltinDigests.md5,
+                            "MD5:fb:29:14:8d:94:f9:1d:cf:6b:0e:a4:35:1d:83:44:2f"
+                        ),
+                        new SimpleImmutableEntry<>(
+                            BuiltinDigests.sha256,
+                            "SHA256:grxw4KhY1cK6eOczBWs7tDVvo9V0PQw4E1wN1gJvHlw"
+                        )
+                    )
+                ),
+                new SimpleImmutableEntry<>(
+                    // CHECKSTYLE:OFF
+                    "ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBFImZtcTj842stlcVHLFBFxTEx7lu3jW9aZCvd0r9fUNKZ6LbRPh6l1oJ4ozArnw7XreQBUc5oNd9HB5RNJ8jl1nWXY5cXBA7McZrKZrYmk+zxNhH6UL+kMLaJkyngJHQw== root@osv-linux",
+                    // CHECKSTYLE:ON
+                    Arrays.asList(
+                        new SimpleImmutableEntry<>(
+                            BuiltinDigests.md5,
+                            "MD5:e6:dc:a2:4f:5b:11:b2:3c:0f:e8:f6:d8:d1:01:e9:d3"
+                        ),
+                        new SimpleImmutableEntry<>(
+                            BuiltinDigests.sha512,
+                            "SHA512:4w6ZB78tmFWhpN2J50Ok6WeMJhZp1X0xN0EKWxZmRLcYDbCWhyJDe8lgrQKWqdTCMZ5aNEBl9xQUklcC5Gt2jg"
+                        )
+                    )
+                )
+            ));
+
+        List<Object[]> ret = new ArrayList<>();
+        for (Map.Entry<String, ? extends Collection<? extends Map.Entry<DigestFactory, String>>> kentry : keyEntries) {
+            String keyValue = kentry.getKey();
+            try {
+                PublicKey key = PublicKeyEntry.parsePublicKeyEntry(keyValue).resolvePublicKey(PublicKeyEntryResolver.FAILING);
+                for (Map.Entry<DigestFactory, String> dentry : kentry.getValue()) {
+                    DigestFactory factory = dentry.getKey();
+                    String fingerprint = dentry.getValue();
+                    if (!factory.isSupported()) {
+                        System.out.println("Skip unsupported digest: " + fingerprint);
+                        continue;
+                    }
+
+                    ret.add(new Object[]{key, factory, fingerprint});
+                }
+            } catch (InvalidKeySpecException e) {
+                System.out.println("Skip unsupported key: " + keyValue);
+            }
+        }
+
+        return ret;
+    }
+
+    @Test
+    public void testFingerprint() throws Exception {
+        String name = digestFactory.getName();
+        assertEquals(
+            String.format("Fingerprint does not match for digest %s", name),
+            expected,
+            KeyUtils.getFingerPrint(digestFactory, key)
+        );
+        assertEquals(
+            String.format("Fingerprint check failed for digest %s", name),
+            new SimpleImmutableEntry<>(true, expected),
+            KeyUtils.checkFingerPrint(expected, digestFactory, key)
+        );
+        assertEquals(
+            String.format("Fingerprint check succeeded for invalid digest %s", name),
+            new SimpleImmutableEntry<>(false, expected),
+            KeyUtils.checkFingerPrint(expected + "A", digestFactory, key)
+        );
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsTest.java b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsTest.java
new file mode 100644
index 0000000..00b273f
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsTest.java
@@ -0,0 +1,157 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config.keys;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.attribute.PosixFilePermission;
+import java.security.DigestException;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Map;
+
+import org.apache.sshd.common.digest.BaseDigest;
+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.OsUtils;
+import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class KeyUtilsTest extends JUnitTestSupport {
+    public KeyUtilsTest() {
+        super();
+    }
+
+    @Test
+    public void testGenerateFingerPrintOnException() {
+        for (DigestFactory info : BuiltinDigests.VALUES) {
+            if (!info.isSupported()) {
+                System.out.println("Skip unsupported digest: " + info.getAlgorithm());
+                continue;
+            }
+
+            Exception thrown = new DigestException(info.getAlgorithm() + ":" + info.getBlockSize());
+            Digest digest = new BaseDigest(info.getAlgorithm(), info.getBlockSize()) {
+                @Override
+                public byte[] digest() throws Exception {
+                    throw thrown;
+                }
+            };
+            String actual = KeyUtils.getFingerPrint(new DigestFactory() {
+                @Override
+                public String getName() {
+                    return getCurrentTestName();
+                }
+
+                @Override
+                public String getAlgorithm() {
+                    return digest.getAlgorithm();
+                }
+
+                @Override
+                public boolean isSupported() {
+                    return info.isSupported();
+                }
+
+                @Override
+                public int getBlockSize() {
+                    return info.getBlockSize();
+                }
+
+                @Override
+                public Digest create() {
+                    return digest;
+                }
+            }, getCurrentTestName());
+            String expected = thrown.getClass().getSimpleName();
+            assertEquals("Mismatched fingerprint for " + thrown.getMessage(), expected, actual);
+        }
+    }
+
+    @Test
+    public void testGenerateDefaultFingerprintDigest() {
+        DigestFactory defaultValue = KeyUtils.getDefaultFingerPrintFactory();
+        assertNotNull("No current default fingerprint digest factory", defaultValue);
+        try {
+            for (DigestFactory f : BuiltinDigests.VALUES) {
+                if (!f.isSupported()) {
+                    System.out.println("Skip unsupported digest=" + f.getAlgorithm());
+                    continue;
+                }
+
+                KeyUtils.setDefaultFingerPrintFactory(f);
+
+                String data = getClass().getName() + "#" + getCurrentTestName() + "(" + f.getName() + ")";
+                String expected = KeyUtils.getFingerPrint(f, data);
+                String actual = KeyUtils.getFingerPrint(data);
+                assertEquals("Mismatched fingerprint for digest=" + f.getName(), expected, actual);
+            }
+        } finally {
+            KeyUtils.setDefaultFingerPrintFactory(defaultValue); // restore the original
+        }
+    }
+
+    @Test   // see SSHD-606
+    public void testValidateStrictKeyFilePermissions() throws IOException {
+        Path file = getTempTargetRelativeFile(getClass().getSimpleName(), getCurrentTestName());
+        outputDebugMessage("%s deletion result=%s", file, Files.deleteIfExists(file));
+        assertNull("Unexpected violation for non-existent file: " + file, KeyUtils.validateStrictKeyFilePermissions(file));
+
+        assertHierarchyTargetFolderExists(file.getParent());
+        try (OutputStream output = Files.newOutputStream(file)) {
+            output.write((getClass().getName() + "#" + getCurrentTestName() + "@" + new Date(System.currentTimeMillis())).getBytes(StandardCharsets.UTF_8));
+        }
+
+        Collection<PosixFilePermission> perms = IoUtils.getPermissions(file);
+        if (GenericUtils.isEmpty(perms)) {
+            assertNull("Unexpected violation for no permissions file: " + file, KeyUtils.validateStrictKeyFilePermissions(file));
+        } else if (OsUtils.isUNIX()) {
+            Map.Entry<String, Object> violation = null;
+            for (PosixFilePermission p : KeyUtils.STRICTLY_PROHIBITED_FILE_PERMISSION) {
+                if (perms.contains(p)) {
+                    violation = KeyUtils.validateStrictKeyFilePermissions(file);
+                    assertNotNull("Unexpected success for permission=" + p + " of file " + file + " permissions=" + perms, violation);
+                    break;
+                }
+            }
+
+            if (violation == null) {    // we expect a failure since the parent does not have the necessary permissions
+                assertNotNull("Unexpected UNIX success for file " + file + " permissions=" + perms, KeyUtils.validateStrictKeyFilePermissions(file));
+            }
+        } else {
+            assertNull("Unexpected Windows violation for file " + file + " permissions=" + perms, KeyUtils.validateStrictKeyFilePermissions(file));
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/config/keys/PublicKeyEntryTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/config/keys/PublicKeyEntryTest.java b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/PublicKeyEntryTest.java
new file mode 100644
index 0000000..1fd372f
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/PublicKeyEntryTest.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config.keys;
+
+import java.security.GeneralSecurityException;
+import java.security.PublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.util.Arrays;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class PublicKeyEntryTest extends JUnitTestSupport {
+    public PublicKeyEntryTest() {
+        super();
+    }
+
+    @Test
+    public void testFallbackResolver() throws Exception {
+        PublicKeyEntry entry =  // TODO use some
+                PublicKeyEntry.parsePublicKeyEntry(
+                        GenericUtils.join(
+                                Arrays.asList(getCurrentTestName(), "AAAA", getClass().getSimpleName()), ' '));
+        for (PublicKeyEntryResolver resolver : new PublicKeyEntryResolver[]{
+            null, PublicKeyEntryResolver.FAILING, PublicKeyEntryResolver.IGNORING}) {
+            try {
+                PublicKey key = entry.resolvePublicKey(resolver);
+                assertSame("Mismatched successful resolver", PublicKeyEntryResolver.IGNORING, resolver);
+                assertNull("Unexpected success for resolver=" + resolver + ": " + KeyUtils.getFingerPrint(key), key);
+            } catch (GeneralSecurityException e) {
+                assertObjectInstanceOf("Mismatched thrown exception for resolver=" + resolver, InvalidKeySpecException.class, e);
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/config/keys/loader/AESPrivateKeyObfuscatorTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/config/keys/loader/AESPrivateKeyObfuscatorTest.java b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/loader/AESPrivateKeyObfuscatorTest.java
new file mode 100644
index 0000000..48797b8
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/loader/AESPrivateKeyObfuscatorTest.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.config.keys.loader;
+
+import java.security.GeneralSecurityException;
+import java.security.Key;
+import java.util.List;
+import java.util.Random;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.junit.runners.Parameterized.UseParametersRunnerFactory;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RunWith(Parameterized.class)   // see https://github.com/junit-team/junit/wiki/Parameterized-tests
+@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
+@Category({ NoIoTestCase.class })
+public class AESPrivateKeyObfuscatorTest extends JUnitTestSupport {
+    private static final Random RANDOMIZER = new Random(System.currentTimeMillis());
+
+    private final int keyLength;
+
+    public AESPrivateKeyObfuscatorTest(int keyLength) {
+        this.keyLength = keyLength;
+    }
+
+    @Parameters(name = "keyLength={0}")
+    public static List<Object[]> parameters() {
+        List<Integer> lengths = AESPrivateKeyObfuscator.getAvailableKeyLengths();
+        assertFalse("No lengths available", GenericUtils.isEmpty(lengths));
+        return parameterize(lengths);
+    }
+
+    @Test
+    public void testAvailableKeyLengthExists() throws GeneralSecurityException {
+        assertEquals("Not a BYTE size multiple", 0, keyLength % Byte.SIZE);
+
+        byte[] iv = new byte[keyLength / Byte.SIZE];
+        synchronized (RANDOMIZER) {
+            RANDOMIZER.nextBytes(iv);
+        }
+
+        Key key = new SecretKeySpec(iv, AESPrivateKeyObfuscator.CIPHER_NAME);
+        Cipher c = SecurityUtils.getCipher(AESPrivateKeyObfuscator.CIPHER_NAME);
+        c.init(Cipher.DECRYPT_MODE, key);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest.java b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest.java
new file mode 100644
index 0000000..d5c6aba
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest.java
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.config.keys.loader.openssh;
+
+import java.net.URL;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.sshd.common.config.keys.AuthorizedKeyEntry;
+import org.apache.sshd.common.config.keys.BuiltinIdentities;
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.PrivateKeyEntryDecoder;
+import org.apache.sshd.common.config.keys.PublicKeyEntryResolver;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.Assume;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.junit.runners.Parameterized.UseParametersRunnerFactory;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RunWith(Parameterized.class)   // see https://github.com/junit-team/junit/wiki/Parameterized-tests
+@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
+@Category({ NoIoTestCase.class })
+public class OpenSSHKeyPairResourceParserTest extends JUnitTestSupport {
+    private static final OpenSSHKeyPairResourceParser PARSER = OpenSSHKeyPairResourceParser.INSTANCE;
+    private final BuiltinIdentities identity;
+
+    public OpenSSHKeyPairResourceParserTest(BuiltinIdentities identity) {
+        this.identity = identity;
+    }
+
+    @Parameters(name = "type={0}")
+    public static List<Object[]> parameters() {
+        return parameterize(BuiltinIdentities.VALUES);
+    }
+
+    @Test
+    public void testLoadKeyPairs() throws Exception {
+        Assume.assumeTrue(identity + " not supported", identity.isSupported());
+
+        String resourceKey = getClass().getSimpleName() + "-" + identity.getName().toUpperCase() + "-" + KeyPair.class.getSimpleName();
+        URL urlKeyPair = getClass().getResource(resourceKey);
+        assertNotNull("Missing key-pair resource: " + resourceKey, urlKeyPair);
+
+        Collection<KeyPair> pairs = PARSER.loadKeyPairs(urlKeyPair, FilePasswordProvider.EMPTY);
+        assertEquals("Mismatched pairs count", 1, GenericUtils.size(pairs));
+
+        URL urlPubKey = getClass().getResource(resourceKey + ".pub");
+        assertNotNull("Missing public key resource: " + resourceKey, urlPubKey);
+
+        List<AuthorizedKeyEntry> entries = AuthorizedKeyEntry.readAuthorizedKeys(urlPubKey);
+        assertEquals("Mismatched public keys count", 1, GenericUtils.size(entries));
+
+        AuthorizedKeyEntry entry = entries.get(0);
+        PublicKey pubEntry = entry.resolvePublicKey(PublicKeyEntryResolver.FAILING);
+        assertNotNull("Cannot retrieve public key", pubEntry);
+
+        Class<? extends PublicKey> pubType = identity.getPublicKeyType();
+        Class<? extends PrivateKey> prvType = identity.getPrivateKeyType();
+        for (KeyPair kp : pairs) {
+            PublicKey pubKey = ValidateUtils.checkInstanceOf(kp.getPublic(), pubType, "Mismatched public key type");
+            assertKeyEquals("Mismatched identity public key", pubEntry, pubKey);
+
+            PrivateKey prvKey = ValidateUtils.checkInstanceOf(kp.getPrivate(), prvType, "Mismatched private key type");
+            @SuppressWarnings("rawtypes")
+            PrivateKeyEntryDecoder decoder =
+                OpenSSHKeyPairResourceParser.getPrivateKeyEntryDecoder(prvKey);
+            assertNotNull("No private key decoder", decoder);
+
+            if (decoder.isPublicKeyRecoverySupported()) {
+                @SuppressWarnings("unchecked")
+                PublicKey recKey = decoder.recoverPublicKey(prvKey);
+                assertKeyEquals("Mismatched recovered public key", pubKey, recKey);
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParserTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParserTest.java b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParserTest.java
new file mode 100644
index 0000000..406a273
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParserTest.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.config.keys.loader.pem;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.PrivateKey;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.commons.ssl.PEMItem;
+import org.apache.commons.ssl.PEMUtil;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.junit.runners.Parameterized.UseParametersRunnerFactory;
+
+/**
+ * TODO Add javadoc
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@RunWith(Parameterized.class)   // see https://github.com/junit-team/junit/wiki/Parameterized-tests
+@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class PKCS8PEMResourceKeyPairParserTest extends JUnitTestSupport {
+    private final String algorithm;
+    private final int keySize;
+
+    public PKCS8PEMResourceKeyPairParserTest(String algorithm, int keySize) {
+        this.algorithm = algorithm;
+        this.keySize = keySize;
+    }
+
+    @Parameters(name = "{0} / {1}")
+    public static List<Object[]> parameters() {
+        List<Object[]> params = new ArrayList<>();
+        for (Integer ks : RSA_SIZES) {
+            params.add(new Object[]{KeyUtils.RSA_ALGORITHM, ks});
+        }
+        for (Integer ks : DSS_SIZES) {
+            params.add(new Object[]{KeyUtils.DSS_ALGORITHM, ks});
+        }
+        return params;
+    }
+
+    @Test   // see SSHD-760
+    public void testPkcs8() throws IOException, GeneralSecurityException {
+        KeyPairGenerator generator = SecurityUtils.getKeyPairGenerator(algorithm);
+        if (keySize > 0) {
+            generator.initialize(keySize);
+        }
+        KeyPair kp = generator.generateKeyPair();
+
+        try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
+            Collection<Object> items = new ArrayList<>();
+            PrivateKey prv1 = kp.getPrivate();
+            items.add(new PEMItem(prv1.getEncoded(), "PRIVATE KEY"));
+            byte[] bytes = PEMUtil.encode(items);
+            os.write(bytes);
+            os.close();
+
+            try (ByteArrayInputStream bais = new ByteArrayInputStream(os.toByteArray())) {
+                KeyPair kp2 = SecurityUtils.loadKeyPairIdentity(getCurrentTestName(), bais, null);
+
+                assertEquals("Mismatched public key", kp.getPublic(), kp2.getPublic());
+                assertEquals("Mismatched private key", prv1, kp2.getPrivate());
+            }
+        }
+    }
+
+    @Override
+    public String toString() {
+        return getClass().getSimpleName() + "[" + algorithm + "/" + keySize + "]";
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/digest/BuiltinDigestsTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/digest/BuiltinDigestsTest.java b/sshd-common/src/test/java/org/apache/sshd/common/digest/BuiltinDigestsTest.java
new file mode 100644
index 0000000..9ec928b
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/digest/BuiltinDigestsTest.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.digest;
+
+import java.lang.reflect.Field;
+import java.util.EnumSet;
+import java.util.Set;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class BuiltinDigestsTest extends JUnitTestSupport {
+    public BuiltinDigestsTest() {
+        super();
+    }
+
+    @Test
+    public void testFromName() {
+        for (BuiltinDigests expected : BuiltinDigests.VALUES) {
+            String name = expected.getName();
+            BuiltinDigests actual = BuiltinDigests.fromFactoryName(name);
+            assertSame(name, expected, actual);
+        }
+    }
+
+    @Test
+    public void testAllConstantsCovered() throws Exception {
+        Set<BuiltinDigests> avail = EnumSet.noneOf(BuiltinDigests.class);
+        Field[] fields = BuiltinDigests.Constants.class.getFields();
+        for (Field f : fields) {
+            String name = (String) f.get(null);
+            BuiltinDigests value = BuiltinDigests.fromFactoryName(name);
+            assertNotNull("No match found for " + name, value);
+            assertTrue(name + " re-specified", avail.add(value));
+        }
+
+        assertEquals("Incomplete coverage", BuiltinDigests.VALUES, avail);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/future/DefaultSshFutureTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/future/DefaultSshFutureTest.java b/sshd-common/src/test/java/org/apache/sshd/common/future/DefaultSshFutureTest.java
new file mode 100644
index 0000000..05e3df2
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/future/DefaultSshFutureTest.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.future;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+import org.mockito.Mockito;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class DefaultSshFutureTest extends JUnitTestSupport {
+    public DefaultSshFutureTest() {
+        super();
+    }
+
+    @Test
+    @SuppressWarnings("rawtypes")
+    public void testAwaitUninterrupted() {
+        DefaultSshFuture future = new DefaultSshFuture(getCurrentTestName(), null);
+        Object expected = new Object();
+        new Thread() {
+            @Override
+            public void run() {
+                try {
+                    Thread.sleep(TimeUnit.SECONDS.toMillis(2L));
+                } catch (InterruptedException e) {
+                    throw new RuntimeException(e);
+                }
+                future.setValue(expected);
+            }
+        }.start();
+
+        future.awaitUninterruptibly();
+        assertSame("Mismatched signalled value", expected, future.getValue());
+    }
+
+    @Test
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    public void testNotifyMultipleListeners() {
+        DefaultSshFuture future = new DefaultSshFuture(getCurrentTestName(), null);
+        AtomicInteger listenerCount = new AtomicInteger(0);
+        Object expected = new Object();
+        SshFutureListener listener = f -> {
+            assertSame("Mismatched future instance", future, f);
+            assertSame("Mismatched value object", expected, future.getValue());
+            listenerCount.incrementAndGet();
+        };
+
+        int numListeners = Byte.SIZE;
+        for (int index = 0; index < numListeners; index++) {
+            future.addListener(listener);
+        }
+
+        future.setValue(expected);
+        assertEquals("Mismatched listeners invocation count", numListeners, listenerCount.get());
+    }
+
+    @Test
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    public void testListenerInvokedDirectlyAfterResultSet() {
+        DefaultSshFuture future = new DefaultSshFuture(getCurrentTestName(), null);
+        AtomicInteger listenerCount = new AtomicInteger(0);
+        Object expected = new Object();
+        SshFutureListener listener = f -> {
+            assertSame("Mismatched future instance", future, f);
+            assertSame("Mismatched value object", expected, future.getValue());
+            listenerCount.incrementAndGet();
+        };
+        future.setValue(expected);
+
+        future.addListener(listener);
+        assertEquals("Mismatched number of registered listeners", 0, future.getNumRegisteredListeners());
+        assertEquals("Listener not invoked", 1, listenerCount.get());
+    }
+
+    @Test
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    public void testAddAndRemoveRegisteredListenersBeforeResultSet() {
+        DefaultSshFuture future = new DefaultSshFuture(getCurrentTestName(), null);
+        SshFutureListener listener = Mockito.mock(SshFutureListener.class);
+        for (int index = 1; index <= Byte.SIZE; index++) {
+            future.addListener(listener);
+            assertEquals("Mismatched number of added listeners", index, future.getNumRegisteredListeners());
+        }
+
+        for (int index = future.getNumRegisteredListeners() - 1; index >= 0; index--) {
+            future.removeListener(listener);
+            assertEquals("Mismatched number of remaining listeners", index, future.getNumRegisteredListeners());
+        }
+    }
+
+    @Test
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    public void testListenerNotRemovedIfResultSet() {
+        DefaultSshFuture future = new DefaultSshFuture(getCurrentTestName(), null);
+        AtomicInteger listenerCount = new AtomicInteger(0);
+        Object expected = new Object();
+        SshFutureListener listener = f -> {
+            assertSame("Mismatched future instance", future, f);
+            assertSame("Mismatched value object", expected, future.getValue());
+            listenerCount.incrementAndGet();
+        };
+        future.addListener(listener);
+        future.setValue(expected);
+        assertEquals("Mismatched number of registered listeners", 1, future.getNumRegisteredListeners());
+        assertEquals("Listener not invoked", 1, listenerCount.get());
+
+        future.removeListener(listener);
+        assertEquals("Mismatched number of remaining listeners", 1, future.getNumRegisteredListeners());
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/keyprovider/KeyPairProviderTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/keyprovider/KeyPairProviderTest.java b/sshd-common/src/test/java/org/apache/sshd/common/keyprovider/KeyPairProviderTest.java
new file mode 100644
index 0000000..021796b
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/keyprovider/KeyPairProviderTest.java
@@ -0,0 +1,79 @@
+/*
+ * 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.keyprovider;
+
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.function.Function;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+import org.mockito.Mockito;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class KeyPairProviderTest extends JUnitTestSupport {
+    public KeyPairProviderTest() {
+        super();
+    }
+
+    @Test
+    public void testEmptyKeyProvider() {
+        KeyPairProvider provider = KeyPairProvider.EMPTY_KEYPAIR_PROVIDER;
+        assertTrue("Non empty loaded keys", GenericUtils.isEmpty(provider.loadKeys()));
+        assertTrue("Non empty key type", GenericUtils.isEmpty(provider.getKeyTypes()));
+
+        for (String keyType : new String[]{null, "", getCurrentTestName()}) {
+            assertNull("Unexpected key-pair loaded for type='" + keyType + "'", provider.loadKey(keyType));
+        }
+    }
+
+    @Test
+    public void testMapToKeyPairProvider() {
+        PublicKey pubKey = Mockito.mock(PublicKey.class);
+        PrivateKey prvKey = Mockito.mock(PrivateKey.class);
+        String[] testKeys = {getCurrentTestName(), getClass().getSimpleName()};
+        Map<String, KeyPair> pairsMap = GenericUtils.toSortedMap(
+            Arrays.asList(testKeys),
+            Function.identity(),
+            k -> new KeyPair(pubKey, prvKey),
+            String.CASE_INSENSITIVE_ORDER);
+
+        KeyPairProvider provider = MappedKeyPairProvider.MAP_TO_KEY_PAIR_PROVIDER.apply(pairsMap);
+        assertEquals("Key types", pairsMap.keySet(), provider.getKeyTypes());
+        assertEquals("Key pairs", pairsMap.values(), provider.loadKeys());
+
+        pairsMap.forEach((keyType, expected) -> {
+            KeyPair actual = provider.loadKey(keyType);
+            assertSame(keyType, expected, actual);
+        });
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/mac/BuiltinMacsTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/mac/BuiltinMacsTest.java b/sshd-common/src/test/java/org/apache/sshd/common/mac/BuiltinMacsTest.java
new file mode 100644
index 0000000..0d50c41
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/mac/BuiltinMacsTest.java
@@ -0,0 +1,166 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.mac;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.mac.BuiltinMacs.ParseResult;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+import org.mockito.Mockito;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class BuiltinMacsTest extends JUnitTestSupport {
+    public BuiltinMacsTest() {
+        super();
+    }
+
+    @Test
+    public void testFromName() {
+        for (BuiltinMacs expected : BuiltinMacs.VALUES) {
+            String name = expected.getName();
+            BuiltinMacs actual = BuiltinMacs.fromFactoryName(name);
+            assertSame(name, expected, actual);
+        }
+    }
+
+    @Test
+    public void testAllConstantsCovered() throws Exception {
+        Set<BuiltinMacs> avail = EnumSet.noneOf(BuiltinMacs.class);
+        Field[] fields = BuiltinMacs.Constants.class.getFields();
+        for (Field f : fields) {
+            String name = (String) f.get(null);
+            BuiltinMacs value = BuiltinMacs.fromFactoryName(name);
+            assertNotNull("No match found for " + name, value);
+            assertTrue(name + " re-specified", avail.add(value));
+        }
+
+        assertEquals("Incomplete coverage", BuiltinMacs.VALUES, avail);
+    }
+
+    @Test
+    public void testParseMacsList() {
+        List<String> builtin = NamedResource.getNameList(BuiltinMacs.VALUES);
+        List<String> unknown = Arrays.asList(getClass().getPackage().getName(), getClass().getSimpleName(), getCurrentTestName());
+        Random rnd = new Random();
+        for (int index = 0; index < (builtin.size() + unknown.size()); index++) {
+            Collections.shuffle(builtin, rnd);
+            Collections.shuffle(unknown, rnd);
+
+            List<String> weavedList = new ArrayList<>(builtin.size() + unknown.size());
+            for (int bIndex = 0, uIndex = 0; (bIndex < builtin.size()) || (uIndex < unknown.size());) {
+                boolean useBuiltin = false;
+                if (bIndex < builtin.size()) {
+                    useBuiltin = uIndex >= unknown.size() || rnd.nextBoolean();
+                }
+
+                if (useBuiltin) {
+                    weavedList.add(builtin.get(bIndex));
+                    bIndex++;
+                } else if (uIndex < unknown.size()) {
+                    weavedList.add(unknown.get(uIndex));
+                    uIndex++;
+                }
+            }
+
+            String fullList = GenericUtils.join(weavedList, ',');
+            ParseResult result = BuiltinMacs.parseMacsList(fullList);
+            List<String> parsed = NamedResource.getNameList(result.getParsedFactories());
+            List<String> missing = result.getUnsupportedFactories();
+
+            // makes sure not only that the contents are the same but also the order
+            assertListEquals(fullList + "[parsed]", builtin, parsed);
+            assertListEquals(fullList + "[unsupported]", unknown, missing);
+        }
+    }
+
+    @Test
+    public void testResolveFactoryOnBuiltinValues() {
+        for (MacFactory expected : BuiltinMacs.VALUES) {
+            String name = expected.getName();
+            MacFactory actual = BuiltinMacs.resolveFactory(name);
+            assertSame(name, expected, actual);
+        }
+    }
+
+    @Test
+    public void testNotAllowedToRegisterBuiltinFactories() {
+        for (MacFactory expected : BuiltinMacs.VALUES) {
+            try {
+                BuiltinMacs.registerExtension(expected);
+                fail("Unexpected success for " + expected.getName());
+            } catch (IllegalArgumentException e) {
+                // expected - ignored
+            }
+        }
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testNotAllowedToOverrideRegisteredFactories() {
+        MacFactory expected = Mockito.mock(MacFactory.class);
+        Mockito.when(expected.getName()).thenReturn(getCurrentTestName());
+
+        String name = expected.getName();
+        try {
+            for (int index = 1; index <= Byte.SIZE; index++) {
+                BuiltinMacs.registerExtension(expected);
+                assertEquals("Unexpected success at attempt #" + index, 1, index);
+            }
+        } finally {
+            BuiltinMacs.unregisterExtension(name);
+        }
+    }
+
+    @Test
+    public void testResolveFactoryOnRegisteredExtension() {
+        MacFactory expected = Mockito.mock(MacFactory.class);
+        Mockito.when(expected.getName()).thenReturn(getCurrentTestName());
+
+        String name = expected.getName();
+        try {
+            assertNull("Extension already registered", BuiltinMacs.resolveFactory(name));
+            BuiltinMacs.registerExtension(expected);
+
+            MacFactory actual = BuiltinMacs.resolveFactory(name);
+            assertSame("Mismatched resolved instance", expected, actual);
+        } finally {
+            MacFactory actual = BuiltinMacs.unregisterExtension(name);
+            assertSame("Mismatched unregistered instance", expected, actual);
+            assertNull("Extension not un-registered", BuiltinMacs.resolveFactory(name));
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/mac/MacVectorsTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/mac/MacVectorsTest.java b/sshd-common/src/test/java/org/apache/sshd/common/mac/MacVectorsTest.java
new file mode 100644
index 0000000..f330d22
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/mac/MacVectorsTest.java
@@ -0,0 +1,311 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.mac;
+
+import java.nio.charset.StandardCharsets;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+import org.apache.sshd.common.Factory;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.buffer.BufferUtils;
+import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.junit.runners.Parameterized.UseParametersRunnerFactory;
+
+/**
+ * @see <A HREF="https://tools.ietf.org/html/rfc4231">RFC 4321</A>
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RunWith(Parameterized.class)   // see https://github.com/junit-team/junit/wiki/Parameterized-tests
+@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
+@Category({ NoIoTestCase.class })
+public class MacVectorsTest extends JUnitTestSupport {
+    private final VectorSeed seed;
+    private final Factory<? extends Mac> macFactory;
+    private final byte[] expected;
+
+    public MacVectorsTest(VectorSeed seed, String factoryName, String expected) {
+        this.seed = Objects.requireNonNull(seed, "No seed");
+        this.macFactory = ValidateUtils.checkNotNull(BuiltinMacs.fromFactoryName(factoryName), "Unknown MAC: %s", factoryName);
+        this.expected = BufferUtils.decodeHex(BufferUtils.EMPTY_HEX_SEPARATOR, expected);
+    }
+
+    @Parameters(name = "factory={1}, expected={2}, seed={0}")
+    public static Collection<Object[]> parameters() {
+        List<Object[]> ret = new ArrayList<>();
+        for (VectorTestData vector : Collections.unmodifiableList(
+                Arrays.asList(
+                    ///////////////// Test Cases for HMAC-MD5 ///////////////////////
+                    // see https://tools.ietf.org/html/rfc2202
+                    new VectorTestData("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", false, "Hi There",
+                       Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_MD5,     // test case 1
+                                                "9294727a3638bb1c13f48ef8158bfc9d"))),
+                    new VectorTestData("Jefe", "what do ya want for nothing?",
+                        Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_MD5,    // test case 2
+                                                 "750c783e6ab0b503eaa86e310a5db738"))),
+                    new VectorTestData("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", false, repeat("dd", 50), false,
+                        Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_MD5,    // test case 3
+                                                 "56be34521d144c88dbb8c733f0e8b3f6"))),
+                    /* TODO see why it fails
+                    new VectorTestData("0102030405060708090a0b0c0d0e0f10111213141516171819", false, repeat("cd", 50), false,
+                        Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_MD5,    // test case 4
+                                                 "697eaf0aca3a3aea3a75164746ffaa79"))),
+                    */
+                    new VectorTestData("0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c", false, "Test With Truncation",
+                        Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_MD5,    // test case 5
+                                                 "56461ef2342edc00f9bab995690efd4c"),
+                                      new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_MD5_96,
+                                                 "56461ef2342edc00f9bab995"))),
+                    /* TODO see why it fails
+                    new VectorTestData(repeat("aa", 80), false, "Test Using Larger Than Block-Size Key - Hash Key First",
+                        Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_MD5,    // test case 6
+                                                 "6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd"))),
+                    */
+                    /* TODO see why it fails
+                    new VectorTestData(repeat("aa", 80), false, "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data",
+                        Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_MD5,    // test case 7
+                                                 "6f630fad67cda0ee1fb1f562db3aa53e"))),
+                    */
+                    ///////////////// Test Cases for HMAC-SHA-1 ///////////////////////
+                    // see https://tools.ietf.org/html/rfc2202
+                    new VectorTestData("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", false, "Hi There",
+                        Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA1,     // test case 1
+                                                 "b617318655057264e28bc0b6fb378c8ef146be00"))),
+                    new VectorTestData("Jefe", "what do ya want for nothing?",
+                        Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA1,     // test case 2
+                                                 "effcdf6ae5eb2fa2d27416d5f184df9c259a7c79"))),
+                    new VectorTestData("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", false, repeat("dd", 50), false,
+                        Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA1,     // test case 3
+                                                 "125d7342b9ac11cd91a39af48aa17b4f63f175d3"))),
+                    /* TODO see why it fails
+                    new VectorTestData("0102030405060708090a0b0c0d0e0f10111213141516171819", false, repeat("cd", 50), false,
+                        Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA1,     // test case 4
+                                                 "4c9007f4026250c6bc8414f9bf50c86c2d7235da"))),
+                    */
+                    new VectorTestData("0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c", false, "Test With Truncation",
+                        Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA1,     // test case 5
+                                                 "4c1a03424b55e07fe7f27be1d58bb9324a9a5a04"),
+                                      new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA1_96,
+                                                 "4c1a03424b55e07fe7f27be1"))),
+                    /* TODO see why this fails
+                    new VectorTestData(repeat("aa", 80), false, "Test Using Larger Than Block-Size Key - Hash Key First",
+                        Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA1,     // test case 6
+                                                 "aa4ae5e15272d00e95705637ce8a3b55ed402112"))),
+                    */
+
+                    /* TODO see why it fails
+                    new VectorTestData(repeat("aa", 80), false, "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data",
+                        Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA1,     // test case 7
+                                                 "4c1a03424b55e07fe7f27be1d58bb9324a9a5a04"),
+                                      new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA1_96,
+                                                 "4c1a03424b55e07fe7f27be1"))),
+                    */
+
+                    /* TODO see why it fails
+                    new VectorTestData(repeat("aa", 80), false, "Test Using Larger Than Block-Size Key - Hash Key First",
+                        Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA1,     // test case 8
+                                                 "aa4ae5e15272d00e95705637ce8a3b55ed402112"))),
+                    */
+
+                    /* TODO see why it fails
+                    new VectorTestData(repeat("aa", 80), false, "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data",
+                        Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA1,     // test case 9
+                                                 "e8e99d0f45237d786d6bbaa7965c7808bbff1a91"))),
+                    */
+
+                    ///////////////// Test Cases for HMAC-SHA-2 ///////////////////////
+                    // see https://tools.ietf.org/html/rfc4231
+                    new VectorTestData(repeat("0b", 20), false, "Hi There",
+                       // test case 1
+                       Arrays.asList(
+                           new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_256,
+                                      "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7"),
+                           new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_512,
+                                      "87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cdedaa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a126854"))),
+                    new VectorTestData("Jefe", "what do ya want for nothing?",
+                        // test case 2
+                        Arrays.asList(
+                            new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_256,
+                                       "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843"),
+                            new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_512,
+                                       "164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea2505549758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737"))),
+                    new VectorTestData(repeat("aa", 20), false, repeat("dd", 50), false,
+                        // test case 3
+                        Arrays.asList(
+                            new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_256,
+                                       "773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe"),
+                            new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_512,
+                                       "fa73b0089d56a284efb0f0756c890be9b1b5dbdd8ee81a3655f83e33b2279d39bf3e848279a722c806b485a47e67c807b946a337bee8942674278859e13292fb"))),
+                    new VectorTestData("0102030405060708090a0b0c0d0e0f10111213141516171819", false, repeat("cd", 50), false,
+                        // test case 4
+                        Arrays.asList(
+                            new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_256,
+                                       "82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b"),
+                            new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_512,
+                                       "b0ba465637458c6990e5a8c5f61d4af7e576d97ff94b872de76f8050361ee3dba91ca5c11aa25eb4d679275cc5788063a5f19741120c4f2de2adebeb10a298dd"))),
+
+                    /* TODO see why it fails
+                    new VectorTestData(repeat("0c", 20), false, "Test With Truncation",
+                        Arrays.asList(   // test case 5
+                            new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_256,
+                                       "a3b6167473100ee06e0c796c2955552b"),
+                            new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_512,
+                                       "415fad6271580a531d4179bc891d87a6"))),
+                    */
+
+                    /* TODO see why it fails
+                    new VectorTestData(repeat("aa", 131), false, "Test Using Larger Than Block-Size Key - Hash Key First",
+                        Arrays.asList(   // test case 6
+                            new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_256,
+                                       "60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54"),
+                            new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_512,
+                                       "80b24263c7c1a3ebb71493c1dd7be8b49b46d1f41b4aeec1121b013783f8f3526b56d037e05f2598bd0fd2215d6a1e5295e64f73f63f0aec8b915a985d786598"))),
+                    */
+
+                    /* TODO see why it fails
+                    new VectorTestData(repeat("aa", 131), false, "This is a test using a larger than block-size"
+                                                               + " key and a larger than block-size data."
+                                                               + " The key needs to be hashed before being used"
+                                                               + " by the HMAC algorithm",
+                        Arrays.asList(   // test case 7
+                            new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_256,
+                                       "9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2"),
+                            new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_512,
+                                       "e37b6a775dc87dbaa4dfa9f96e5e3ffddebd71f8867289865df5a32d20cdc944b6022cac3c4982b10d5eeb55c3e4de15134676fb6de0446065c97440fa8c6a58")))
+                    */
+
+                    // mark end
+                    new VectorTestData("", false, "", false, Collections.emptyList())))) {
+            for (Map.Entry<String, String> tc : vector.getResults()) {
+                ret.add(new Object[]{vector, tc.getKey(), tc.getValue()});
+            }
+        }
+
+        return ret;
+    }
+
+    @Test
+    public void testStandardVectorMac() throws Exception {
+        Mac mac = macFactory.create();
+        mac.init(seed.getKey());
+        mac.update(seed.getData());
+
+        byte[] actual = new byte[mac.getBlockSize()];
+        mac.doFinal(actual);
+        assertArrayEquals("Mismatched results for actual=" + BufferUtils.toHex(BufferUtils.EMPTY_HEX_SEPARATOR, actual), expected, actual);
+    }
+
+    private static class VectorSeed {
+        private final byte[] key;
+        private final String keyString;
+        private final byte[] data;
+        private final String dataString;
+
+        VectorSeed(String key, String data) {
+            this.key = key.getBytes(StandardCharsets.UTF_8);
+            this.keyString = key;
+            this.data = data.getBytes(StandardCharsets.UTF_8);
+            this.dataString = data;
+        }
+
+        VectorSeed(String key, boolean useKeyString, String data) {
+            this.key = BufferUtils.decodeHex(BufferUtils.EMPTY_HEX_SEPARATOR, key);
+            this.keyString = useKeyString ? new String(this.key, StandardCharsets.UTF_8) : key;
+            this.data = data.getBytes(StandardCharsets.UTF_8);
+            this.dataString = data;
+        }
+
+        VectorSeed(String key, boolean useKeyString, String data, boolean useDataString) {
+            this.key = BufferUtils.decodeHex(BufferUtils.EMPTY_HEX_SEPARATOR, key);
+            this.keyString = useKeyString ? new String(this.key, StandardCharsets.UTF_8) : key;
+            this.data = BufferUtils.decodeHex(BufferUtils.EMPTY_HEX_SEPARATOR, data);
+            this.dataString = useDataString ? new String(this.data, StandardCharsets.UTF_8) : data;
+        }
+
+        public byte[] getKey() {
+            return key.clone(); // clone to avoid inadvertent change
+        }
+
+        public String getKeyString() {
+            return keyString;
+        }
+
+        public byte[] getData() {
+            return data.clone();  // clone to avoid inadvertent change
+        }
+
+        public String getDataString() {
+            return dataString;
+        }
+
+        @Override
+        public String toString() {
+            return "key=" + trimToLength(getKeyString(), 32) + ", data=" + trimToLength(getDataString(), 32);
+        }
+
+        private static CharSequence trimToLength(CharSequence csq, int maxLen) {
+            if (GenericUtils.length(csq) <= maxLen) {
+                return csq;
+            }
+
+            return csq.subSequence(0, maxLen) + "...";
+        }
+    }
+
+    private static class VectorTestData extends VectorSeed {
+        private final Collection<Map.Entry<String, String>> results;
+
+        VectorTestData(String key, String data, Collection<Map.Entry<String, String>> results) {
+            super(key, data);
+            this.results = results;
+        }
+
+        VectorTestData(String key, boolean useKeyString, String data, Collection<Map.Entry<String, String>> results) {
+            super(key, useKeyString, data);
+            this.results = results;
+        }
+
+        VectorTestData(String key, boolean useKeyString, String data, boolean useDataString, Collection<Map.Entry<String, String>> results) {
+            super(key, useKeyString, data, useDataString);
+            this.results = results;
+        }
+
+        public Collection<Map.Entry<String, String>> getResults() {
+            return results;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/random/RandomFactoryTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/random/RandomFactoryTest.java b/sshd-common/src/test/java/org/apache/sshd/common/random/RandomFactoryTest.java
new file mode 100644
index 0000000..08872d9
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/random/RandomFactoryTest.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.random;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.sshd.common.util.security.SecurityUtils;
+import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.Assume;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.junit.runners.Parameterized.UseParametersRunnerFactory;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RunWith(Parameterized.class)   // see https://github.com/junit-team/junit/wiki/Parameterized-tests
+@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
+@Category({ NoIoTestCase.class })
+public class RandomFactoryTest extends JUnitTestSupport {
+    private final RandomFactory factory;
+
+    public RandomFactoryTest(RandomFactory factory) {
+        this.factory = factory;
+    }
+
+    @Parameters(name = "type={0}")
+    public static Collection<Object[]> parameters() {
+        Collection<RandomFactory> testCases = new LinkedList<>();
+        testCases.add(JceRandomFactory.INSTANCE);
+        if (SecurityUtils.isBouncyCastleRegistered()) {
+            testCases.add(SecurityUtils.getRandomFactory());
+        } else {
+            System.out.println("Skip BouncyCastleRandomFactory - unsupported");
+        }
+
+        return parameterize(testCases);
+    }
+
+    @Test
+    public void testRandomFactory() {
+        Assume.assumeTrue("Skip unsupported factory: " + factory.getName(), factory.isSupported());
+        long t = testRandom(factory.create());
+        System.out.println(factory.getName() + " duration: " + t + " " + TimeUnit.MICROSECONDS);
+    }
+
+    // returns duration in microseconds
+    private static long testRandom(Random random) {
+        byte[] bytes = new byte[32];
+        long l0 = System.nanoTime();
+        for (int i = 0; i < 1000; i++) {
+            random.fill(bytes, 8, 16);
+        }
+        long l1 = System.nanoTime();
+        return TimeUnit.NANOSECONDS.toMicros(l1 - l0);
+    }
+}


[27/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common utilities code from sshd-core into sshd-common (new artifact)

Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/DefaultConfigFileHostEntryResolver.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/DefaultConfigFileHostEntryResolver.java b/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/DefaultConfigFileHostEntryResolver.java
deleted file mode 100644
index da5f687..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/DefaultConfigFileHostEntryResolver.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.client.config.hosts;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.LinkOption;
-import java.nio.file.Path;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-
-import org.apache.sshd.common.util.io.IoUtils;
-
-/**
- * Monitors the {@code ~/.ssh/config} file of the user currently running
- * the client, re-loading it if necessary. It also (optionally) enforces
- * the same permissions regime as {@code OpenSSH}
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class DefaultConfigFileHostEntryResolver extends ConfigFileHostEntryResolver {
-    /**
-     * The default instance that enforces the same permissions regime as {@code OpenSSH}
-     */
-    public static final DefaultConfigFileHostEntryResolver INSTANCE = new DefaultConfigFileHostEntryResolver(true);
-
-    private final boolean strict;
-
-    /**
-     * @param strict If {@code true} then makes sure that the containing folder
-     *               has 0700 access and the file 0644. <B>Note:</B> for <I>Windows</I> it
-     *               does not check these permissions
-     * @see #validateStrictConfigFilePermissions(Path, LinkOption...)
-     */
-    public DefaultConfigFileHostEntryResolver(boolean strict) {
-        this(HostConfigEntry.getDefaultHostConfigFile(), strict);
-    }
-
-    public DefaultConfigFileHostEntryResolver(File file, boolean strict) {
-        this(Objects.requireNonNull(file, "No file provided").toPath(), strict, IoUtils.getLinkOptions(true));
-    }
-
-    public DefaultConfigFileHostEntryResolver(Path path, boolean strict, LinkOption... options) {
-        super(path, options);
-        this.strict = strict;
-    }
-
-    /**
-     * @return If {@code true} then makes sure that the containing folder
-     * has 0700 access and the file 0644. <B>Note:</B> for <I>Windows</I> it
-     * does not check these permissions
-     * @see #validateStrictConfigFilePermissions(Path, LinkOption...)
-     */
-    public final boolean isStrict() {
-        return strict;
-    }
-
-    @Override
-    protected List<HostConfigEntry> reloadHostConfigEntries(Path path, String host, int port, String username) throws IOException {
-        if (isStrict()) {
-            if (log.isDebugEnabled()) {
-                log.debug("reloadHostConfigEntries({}@{}:{}) check permissions of {}", username, host, port, path);
-            }
-
-            Map.Entry<String, ?> violation = validateStrictConfigFilePermissions(path);
-            if (violation != null) {
-                log.warn("reloadHostConfigEntries({}@{}:{}) invalid file={} permissions: {}",
-                         username, host, port, path, violation.getKey());
-                updateReloadAttributes();
-                return Collections.emptyList();
-            }
-        }
-
-        return super.reloadHostConfigEntries(path, host, port, username);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntry.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntry.java b/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntry.java
deleted file mode 100644
index f401cee..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntry.java
+++ /dev/null
@@ -1,1169 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sshd.client.config.hosts;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.Reader;
-import java.io.StreamCorruptedException;
-import java.io.Writer;
-import java.net.InetAddress;
-import java.net.URL;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.OpenOption;
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.TreeMap;
-
-import org.apache.sshd.common.auth.MutableUserHolder;
-import org.apache.sshd.common.config.SshConfigFileReader;
-import org.apache.sshd.common.config.keys.IdentityUtils;
-import org.apache.sshd.common.config.keys.PublicKeyEntry;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.OsUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.common.util.io.NoCloseInputStream;
-import org.apache.sshd.common.util.io.NoCloseOutputStream;
-import org.apache.sshd.common.util.io.NoCloseReader;
-
-/**
- * Represents an entry in the client's configuration file as defined by
- * the <A HREF="http://www.gsp.com/cgi-bin/man.cgi?topic=ssh_config">configuration
- * file format</A>
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class HostConfigEntry extends HostPatternsHolder implements MutableUserHolder {
-    /**
-     * Standard OpenSSH config file name
-     */
-    public static final String STD_CONFIG_FILENAME = "config";
-
-    public static final String HOST_CONFIG_PROP = "Host";
-    public static final String HOST_NAME_CONFIG_PROP = "HostName";
-    public static final String PORT_CONFIG_PROP = SshConfigFileReader.PORT_CONFIG_PROP;
-    public static final String USER_CONFIG_PROP = "User";
-    public static final String IDENTITY_FILE_CONFIG_PROP = "IdentityFile";
-    /**
-     * Use only the identities specified in the host entry (if any)
-     */
-    public static final String EXCLUSIVE_IDENTITIES_CONFIG_PROP = "IdentitiesOnly";
-    public static final boolean DEFAULT_EXCLUSIVE_IDENTITIES = false;
-
-    /**
-     * A case <U>insensitive</U> {@link Set} of the properties that receive special handling
-     */
-    public static final Set<String> EXPLICIT_PROPERTIES =
-            Collections.unmodifiableSet(
-                    GenericUtils.asSortedSet(String.CASE_INSENSITIVE_ORDER,
-                            HOST_CONFIG_PROP, HOST_NAME_CONFIG_PROP, PORT_CONFIG_PROP,
-                            USER_CONFIG_PROP, IDENTITY_FILE_CONFIG_PROP, EXCLUSIVE_IDENTITIES_CONFIG_PROP
-                        ));
-
-    public static final String MULTI_VALUE_SEPARATORS = " ,";
-
-    public static final char HOME_TILDE_CHAR = '~';
-    public static final char PATH_MACRO_CHAR = '%';
-    public static final char LOCAL_HOME_MACRO = 'd';
-    public static final char LOCAL_USER_MACRO = 'u';
-    public static final char LOCAL_HOST_MACRO = 'l';
-    public static final char REMOTE_HOST_MACRO = 'h';
-    public static final char REMOTE_USER_MACRO = 'r';
-    // Extra - not part of the standard
-    public static final char REMOTE_PORT_MACRO = 'p';
-
-    private static final class LazyDefaultConfigFileHolder {
-        private static final Path CONFIG_FILE =
-            PublicKeyEntry.getDefaultKeysFolderPath().resolve(STD_CONFIG_FILENAME);
-
-        private LazyDefaultConfigFileHolder() {
-            throw new UnsupportedOperationException("No instance allowed");
-        }
-    }
-
-    private String host;
-    private String hostName;
-    private int port;
-    private String username;
-    private Boolean exclusiveIdentites;
-    private Collection<String> identities = Collections.emptyList();
-    private Map<String, String> properties = Collections.emptyMap();
-
-    public HostConfigEntry() {
-        super();
-    }
-
-    public HostConfigEntry(String pattern, String host, int port, String username) {
-        setHost(pattern);
-        setHostName(host);
-        setPort(port);
-        setUsername(username);
-    }
-
-    /**
-     * @return The <U>pattern(s)</U> represented by this entry
-     */
-    public String getHost() {
-        return host;
-    }
-
-    public void setHost(String host) {
-        this.host = host;
-        setPatterns(parsePatterns(parseConfigValue(host)));
-    }
-
-    public void setHost(Collection<String> patterns) {
-        this.host = GenericUtils.join(ValidateUtils.checkNotNullAndNotEmpty(patterns, "No patterns"), ',');
-        setPatterns(parsePatterns(patterns));
-    }
-
-    /**
-     * @return The effective host name to connect to if the pattern matches
-     */
-    public String getHostName() {
-        return hostName;
-    }
-
-    public void setHostName(String hostName) {
-        this.hostName = hostName;
-    }
-
-    public String resolveHostName(String originalHost) {
-        return resolveHostName(originalHost, getHostName());
-    }
-
-    /**
-     * @return A port override - if positive
-     */
-    public int getPort() {
-        return port;
-    }
-
-    public void setPort(int port) {
-        this.port = port;
-    }
-
-    /**
-     * Resolves the effective port to use
-     *
-     * @param originalPort The original requested port
-     * @return If the host entry port is positive, then it is used, otherwise
-     * the original requested port
-     * @see #resolvePort(int, int)
-     */
-    public int resolvePort(int originalPort) {
-        return resolvePort(originalPort, getPort());
-    }
-
-    /**
-     * @return A username override - if not {@code null}/empty
-     */
-    @Override
-    public String getUsername() {
-        return username;
-    }
-
-    @Override
-    public void setUsername(String username) {
-        this.username = username;
-    }
-
-    /**
-     * Resolves the effective username
-     *
-     * @param originalUser The original requested username
-     * @return If the configured host entry username is not {@code null}/empty
-     * then it is used, otherwise the original one.
-     * @see #resolveUsername(String)
-     */
-    public String resolveUsername(String originalUser) {
-        return resolveUsername(originalUser, getUsername());
-    }
-
-    /**
-     * @return The current identities file paths - may be {@code null}/empty
-     */
-    public Collection<String> getIdentities() {
-        return identities;
-    }
-
-    /**
-     * @param file A {@link File} that contains an identity key - never {@code null}
-     */
-    public void addIdentity(File file) {
-        addIdentity(Objects.requireNonNull(file, "No file").toPath());
-    }
-
-    /**
-     * @param path A {@link Path} to a file that contains an identity key
-     * - never {@code null}
-     */
-    public void addIdentity(Path path) {
-        addIdentity(Objects.requireNonNull(path, "No path").toAbsolutePath().normalize().toString());
-    }
-
-    /**
-     * Adds a path to an identity file
-     *
-     * @param id The identity path to add - never {@code null}
-     */
-    public void addIdentity(String id) {
-        String path = ValidateUtils.checkNotNullAndNotEmpty(id, "No identity provided");
-        if (GenericUtils.isEmpty(identities)) {
-            identities = new LinkedList<>();
-        }
-        identities.add(path);
-    }
-
-    public void setIdentities(Collection<String> identities) {
-        this.identities = (identities == null) ? Collections.emptyList() : identities;
-    }
-
-    /**
-     * @return {@code true} if must use only the identities in this entry
-     */
-    public boolean isIdentitiesOnly() {
-        return (exclusiveIdentites == null) ? DEFAULT_EXCLUSIVE_IDENTITIES : exclusiveIdentites;
-    }
-
-    public void setIdentitiesOnly(boolean identitiesOnly) {
-        exclusiveIdentites = identitiesOnly;
-    }
-
-    /**
-     * @return A {@link Map} of extra properties that have been read - may be
-     * {@code null}/empty, or even contain some values that have been parsed
-     * and set as members of the entry (e.g., host, port, etc.). <B>Note:</B>
-     * multi-valued keys use a comma-separated list of values
-     */
-    public Map<String, String> getProperties() {
-        return properties;
-    }
-
-    /**
-     * @param name Property name - never {@code null}/empty
-     * @return Property value or {@code null}  if no such property
-     * @see #getProperty(String, String)
-     */
-    public String getProperty(String name) {
-        return getProperty(name, null);
-    }
-
-    /**
-     * @param name Property name - never {@code null}/empty
-     * @param defaultValue Default value to return if no such property
-     * @return The property value or the default one if no such property
-     */
-    public String getProperty(String name, String defaultValue) {
-        String key = ValidateUtils.checkNotNullAndNotEmpty(name, "No property name");
-        Map<String, String> props = getProperties();
-        if (GenericUtils.isEmpty(props)) {
-            return null;
-        }
-
-        String value = props.get(key);
-        if (GenericUtils.isEmpty(value)) {
-            return defaultValue;
-        } else {
-            return value;
-        }
-    }
-
-    /**
-     * Updates the values that are <U>not</U> already configured with those
-     * from the global entry
-     *
-     * @param globalEntry The global entry - ignored if {@code null} or
-     * same reference as this entry
-     * @return {@code true} if anything updated
-     */
-    public boolean processGlobalValues(HostConfigEntry globalEntry) {
-        if ((globalEntry == null) || (this == globalEntry)) {
-            return false;
-        }
-
-        boolean modified = false;
-        /*
-         * NOTE !!! DO NOT TRY TO CHANGE THE ORDER OF THE OR-ing AS IT
-         * WOULD CAUSE INVALID CODE EXECUTION
-         */
-        modified = updateGlobalPort(globalEntry.getPort()) || modified;
-        modified = updateGlobalHostName(globalEntry.getHostName()) || modified;
-        modified = updateGlobalUserName(globalEntry.getUsername()) || modified;
-        modified = updateGlobalIdentities(globalEntry.getIdentities()) || modified;
-        modified = updateGlobalIdentityOnly(globalEntry.isIdentitiesOnly()) || modified;
-
-        Map<String, String> updated = updateGlobalProperties(globalEntry.getProperties());
-        modified = (GenericUtils.size(updated) > 0) || modified;
-
-        return modified;
-    }
-
-    /**
-     * Sets all the properties for which no current value exists in the entry
-     *
-     * @param props The global properties - ignored if {@code null}/empty
-     * @return A {@link Map} of the <U>updated</U> properties
-     */
-    public Map<String, String> updateGlobalProperties(Map<String, String> props) {
-        if (GenericUtils.isEmpty(props)) {
-            return Collections.emptyMap();
-        }
-
-        Map<String, String> updated = null;
-        // Cannot use forEach because of the modification of the updated map value (non-final)
-        for (Map.Entry<String, String> pe : props.entrySet()) {
-            String key = pe.getKey();
-            String curValue = getProperty(key);
-            if (GenericUtils.length(curValue) > 0) {
-                continue;
-            }
-
-            String newValue = pe.getValue();
-            setProperty(key, newValue);
-
-            if (updated == null) {
-                updated = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
-            }
-
-            updated.put(key, newValue);
-        }
-
-        if (updated == null) {
-            return Collections.emptyMap();
-        } else {
-            return updated;
-        }
-    }
-
-    /**
-     * @param ids Global identities - ignored if {@code null}/empty or already
-     * have configured identities
-     * @return {@code true} if updated identities
-     */
-    public boolean updateGlobalIdentities(Collection<String> ids) {
-        if (GenericUtils.isEmpty(ids) || (GenericUtils.size(getIdentities()) > 0)) {
-            return false;
-        }
-
-        for (String id : ids) {
-            addIdentity(id);
-        }
-
-        return true;
-    }
-
-    /**
-     * @param user The global user name - ignored if {@code null}/empty or
-     * already have a configured user
-     * @return {@code true} if updated the username
-     */
-    public boolean updateGlobalUserName(String user) {
-        if (GenericUtils.isEmpty(user) || (GenericUtils.length(getUsername()) > 0)) {
-            return false;
-        }
-
-        setUsername(user);
-        return true;
-    }
-
-    /**
-     * @param name The global host name - ignored if {@code null}/empty or
-     * already have a configured target host
-     * @return {@code true} if updated the target host
-     */
-    public boolean updateGlobalHostName(String name) {
-        if (GenericUtils.isEmpty(name) || (GenericUtils.length(getHostName()) > 0)) {
-            return false;
-        }
-
-        setHostName(name);
-        return true;
-    }
-
-    /**
-     * @param portValue The global port value - ignored if not positive
-     * or already have a configured port
-     * @return {@code true} if updated the port value
-     */
-    public boolean updateGlobalPort(int portValue) {
-        if ((portValue <= 0) || (getPort() > 0)) {
-            return false;
-        }
-
-        setPort(portValue);
-        return true;
-    }
-
-    /**
-     * @param identitiesOnly Whether to use only the identities in this entry.
-     * Ignored if already set
-     * @return {@code true} if updated the option value
-     */
-    public boolean updateGlobalIdentityOnly(boolean identitiesOnly) {
-        if (exclusiveIdentites != null) {
-            return false;
-        }
-
-        setIdentitiesOnly(identitiesOnly);
-        return true;
-    }
-
-    /**
-     * @param name Property name - never {@code null}/empty
-     * @param valsList The available values for the property
-     * @param ignoreAlreadyInitialized If {@code false} and one of the &quot;known&quot;
-     * properties is encountered then throws an exception
-     * @throws IllegalArgumentException If an existing value is overwritten and
-     * <tt>ignoreAlreadyInitialized</tt> is {@code false} (except for {@link #IDENTITY_FILE_CONFIG_PROP}
-     * which is <U>cumulative</U>
-     * @see #HOST_NAME_CONFIG_PROP
-     * @see #PORT_CONFIG_PROP
-     * @see #USER_CONFIG_PROP
-     * @see #IDENTITY_FILE_CONFIG_PROP
-     */
-    public void processProperty(String name, Collection<String> valsList, boolean ignoreAlreadyInitialized) {
-        String key = ValidateUtils.checkNotNullAndNotEmpty(name, "No property name");
-        String joinedValue = GenericUtils.join(valsList, ',');
-        appendPropertyValue(key, joinedValue);
-
-        if (HOST_NAME_CONFIG_PROP.equalsIgnoreCase(key)) {
-            ValidateUtils.checkTrue(GenericUtils.size(valsList) == 1, "Multiple target hosts N/A: %s", joinedValue);
-
-            String curValue = getHostName();
-            ValidateUtils.checkTrue(GenericUtils.isEmpty(curValue) || ignoreAlreadyInitialized, "Already initialized %s: %s", key, curValue);
-            setHostName(joinedValue);
-        } else if (PORT_CONFIG_PROP.equalsIgnoreCase(key)) {
-            ValidateUtils.checkTrue(GenericUtils.size(valsList) == 1, "Multiple target ports N/A: %s", joinedValue);
-
-            int curValue = getPort();
-            ValidateUtils.checkTrue((curValue <= 0) || ignoreAlreadyInitialized, "Already initialized %s: %d", key, curValue);
-
-            int newValue = Integer.parseInt(joinedValue);
-            ValidateUtils.checkTrue(newValue > 0, "Bad new port value: %d", newValue);
-            setPort(newValue);
-        } else if (USER_CONFIG_PROP.equalsIgnoreCase(key)) {
-            ValidateUtils.checkTrue(GenericUtils.size(valsList) == 1, "Multiple target users N/A: %s", joinedValue);
-
-            String curValue = getUsername();
-            ValidateUtils.checkTrue(GenericUtils.isEmpty(curValue) || ignoreAlreadyInitialized, "Already initialized %s: %s", key, curValue);
-            setUsername(joinedValue);
-        } else if (IDENTITY_FILE_CONFIG_PROP.equalsIgnoreCase(key)) {
-            ValidateUtils.checkTrue(GenericUtils.size(valsList) > 0, "No identity files specified");
-            for (String id : valsList) {
-                addIdentity(id);
-            }
-        } else if (EXCLUSIVE_IDENTITIES_CONFIG_PROP.equalsIgnoreCase(key)) {
-            setIdentitiesOnly(
-                    SshConfigFileReader.parseBooleanValue(
-                            ValidateUtils.checkNotNullAndNotEmpty(joinedValue, "No identities option value")));
-        }
-    }
-
-    /**
-     * Appends a value using a <U>comma</U> to an existing one. If no previous
-     * value then same as calling {@link #setProperty(String, String)}.
-     *
-     * @param name Property name - never {@code null}/empty
-     * @param value The value to be appended - ignored if {@code null}/empty
-     * @return The value <U>before</U> appending - {@code null} if no previous value
-     */
-    public String appendPropertyValue(String name, String value) {
-        String key = ValidateUtils.checkNotNullAndNotEmpty(name, "No property name");
-        String curVal = getProperty(key);
-        if (GenericUtils.isEmpty(value)) {
-            return curVal;
-        }
-
-        if (GenericUtils.isEmpty(curVal)) {
-            return setProperty(key, value);
-        }
-
-        return setProperty(key, curVal + ',' + value);
-    }
-
-    /**
-     * Sets / Replaces the property value
-     *
-     * @param name Property name - never {@code null}/empty
-     * @param value Property value - if {@code null}/empty then
-     * {@link #removeProperty(String)} is called
-     * @return The previous property value - {@code null} if no such name
-     */
-    public String setProperty(String name, String value) {
-        if (GenericUtils.isEmpty(value)) {
-            return removeProperty(name);
-        }
-
-        String key = ValidateUtils.checkNotNullAndNotEmpty(name, "No property name");
-        if (GenericUtils.isEmpty(properties)) {
-            properties = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
-        }
-
-        return properties.put(key, value);
-    }
-
-    /**
-     * @param name Property name - never {@code null}/empty
-     * @return The removed property value - {@code null} if no such property name
-     */
-    public String removeProperty(String name) {
-        String key = ValidateUtils.checkNotNullAndNotEmpty(name, "No property name");
-        Map<String, String> props = getProperties();
-        if (GenericUtils.isEmpty(props)) {
-            return null;
-        } else {
-            return props.remove(key);
-        }
-    }
-
-    /**
-     * @param properties The properties to set - if {@code null} then an empty
-     * map is effectively set. <B>Note:</B> it is highly recommended to use a
-     * <U>case insensitive</U> key mapper.
-     */
-    public void setProperties(Map<String, String> properties) {
-        this.properties = (properties == null) ? Collections.emptyMap() : properties;
-    }
-
-    public <A extends Appendable> A append(A sb) throws IOException {
-        sb.append(HOST_CONFIG_PROP).append(' ').append(ValidateUtils.checkNotNullAndNotEmpty(getHost(), "No host pattern")).append(IoUtils.EOL);
-        appendNonEmptyProperty(sb, HOST_NAME_CONFIG_PROP, getHostName());
-        appendNonEmptyPort(sb, PORT_CONFIG_PROP, getPort());
-        appendNonEmptyProperty(sb, USER_CONFIG_PROP, getUsername());
-        appendNonEmptyValues(sb, IDENTITY_FILE_CONFIG_PROP, getIdentities());
-        if (exclusiveIdentites != null) {
-            appendNonEmptyProperty(sb, EXCLUSIVE_IDENTITIES_CONFIG_PROP, SshConfigFileReader.yesNoValueOf(exclusiveIdentites));
-        }
-        appendNonEmptyProperties(sb, getProperties());
-        return sb;
-    }
-
-    @Override
-    public String toString() {
-        return getHost() + ": " + getUsername() + "@" + getHostName() + ":" + getPort();
-    }
-
-    /**
-     * @param <A> The {@link Appendable} type
-     * @param sb The target appender
-     * @param name The property name - never {@code null}/empty
-     * @param port The port value - ignored if non-positive
-     * @return The target appender after having appended (or not) the value
-     * @throws IOException If failed to append the requested data
-     * @see #appendNonEmptyProperty(Appendable, String, Object)
-     */
-    public static <A extends Appendable> A appendNonEmptyPort(A sb, String name, int port) throws IOException {
-        return appendNonEmptyProperty(sb, name, (port > 0) ? Integer.toString(port) : null);
-    }
-
-    /**
-     * Appends the extra properties - while skipping the {@link #EXPLICIT_PROPERTIES} ones
-     *
-     * @param <A> The {@link Appendable} type
-     * @param sb The target appender
-     * @param props The {@link Map} of properties - ignored if {@code null}/empty
-     * @return The target appender after having appended (or not) the value
-     * @throws IOException If failed to append the requested data
-     * @see #appendNonEmptyProperty(Appendable, String, Object)
-     */
-    public static <A extends Appendable> A appendNonEmptyProperties(A sb, Map<String, ?> props) throws IOException {
-        if (GenericUtils.isEmpty(props)) {
-            return sb;
-        }
-
-        // Cannot use forEach because of the IOException being thrown by appendNonEmptyProperty
-        for (Map.Entry<String, ?> pe : props.entrySet()) {
-            String name = pe.getKey();
-            if (EXPLICIT_PROPERTIES.contains(name)) {
-                continue;
-            }
-
-            appendNonEmptyProperty(sb, name, pe.getValue());
-        }
-
-        return sb;
-    }
-
-    /**
-     * @param <A> The {@link Appendable} type
-     * @param sb The target appender
-     * @param name The property name - never {@code null}/empty
-     * @param value The property value - ignored if {@code null}. <B>Note:</B>
-     * if the string representation of the value contains any commas, they are
-     * assumed to indicate a multi-valued property which is broken down to
-     * <U>individual</U> lines - one per value.
-     * @return The target appender after having appended (or not) the value
-     * @throws IOException If failed to append the requested data
-     * @see #appendNonEmptyValues(Appendable, String, Object...)
-     */
-    public static <A extends Appendable> A appendNonEmptyProperty(A sb, String name, Object value) throws IOException {
-        String s = Objects.toString(value, null);
-        String[] vals = GenericUtils.split(s, ',');
-        return appendNonEmptyValues(sb, name, (Object[]) vals);
-    }
-
-    /**
-     * @param <A> The {@link Appendable} type
-     * @param sb The target appender
-     * @param name The property name - never {@code null}/empty
-     * @param values The values to be added - one per line - ignored if {@code null}/empty
-     * @return The target appender after having appended (or not) the value
-     * @throws IOException If failed to append the requested data
-     * @see #appendNonEmptyValues(Appendable, String, Collection)
-     */
-    public static <A extends Appendable> A appendNonEmptyValues(A sb, String name, Object... values) throws IOException {
-        return appendNonEmptyValues(sb, name, GenericUtils.isEmpty(values) ? Collections.emptyList() : Arrays.asList(values));
-    }
-
-    /**
-     * @param <A> The {@link Appendable} type
-     * @param sb The target appender
-     * @param name The property name - never {@code null}/empty
-     * @param values The values to be added - one per line - ignored if {@code null}/empty
-     * @return The target appender after having appended (or not) the value
-     * @throws IOException If failed to append the requested data
-     */
-    public static <A extends Appendable> A appendNonEmptyValues(A sb, String name, Collection<?> values) throws IOException {
-        String k = ValidateUtils.checkNotNullAndNotEmpty(name, "No property name");
-        if (GenericUtils.isEmpty(values)) {
-            return sb;
-        }
-
-        for (Object v : values) {
-            sb.append("    ").append(k).append(' ').append(Objects.toString(v)).append(IoUtils.EOL);
-        }
-
-        return sb;
-    }
-
-    /**
-     * @param entries The entries - ignored if {@code null}/empty
-     * @return A {@link HostConfigEntryResolver} wrapper using the entries
-     */
-    public static HostConfigEntryResolver toHostConfigEntryResolver(final Collection<? extends HostConfigEntry> entries) {
-        if (GenericUtils.isEmpty(entries)) {
-            return HostConfigEntryResolver.EMPTY;
-        } else {
-            return (host1, port1, username1) -> {
-                List<HostConfigEntry> matches = findMatchingEntries(host1, entries);
-                int numMatches = GenericUtils.size(matches);
-                if (numMatches <= 0) {
-                    return null;
-                }
-
-                HostConfigEntry match = (numMatches == 1) ? matches.get(0) : findBestMatch(matches);
-                if (match == null) {
-                    ValidateUtils.throwIllegalArgumentException("No best match found for %s@%s:%d out of %d matches", username1, host1, port1, numMatches);
-                }
-
-                return normalizeEntry(match, host1, port1, username1);
-            };
-        }
-    }
-
-    /**
-     * @param entry The original entry - ignored if {@code null}
-     * @param host The original host name / address
-     * @param port The original port
-     * @param username The original user name
-     * @return A <U>cloned</U> entry whose values are resolved - including
-     * expanding macros in the identities files
-     * @throws IOException If failed to normalize the entry
-     * @see #resolveHostName(String)
-     * @see #resolvePort(int)
-     * @see #resolveUsername(String)
-     * @see #resolveIdentityFilePath(String, String, int, String)
-     */
-    public static HostConfigEntry normalizeEntry(HostConfigEntry entry, String host, int port, String username) throws IOException {
-        if (entry == null) {
-            return null;
-        }
-
-        HostConfigEntry normal = new HostConfigEntry();
-        normal.setHost(host);
-        normal.setHostName(entry.resolveHostName(host));
-        normal.setPort(entry.resolvePort(port));
-        normal.setUsername(entry.resolveUsername(username));
-
-        Map<String, String> props = entry.getProperties();
-        if (GenericUtils.size(props) > 0) {
-            normal.setProperties(new TreeMap<>(props));
-        }
-
-        Collection<String> ids = entry.getIdentities();
-        if (GenericUtils.isEmpty(ids)) {
-            return normal;
-        }
-
-        normal.setIdentities(Collections.emptyList());  // start fresh
-        for (String id : ids) {
-            String path = resolveIdentityFilePath(id, host, port, username);
-            normal.addIdentity(path);
-        }
-
-        return normal;
-    }
-
-    /**
-     * Resolves the effective target host
-     *
-     * @param originalName The original requested host
-     * @param entryName The configured host
-     * @return If the configured host entry is not {@code null}/empty
-     * then it is used, otherwise the original one.
-     */
-    public static String resolveHostName(String originalName, String entryName) {
-        if (GenericUtils.isEmpty(entryName)) {
-            return originalName;
-        } else {
-            return entryName;
-        }
-    }
-
-    /**
-     * Resolves the effective username
-     *
-     * @param originalUser The original requested username
-     * @param entryUser The configured host entry username
-     * @return If the configured host entry username is not {@code null}/empty
-     * then it is used, otherwise the original one.
-     */
-    public static String resolveUsername(String originalUser, String entryUser) {
-        if (GenericUtils.isEmpty(entryUser)) {
-            return originalUser;
-        } else {
-            return entryUser;
-        }
-    }
-
-    /**
-     * Resolves the effective port to use
-     *
-     * @param originalPort The original requested port
-     * @param entryPort The configured host entry port
-     * @return If the host entry port is positive, then it is used, otherwise
-     * the original requested port
-     */
-    public static int resolvePort(int originalPort, int entryPort) {
-        if (entryPort <= 0) {
-            return originalPort;
-        } else {
-            return entryPort;
-        }
-    }
-
-    public static List<HostConfigEntry> readHostConfigEntries(File file) throws IOException {
-        return readHostConfigEntries(file.toPath(), IoUtils.EMPTY_OPEN_OPTIONS);
-    }
-
-    public static List<HostConfigEntry> readHostConfigEntries(Path path, OpenOption... options) throws IOException {
-        try (InputStream input = Files.newInputStream(path, options)) {
-            return readHostConfigEntries(input, true);
-        }
-    }
-
-    public static List<HostConfigEntry> readHostConfigEntries(URL url) throws IOException {
-        try (InputStream input = url.openStream()) {
-            return readHostConfigEntries(input, true);
-        }
-    }
-
-    public static List<HostConfigEntry> readHostConfigEntries(String filePath) throws IOException {
-        try (InputStream inStream = new FileInputStream(filePath)) {
-            return readHostConfigEntries(inStream, true);
-        }
-    }
-
-    public static List<HostConfigEntry> readHostConfigEntries(InputStream inStream, boolean okToClose) throws IOException {
-        try (Reader reader = new InputStreamReader(NoCloseInputStream.resolveInputStream(inStream, okToClose), StandardCharsets.UTF_8)) {
-            return readHostConfigEntries(reader, true);
-        }
-    }
-
-    public static List<HostConfigEntry> readHostConfigEntries(Reader rdr, boolean okToClose) throws IOException {
-        try (BufferedReader buf = new BufferedReader(NoCloseReader.resolveReader(rdr, okToClose))) {
-            return readHostConfigEntries(buf);
-        }
-    }
-
-    /**
-     * Reads configuration entries
-     *
-     * @param rdr The {@link BufferedReader} to use
-     * @return The {@link List} of read {@link HostConfigEntry}-ies
-     * @throws IOException If failed to parse the read configuration
-     */
-    public static List<HostConfigEntry> readHostConfigEntries(BufferedReader rdr) throws IOException {
-        HostConfigEntry curEntry = null;
-        HostConfigEntry globalEntry = null;
-        List<HostConfigEntry> entries = null;
-
-        int lineNumber = 1;
-        for (String line = rdr.readLine(); line != null; line = rdr.readLine(), lineNumber++) {
-            line = GenericUtils.replaceWhitespaceAndTrim(line);
-            if (GenericUtils.isEmpty(line)) {
-                continue;
-            }
-
-            int pos = line.indexOf(SshConfigFileReader.COMMENT_CHAR);
-            if (pos == 0) {
-                continue;
-            }
-
-            if (pos > 0) {
-                line = line.substring(0, pos);
-                line = line.trim();
-            }
-
-            /*
-             * Some options use '=', others use ' ' - try both
-             * NOTE: we do not validate the format for each option separately
-             */
-            pos = line.indexOf(' ');
-            if (pos < 0) {
-                pos = line.indexOf('=');
-            }
-
-            if (pos < 0) {
-                throw new StreamCorruptedException("No configuration value delimiter at line " + lineNumber + ": " + line);
-            }
-
-            String key = line.substring(0, pos);
-            String value = line.substring(pos + 1);
-            List<String> valsList = parseConfigValue(value);
-
-            if (HOST_CONFIG_PROP.equalsIgnoreCase(key)) {
-                if (GenericUtils.isEmpty(valsList)) {
-                    throw new StreamCorruptedException("Missing host pattern(s) at line " + lineNumber + ": " + line);
-                }
-
-                // If the all-hosts pattern is used, make sure no global section already active
-                for (String name : valsList) {
-                    if (ALL_HOSTS_PATTERN.equalsIgnoreCase(name) && (globalEntry != null)) {
-                        throw new StreamCorruptedException("Overriding the global section with a specific one at line " + lineNumber + ": " + line);
-                    }
-                }
-
-                if (curEntry != null) {
-                    curEntry.processGlobalValues(globalEntry);
-                }
-
-                entries = updateEntriesList(entries, curEntry);
-
-                curEntry = new HostConfigEntry();
-                curEntry.setHost(valsList);
-            } else if (curEntry == null) {
-                // if 1st encountered property is NOT for a specific host, then configuration applies to ALL
-                curEntry = new HostConfigEntry();
-                curEntry.setHost(Collections.singletonList(ALL_HOSTS_PATTERN));
-                globalEntry = curEntry;
-            }
-
-            try {
-                curEntry.processProperty(key, valsList, false);
-            } catch (RuntimeException e) {
-                throw new StreamCorruptedException("Failed (" + e.getClass().getSimpleName() + ")"
-                                                 + " to process line #" + lineNumber + " (" + line + ")"
-                                                 + ": " + e.getMessage());
-            }
-        }
-
-        if (curEntry != null) {
-            curEntry.processGlobalValues(globalEntry);
-        }
-
-        entries = updateEntriesList(entries, curEntry);
-        if (entries == null) {
-            return Collections.emptyList();
-        } else {
-            return entries;
-        }
-    }
-
-    /**
-     * Finds the best match out of the given ones.
-     *
-     * @param matches The available matches - ignored if {@code null}/empty
-     * @return The best match or {@code null} if no matches or no best match found
-     * @see #findBestMatch(Iterator)
-     */
-    public static HostConfigEntry findBestMatch(Collection<? extends HostConfigEntry> matches) {
-        if (GenericUtils.isEmpty(matches)) {
-            return null;
-        } else {
-            return findBestMatch(matches.iterator());
-        }
-    }
-
-    /**
-     * Finds the best match out of the given ones.
-     *
-     * @param matches The available matches - ignored if {@code null}/empty
-     * @return The best match or {@code null} if no matches or no best match found
-     * @see #findBestMatch(Iterator)
-     */
-    public static HostConfigEntry findBestMatch(Iterable<? extends HostConfigEntry> matches) {
-        if (matches == null) {
-            return null;
-        } else {
-            return findBestMatch(matches.iterator());
-        }
-    }
-
-    /**
-     * Finds the best match out of the given ones. The best match is defined as one whose
-     * pattern is as <U>specific</U> as possible (if more than one match is available).
-     * I.e., a non-global match is preferred over global one, and a match with no wildcards
-     * is preferred over one with such a pattern.
-     *
-     * @param matches The available matches - ignored if {@code null}/empty
-     * @return The best match or {@code null} if no matches or no best match found
-     * @see #isSpecificHostPattern(String)
-     */
-    public static HostConfigEntry findBestMatch(Iterator<? extends HostConfigEntry> matches) {
-        if ((matches == null) || (!matches.hasNext())) {
-            return null;
-        }
-
-        HostConfigEntry candidate = matches.next();
-        int wildcardMatches = 0;
-        while (matches.hasNext()) {
-            HostConfigEntry entry = matches.next();
-            String entryPattern = entry.getHost();
-            String candidatePattern = candidate.getHost();
-            // prefer non-global entry over global entry
-            if (ALL_HOSTS_PATTERN.equalsIgnoreCase(candidatePattern)) {
-                // unlikely, but handle it
-                if (ALL_HOSTS_PATTERN.equalsIgnoreCase(entryPattern)) {
-                    wildcardMatches++;
-                } else {
-                    candidate = entry;
-                    wildcardMatches = 0;
-                }
-                continue;
-            }
-
-            if (isSpecificHostPattern(entryPattern)) {
-                // if both are specific then no best match
-                if (isSpecificHostPattern(candidatePattern)) {
-                    return null;
-                }
-
-                candidate = entry;
-                wildcardMatches = 0;
-                continue;
-            }
-
-            wildcardMatches++;
-        }
-
-        String candidatePattern = candidate.getHost();
-        // best match either has specific host or no wildcard matches
-        if ((wildcardMatches <= 0) || (isSpecificHostPattern(candidatePattern))) {
-            return candidate;
-        }
-
-        return null;
-    }
-
-    public static List<HostConfigEntry> updateEntriesList(List<HostConfigEntry> entries, HostConfigEntry curEntry) {
-        if (curEntry == null) {
-            return entries;
-        }
-
-        if (entries == null) {
-            entries = new ArrayList<>();
-        }
-
-        entries.add(curEntry);
-        return entries;
-    }
-
-    public static void writeHostConfigEntries(File file, Collection<? extends HostConfigEntry> entries) throws IOException {
-        writeHostConfigEntries(Objects.requireNonNull(file, "No file").toPath(), entries, IoUtils.EMPTY_OPEN_OPTIONS);
-    }
-
-    public static void writeHostConfigEntries(Path path, Collection<? extends HostConfigEntry> entries, OpenOption... options) throws IOException {
-        try (OutputStream outputStream = Files.newOutputStream(path, options)) {
-            writeHostConfigEntries(outputStream, true, entries);
-        }
-    }
-
-    public static void writeHostConfigEntries(OutputStream outputStream, boolean okToClose, Collection<? extends HostConfigEntry> entries) throws IOException {
-        if (GenericUtils.isEmpty(entries)) {
-            return;
-        }
-
-        try (Writer w = new OutputStreamWriter(NoCloseOutputStream.resolveOutputStream(outputStream, okToClose), StandardCharsets.UTF_8)) {
-            appendHostConfigEntries(w, entries);
-        }
-    }
-
-    public static <A extends Appendable> A appendHostConfigEntries(A sb, Collection<? extends HostConfigEntry> entries) throws IOException {
-        if (GenericUtils.isEmpty(entries)) {
-            return sb;
-        }
-
-        for (HostConfigEntry entry : entries) {
-            entry.append(sb);
-        }
-
-        return sb;
-    }
-
-    /**
-     * Checks if this is a multi-value - allow space and comma
-     *
-     * @param value The value - ignored if {@code null}/empty (after trimming)
-     * @return A {@link List} of the encountered values
-     */
-    public static List<String> parseConfigValue(String value) {
-        String s = GenericUtils.replaceWhitespaceAndTrim(value);
-        if (GenericUtils.isEmpty(s)) {
-            return Collections.emptyList();
-        }
-
-        for (int index = 0; index < MULTI_VALUE_SEPARATORS.length(); index++) {
-            char sep = MULTI_VALUE_SEPARATORS.charAt(index);
-            int pos = s.indexOf(sep);
-            if (pos >= 0) {
-                String[] vals = GenericUtils.split(s, sep);
-                if (GenericUtils.isEmpty(vals)) {
-                    return Collections.emptyList();
-                } else {
-                    return Arrays.asList(vals);
-                }
-            }
-        }
-
-        // this point is reached if no separators found
-        return Collections.singletonList(s);
-    }
-
-    // The file name may use the tilde syntax to refer to a user’s home directory or one of the following escape characters:
-    // '%d' (local user's home directory), '%u' (local user name), '%l' (local host name), '%h' (remote host name) or '%r' (remote user name).
-    public static String resolveIdentityFilePath(String id, String host, int port, String username) throws IOException {
-        if (GenericUtils.isEmpty(id)) {
-            return id;
-        }
-
-        String path = id.replace('/', File.separatorChar);  // make sure all separators are local
-        String[] elements = GenericUtils.split(path, File.separatorChar);
-        StringBuilder sb = new StringBuilder(path.length() + Long.SIZE);
-        for (int index = 0; index < elements.length; index++) {
-            String elem = elements[index];
-            if (index > 0) {
-                sb.append(File.separatorChar);
-            }
-
-            for (int curPos = 0; curPos < elem.length(); curPos++) {
-                char ch = elem.charAt(curPos);
-                if (ch == HOME_TILDE_CHAR) {
-                    ValidateUtils.checkTrue((curPos == 0) && (index == 0), "Home tilde must be first: %s", id);
-                    appendUserHome(sb);
-                } else if (ch == PATH_MACRO_CHAR) {
-                    curPos++;
-                    ValidateUtils.checkTrue(curPos < elem.length(), "Missing macro modifier in %s", id);
-                    ch = elem.charAt(curPos);
-                    switch(ch) {
-                        case PATH_MACRO_CHAR:
-                            sb.append(ch);
-                            break;
-                        case LOCAL_HOME_MACRO:
-                            ValidateUtils.checkTrue((curPos == 1) && (index == 0), "Home macro must be first: %s", id);
-                            appendUserHome(sb);
-                            break;
-                        case LOCAL_USER_MACRO:
-                            sb.append(ValidateUtils.checkNotNullAndNotEmpty(OsUtils.getCurrentUser(), "No local user name value"));
-                            break;
-                        case LOCAL_HOST_MACRO: {
-                            InetAddress address = Objects.requireNonNull(InetAddress.getLocalHost(), "No local address");
-                            sb.append(ValidateUtils.checkNotNullAndNotEmpty(address.getHostName(), "No local name"));
-                            break;
-                        }
-                        case REMOTE_HOST_MACRO:
-                            sb.append(ValidateUtils.checkNotNullAndNotEmpty(host, "No remote host provided"));
-                            break;
-                        case REMOTE_USER_MACRO:
-                            sb.append(ValidateUtils.checkNotNullAndNotEmpty(username, "No remote user provided"));
-                            break;
-                        case REMOTE_PORT_MACRO:
-                            ValidateUtils.checkTrue(port > 0, "Bad remote port value: %d", port);
-                            sb.append(port);
-                            break;
-                        default:
-                            ValidateUtils.throwIllegalArgumentException("Bad modifier '%s' in %s", String.valueOf(ch), id);
-                    }
-                } else {
-                    sb.append(ch);
-                }
-            }
-        }
-
-        return sb.toString();
-    }
-
-    public static StringBuilder appendUserHome(StringBuilder sb) {
-        return appendUserHome(sb, IdentityUtils.getUserHomeFolder());
-    }
-
-    public static StringBuilder appendUserHome(StringBuilder sb, Path userHome) {
-        return appendUserHome(sb, Objects.requireNonNull(userHome, "No user home folder").toString());
-    }
-
-    public static StringBuilder appendUserHome(StringBuilder sb, String userHome) {
-        if (GenericUtils.isEmpty(userHome)) {
-            return sb;
-        }
-
-        sb.append(userHome);
-        // strip any ending separator since we add our own
-        int len = sb.length();
-        if (sb.charAt(len - 1) == File.separatorChar) {
-            sb.setLength(len - 1);
-        }
-
-        return sb;
-    }
-
-    /**
-     * @return The default {@link Path} location of the OpenSSH hosts entries configuration file
-     */
-    @SuppressWarnings("synthetic-access")
-    public static Path getDefaultHostConfigFile() {
-        return LazyDefaultConfigFileHolder.CONFIG_FILE;
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntryResolver.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntryResolver.java b/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntryResolver.java
deleted file mode 100644
index a07cfcf..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntryResolver.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.client.config.hosts;
-
-import java.io.IOException;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FunctionalInterface
-public interface HostConfigEntryResolver {
-
-    /**
-     * An &quot;empty&quot; implementation that does not resolve any entry
-     */
-    HostConfigEntryResolver EMPTY = new HostConfigEntryResolver() {
-        @Override
-        public HostConfigEntry resolveEffectiveHost(String host, int port, String username) throws IOException {
-            return null;
-        }
-
-        @Override
-        public String toString() {
-            return "EMPTY";
-        }
-    };
-
-    /**
-     * Invoked when creating a new client session in order to allow for overriding
-     * of the original parameters
-     *
-     * @param host The requested host - never {@code null}/empty
-     * @param port The requested port
-     * @param username The requested username
-     * @return A {@link HostConfigEntry} for the actual target - {@code null} if use
-     * original parameters. <B>Note:</B> if any identity files are attached to the
-     * configuration then they must point to <U>existing</U> locations. This means
-     * that any macros such as <code>~, %d, %h</code>, etc. must be resolved <U>prior</U>
-     * to returning the value
-     * @throws IOException If failed to resolve the configuration
-     */
-    HostConfigEntry resolveEffectiveHost(String host, int port, String username) throws IOException;
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostPatternValue.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostPatternValue.java b/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostPatternValue.java
deleted file mode 100644
index 20d682f..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostPatternValue.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.client.config.hosts;
-
-import java.util.regex.Pattern;
-
-import org.apache.sshd.common.util.GenericUtils;
-
-/**
- * Represents a pattern definition in the <U>known_hosts</U> file
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- * @see <A HREF="https://en.wikibooks.org/wiki/OpenSSH/Client_Configuration_Files#About_the_Contents_of_the_known_hosts_Files">
- * OpenSSH cookbook - About the Contents of the known hosts Files</A>
- */
-public class HostPatternValue {
-    private Pattern pattern;
-    private int port;
-    private boolean negated;
-
-    public HostPatternValue() {
-        super();
-    }
-
-    public HostPatternValue(Pattern pattern, boolean negated) {
-        this(pattern, 0, negated);
-    }
-
-    public HostPatternValue(Pattern pattern, int port, boolean negated) {
-        this.pattern = pattern;
-        this.port = port;
-        this.negated = negated;
-    }
-
-    public Pattern getPattern() {
-        return pattern;
-    }
-
-    public void setPattern(Pattern pattern) {
-        this.pattern = pattern;
-    }
-
-    public int getPort() {
-        return port;
-    }
-
-    public void setPort(int port) {
-        this.port = port;
-    }
-
-    public boolean isNegated() {
-        return negated;
-    }
-
-    public void setNegated(boolean negated) {
-        this.negated = negated;
-    }
-
-    @Override
-    public String toString() {
-        Pattern p = getPattern();
-        String purePattern = (p == null) ? null : p.pattern();
-        StringBuilder sb = new StringBuilder(GenericUtils.length(purePattern) + Short.SIZE);
-        if (isNegated()) {
-            sb.append(HostPatternsHolder.NEGATION_CHAR_PATTERN);
-        }
-
-        int portValue = getPort();
-        if (portValue > 0) {
-            sb.append(HostPatternsHolder.NON_STANDARD_PORT_PATTERN_ENCLOSURE_START_DELIM);
-        }
-        sb.append(purePattern);
-        if (portValue > 0) {
-            sb.append(HostPatternsHolder.NON_STANDARD_PORT_PATTERN_ENCLOSURE_END_DELIM);
-            sb.append(HostPatternsHolder.PORT_VALUE_DELIMITER);
-            sb.append(portValue);
-        }
-
-        return sb.toString();
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostPatternsHolder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostPatternsHolder.java b/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostPatternsHolder.java
deleted file mode 100644
index 9d90dac..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostPatternsHolder.java
+++ /dev/null
@@ -1,343 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.client.config.hosts;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Objects;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class HostPatternsHolder {
-
-    /**
-     * Used in a host pattern to denote zero or more consecutive characters
-     */
-    public static final char WILDCARD_PATTERN = '*';
-    public static final String ALL_HOSTS_PATTERN = String.valueOf(WILDCARD_PATTERN);
-
-    /**
-     * Used in a host pattern to denote any <U>one</U> character
-     */
-    public static final char SINGLE_CHAR_PATTERN = '?';
-
-    /**
-     * Used to negate a host pattern
-     */
-    public static final char NEGATION_CHAR_PATTERN = '!';
-
-    /**
-     * The available pattern characters
-     */
-    public static final String PATTERN_CHARS = new String(new char[]{WILDCARD_PATTERN, SINGLE_CHAR_PATTERN, NEGATION_CHAR_PATTERN});
-
-    /** Port value separator if non-standard port pattern used */
-    public static final char PORT_VALUE_DELIMITER = ':';
-
-    /** Non-standard port specification host pattern enclosure start delimiter */
-    public static final char NON_STANDARD_PORT_PATTERN_ENCLOSURE_START_DELIM = '[';
-
-    /** Non-standard port specification host pattern enclosure end delimiter */
-    public static final char NON_STANDARD_PORT_PATTERN_ENCLOSURE_END_DELIM = ']';
-
-    private Collection<HostPatternValue> patterns = new LinkedList<>();
-
-    protected HostPatternsHolder() {
-        super();
-    }
-
-    public Collection<HostPatternValue> getPatterns() {
-        return patterns;
-    }
-
-    public void setPatterns(Collection<HostPatternValue> patterns) {
-        this.patterns = patterns;
-    }
-
-    /**
-     * Checks if a given host name / address matches the entry's host pattern(s)
-     *
-     * @param host The host name / address - ignored if {@code null}/empty
-     * @param port The connection port
-     * @return {@code true} if the name / address matches the pattern(s)
-     * @see #isHostMatch(String, Pattern)
-     */
-    public boolean isHostMatch(String host, int port) {
-        return isHostMatch(host, port, getPatterns());
-    }
-
-    /**
-     * @param pattern The pattern to check - ignored if {@code null}/empty
-     * @return {@code true} if the pattern is not empty and contains no wildcard characters
-     * @see #WILDCARD_PATTERN
-     * @see #SINGLE_CHAR_PATTERN
-     * @see #SINGLE_CHAR_PATTERN
-     */
-    public static boolean isSpecificHostPattern(String pattern) {
-        if (GenericUtils.isEmpty(pattern)) {
-            return false;
-        }
-
-        for (int index = 0; index < PATTERN_CHARS.length(); index++) {
-            char ch = PATTERN_CHARS.charAt(index);
-            if (pattern.indexOf(ch) >= 0) {
-                return false;
-            }
-        }
-
-        return true;
-    }
-
-    /**
-     * Locates all the matching entries for a give host name / address
-     *
-     * @param host The host name / address - ignored if {@code null}/empty
-     * @param entries The {@link HostConfigEntry}-ies to scan - ignored if {@code null}/empty
-     * @return A {@link List} of all the matching entries
-     * @see #isHostMatch(String, int)
-     */
-    public static List<HostConfigEntry> findMatchingEntries(String host, HostConfigEntry... entries) {
-        // TODO in Java-8 use Stream(s) + predicate
-        if (GenericUtils.isEmpty(host) || GenericUtils.isEmpty(entries)) {
-            return Collections.emptyList();
-        } else {
-            return findMatchingEntries(host, Arrays.asList(entries));
-        }
-    }
-
-    /**
-     * Locates all the matching entries for a give host name / address
-     *
-     * @param host The host name / address - ignored if {@code null}/empty
-     * @param entries The {@link HostConfigEntry}-ies to scan - ignored if {@code null}/empty
-     * @return A {@link List} of all the matching entries
-     * @see #isHostMatch(String, int)
-     */
-    public static List<HostConfigEntry> findMatchingEntries(String host, Collection<? extends HostConfigEntry> entries) {
-        // TODO in Java-8 use Stream(s) + predicate
-        if (GenericUtils.isEmpty(host) || GenericUtils.isEmpty(entries)) {
-            return Collections.emptyList();
-        }
-
-        List<HostConfigEntry> matches = null;
-        for (HostConfigEntry entry : entries) {
-            if (!entry.isHostMatch(host, 0 /* any port */)) {
-                continue;   // debug breakpoint
-            }
-
-            if (matches == null) {
-                matches = new ArrayList<>(entries.size());  // in case ALL of them match
-            }
-
-            matches.add(entry);
-        }
-
-        if (matches == null) {
-            return Collections.emptyList();
-        } else {
-            return matches;
-        }
-    }
-
-    public static boolean isHostMatch(String host, int port, Collection<HostPatternValue> patterns) {
-        if (GenericUtils.isEmpty(patterns)) {
-            return false;
-        }
-
-        boolean matchFound = false;
-        for (HostPatternValue pv : patterns) {
-            boolean negated = pv.isNegated();
-            /*
-             * If already found a match we are interested only in negations
-             */
-            if (matchFound && (!negated)) {
-                continue;
-            }
-
-            if (!isHostMatch(host, pv.getPattern())) {
-                continue;
-            }
-
-            /*
-             * According to https://www.freebsd.org/cgi/man.cgi?query=ssh_config&sektion=5:
-             *
-             *      If a negated entry is matched, then the Host entry is ignored,
-             *      regardless of whether any other patterns on the line match.
-             */
-            if (negated) {
-                return false;
-            }
-
-            int pvPort = pv.getPort();
-            if ((pvPort != 0) && (port != 0) && (pvPort != port)) {
-                continue;
-            }
-
-            matchFound = true;
-        }
-
-        return matchFound;
-    }
-
-    /**
-     * Checks if a given host name / address matches a host pattern
-     *
-     * @param host The host name / address - ignored if {@code null}/empty
-     * @param pattern The host {@link Pattern} - ignored if {@code null}
-     * @return {@code true} if the name / address matches the pattern
-     */
-    public static boolean isHostMatch(String host, Pattern pattern) {
-        if (GenericUtils.isEmpty(host) || (pattern == null)) {
-            return false;
-        }
-
-        Matcher m = pattern.matcher(host);
-        return m.matches();
-    }
-
-    public static List<HostPatternValue> parsePatterns(CharSequence... patterns) {
-        return parsePatterns(GenericUtils.isEmpty(patterns) ? Collections.emptyList() : Arrays.asList(patterns));
-    }
-
-    public static List<HostPatternValue> parsePatterns(Collection<? extends CharSequence> patterns) {
-        if (GenericUtils.isEmpty(patterns)) {
-            return Collections.emptyList();
-        }
-
-        List<HostPatternValue> result = new ArrayList<>(patterns.size());
-        for (CharSequence p : patterns) {
-            result.add(ValidateUtils.checkNotNull(toPattern(p), "No pattern for %s", p));
-        }
-
-        return result;
-    }
-
-    /**
-     * Converts a host pattern string to a regular expression matcher.
-     * <B>Note:</B> pattern matching is <U>case insensitive</U>
-     *
-     * @param patternString The original pattern string - ignored if {@code null}/empty
-     * @return The regular expression matcher {@link Pattern} and the indication
-     * whether it is a negating pattern or not - {@code null} if no original string
-     * @see #NON_STANDARD_PORT_PATTERN_ENCLOSURE_START_DELIM
-     * @see #NON_STANDARD_PORT_PATTERN_ENCLOSURE_END_DELIM
-     * @see #WILDCARD_PATTERN
-     * @see #SINGLE_CHAR_PATTERN
-     * @see #NEGATION_CHAR_PATTERN
-     */
-    public static HostPatternValue toPattern(CharSequence patternString) {
-        String pattern = GenericUtils.replaceWhitespaceAndTrim(Objects.toString(patternString, null));
-        if (GenericUtils.isEmpty(pattern)) {
-            return null;
-        }
-
-        int patternLen = pattern.length();
-        int port = 0;
-        // Check if non-standard port value used
-        StringBuilder sb = new StringBuilder(patternLen);
-        if (pattern.charAt(0) == HostPatternsHolder.NON_STANDARD_PORT_PATTERN_ENCLOSURE_START_DELIM) {
-            int pos = GenericUtils.lastIndexOf(pattern, HostPatternsHolder.PORT_VALUE_DELIMITER);
-            ValidateUtils.checkTrue(pos > 0, "Missing non-standard port value delimiter in %s", pattern);
-            ValidateUtils.checkTrue(pos < (patternLen - 1), "Missing non-standard port value number in %s", pattern);
-            ValidateUtils.checkTrue(pattern.charAt(pos - 1) == HostPatternsHolder.NON_STANDARD_PORT_PATTERN_ENCLOSURE_END_DELIM,
-                "Invalid non-standard port value host pattern enclosure delimiters in %s", pattern);
-
-            String csPort = pattern.substring(pos + 1, patternLen);
-            port = Integer.parseInt(csPort);
-            ValidateUtils.checkTrue((port > 0) && (port <= 0xFFFF), "Invalid non-start port value (%d) in %s", port, pattern);
-
-            pattern = pattern.substring(1, pos - 1);
-            patternLen = pattern.length();
-        }
-
-        boolean negated = false;
-        for (int curPos = 0; curPos < patternLen; curPos++) {
-            char ch = pattern.charAt(curPos);
-            ValidateUtils.checkTrue(isValidPatternChar(ch), "Invalid host pattern char in %s", pattern);
-
-            switch(ch) {
-                case '.':   // need to escape it
-                    sb.append('\\').append(ch);
-                    break;
-                case SINGLE_CHAR_PATTERN:
-                    sb.append('.');
-                    break;
-                case WILDCARD_PATTERN:
-                    sb.append(".*");
-                    break;
-                case NEGATION_CHAR_PATTERN:
-                    ValidateUtils.checkTrue(!negated, "Double negation in %s", pattern);
-                    ValidateUtils.checkTrue(curPos == 0, "Negation must be 1st char: %s", pattern);
-                    negated = true;
-                    break;
-                default:
-                    sb.append(ch);
-            }
-        }
-
-        return new HostPatternValue(Pattern.compile(sb.toString(), Pattern.CASE_INSENSITIVE), port, negated);
-    }
-
-    /**
-     * Checks if the given character is valid for a host pattern. Valid
-     * characters are:
-     * <UL>
-     *      <LI>A-Z</LI>
-     *      <LI>a-z</LI>
-     *      <LI>0-9</LI>
-     *      <LI>Underscore (_)</LI>
-     *      <LI>Hyphen (-)</LI>
-     *      <LI>Dot (.)</LI>
-     *      <LI>The {@link #WILDCARD_PATTERN}</LI>
-     *      <LI>The {@link #SINGLE_CHAR_PATTERN}</LI>
-     * </UL>
-     *
-     * @param ch The character to validate
-     * @return {@code true} if valid pattern character
-     */
-    public static boolean isValidPatternChar(char ch) {
-        if ((ch <= ' ') || (ch >= 0x7E)) {
-            return false;
-        }
-        if ((ch >= 'a') && (ch <= 'z')) {
-            return true;
-        }
-        if ((ch >= 'A') && (ch <= 'Z')) {
-            return true;
-        }
-        if ((ch >= '0') && (ch <= '9')) {
-            return true;
-        }
-        if ("-_.".indexOf(ch) >= 0) {
-            return true;
-        }
-        return PATTERN_CHARS.indexOf(ch) >= 0;
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/KnownHostDigest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/KnownHostDigest.java b/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/KnownHostDigest.java
deleted file mode 100644
index 2d9a322..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/KnownHostDigest.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.client.config.hosts;
-
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.Objects;
-import java.util.Set;
-
-import org.apache.sshd.common.Factory;
-import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.NamedResource;
-import org.apache.sshd.common.mac.BuiltinMacs;
-import org.apache.sshd.common.mac.Mac;
-import org.apache.sshd.common.util.ValidateUtils;
-
-/**
- * Available digesters for known hosts entries
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public enum KnownHostDigest implements NamedFactory<Mac> {
-    SHA1("1", BuiltinMacs.hmacsha1);
-
-    public static final Set<KnownHostDigest> VALUES =
-            Collections.unmodifiableSet(EnumSet.allOf(KnownHostDigest.class));
-
-    private final String name;
-    private final Factory<Mac> factory;
-
-    KnownHostDigest(String name, Factory<Mac> factory) {
-        this.name = ValidateUtils.checkNotNullAndNotEmpty(name, "No name");
-        this.factory = Objects.requireNonNull(factory, "No factory");
-    }
-
-    @Override
-    public String getName() {
-        return name;
-    }
-
-    @Override
-    public Mac create() {
-        return factory.create();
-    }
-
-    public static KnownHostDigest fromName(String name) {
-        return NamedResource.findByName(name, String.CASE_INSENSITIVE_ORDER, VALUES);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/KnownHostEntry.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/KnownHostEntry.java b/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/KnownHostEntry.java
deleted file mode 100644
index 4d4e97a..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/KnownHostEntry.java
+++ /dev/null
@@ -1,276 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.client.config.hosts;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.io.StreamCorruptedException;
-import java.net.URL;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.OpenOption;
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-import org.apache.sshd.common.config.SshConfigFileReader;
-import org.apache.sshd.common.config.keys.AuthorizedKeyEntry;
-import org.apache.sshd.common.config.keys.PublicKeyEntry;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.common.util.io.NoCloseInputStream;
-import org.apache.sshd.common.util.io.NoCloseReader;
-
-/**
- * Contains a representation of an entry in the <code>known_hosts</code> file
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- * @see <A HREF="http://www.manpagez.com/man/8/sshd/">sshd(8) man page</A>
- */
-public class KnownHostEntry extends HostPatternsHolder {
-    /**
-     * Character that denotes that start of a marker
-     */
-    public static final char MARKER_INDICATOR = '@';
-
-    /**
-     * Standard OpenSSH config file name
-     */
-    public static final String STD_HOSTS_FILENAME = "known_hosts";
-
-    private static final class LazyDefaultConfigFileHolder {
-        private static final Path HOSTS_FILE =
-            PublicKeyEntry.getDefaultKeysFolderPath().resolve(STD_HOSTS_FILENAME);
-
-        private LazyDefaultConfigFileHolder() {
-            throw new UnsupportedOperationException("No instance allowed");
-        }
-    }
-
-    private String line;
-    private String marker;
-    private AuthorizedKeyEntry keyEntry;
-    private KnownHostHashValue hashedEntry;
-
-    public KnownHostEntry() {
-        super();
-    }
-
-    /**
-     * @param line The original line from which this entry was created
-     */
-    public KnownHostEntry(String line) {
-        this.line = line;
-    }
-
-    /**
-     * @return The original line from which this entry was created
-     */
-    public String getConfigLine() {
-        return line;
-    }
-
-    public void setConfigLine(String line) {
-        this.line = line;
-    }
-
-    public String getMarker() {
-        return marker;
-    }
-
-    public void setMarker(String marker) {
-        this.marker = marker;
-    }
-
-    public AuthorizedKeyEntry getKeyEntry() {
-        return keyEntry;
-    }
-
-    public void setKeyEntry(AuthorizedKeyEntry keyEntry) {
-        this.keyEntry = keyEntry;
-    }
-
-    public KnownHostHashValue getHashedEntry() {
-        return hashedEntry;
-    }
-
-    public void setHashedEntry(KnownHostHashValue hashedEntry) {
-        this.hashedEntry = hashedEntry;
-    }
-
-    @Override
-    public boolean isHostMatch(String host, int port) {
-        if (super.isHostMatch(host, port)) {
-            return true;
-        }
-
-        KnownHostHashValue hash = getHashedEntry();
-        return (hash != null) && hash.isHostMatch(host);
-    }
-
-    @Override
-    public String toString() {
-        return getConfigLine();
-    }
-
-    /**
-     * @return The default {@link Path} location of the OpenSSH known hosts file
-     */
-    @SuppressWarnings("synthetic-access")
-    public static Path getDefaultKnownHostsFile() {
-        return LazyDefaultConfigFileHolder.HOSTS_FILE;
-    }
-
-    public static List<KnownHostEntry> readKnownHostEntries(File file) throws IOException {
-        return readKnownHostEntries(file.toPath(), IoUtils.EMPTY_OPEN_OPTIONS);
-    }
-
-    public static List<KnownHostEntry> readKnownHostEntries(Path path, OpenOption... options) throws IOException {
-        try (InputStream input = Files.newInputStream(path, options)) {
-            return readKnownHostEntries(input, true);
-        }
-    }
-
-    public static List<KnownHostEntry> readKnownHostEntries(URL url) throws IOException {
-        try (InputStream input = url.openStream()) {
-            return readKnownHostEntries(input, true);
-        }
-    }
-
-    public static List<KnownHostEntry> readKnownHostEntries(String filePath) throws IOException {
-        try (InputStream inStream = new FileInputStream(filePath)) {
-            return readKnownHostEntries(inStream, true);
-        }
-    }
-
-    public static List<KnownHostEntry> readKnownHostEntries(InputStream inStream, boolean okToClose) throws IOException {
-        try (Reader reader = new InputStreamReader(NoCloseInputStream.resolveInputStream(inStream, okToClose), StandardCharsets.UTF_8)) {
-            return readKnownHostEntries(reader, true);
-        }
-    }
-
-    public static List<KnownHostEntry> readKnownHostEntries(Reader rdr, boolean okToClose) throws IOException {
-        try (BufferedReader buf = new BufferedReader(NoCloseReader.resolveReader(rdr, okToClose))) {
-            return readKnownHostEntries(buf);
-        }
-    }
-
-    /**
-     * Reads configuration entries
-     *
-     * @param rdr The {@link BufferedReader} to use
-     * @return The {@link List} of read {@link KnownHostEntry}-ies
-     * @throws IOException If failed to parse the read configuration
-     */
-    public static List<KnownHostEntry> readKnownHostEntries(BufferedReader rdr) throws IOException {
-        List<KnownHostEntry> entries = null;
-
-        int lineNumber = 1;
-        for (String line = rdr.readLine(); line != null; line = rdr.readLine(), lineNumber++) {
-            line = GenericUtils.trimToEmpty(line);
-            if (GenericUtils.isEmpty(line)) {
-                continue;
-            }
-
-            int pos = line.indexOf(SshConfigFileReader.COMMENT_CHAR);
-            if (pos == 0) {
-                continue;
-            }
-
-            if (pos > 0) {
-                line = line.substring(0, pos);
-                line = line.trim();
-            }
-
-            try {
-                KnownHostEntry entry = parseKnownHostEntry(line);
-                if (entry == null) {
-                    continue;
-                }
-
-                if (entries == null) {
-                    entries = new ArrayList<>();
-                }
-                entries.add(entry);
-            } catch (RuntimeException | Error e) {   // TODO consider consulting a user callback
-                throw new StreamCorruptedException("Failed (" + e.getClass().getSimpleName() + ")"
-                        + " to parse line #" + lineNumber + " '" + line + "': " + e.getMessage());
-            }
-        }
-
-        if (entries == null) {
-            return Collections.emptyList();
-        } else {
-            return entries;
-        }
-    }
-
-    public static KnownHostEntry parseKnownHostEntry(String line) {
-        return parseKnownHostEntry(GenericUtils.isEmpty(line) ? null : new KnownHostEntry(), line);
-    }
-
-    public static <E extends KnownHostEntry> E parseKnownHostEntry(E entry, String data) {
-        String line = GenericUtils.replaceWhitespaceAndTrim(data);
-        if (GenericUtils.isEmpty(line) || (line.charAt(0) == PublicKeyEntry.COMMENT_CHAR)) {
-            return entry;
-        }
-
-        entry.setConfigLine(line);
-
-        if (line.charAt(0) == MARKER_INDICATOR) {
-            int pos = line.indexOf(' ');
-            ValidateUtils.checkTrue(pos > 0, "Missing marker name end delimiter in line=%s", data);
-            ValidateUtils.checkTrue(pos > 1, "No marker name after indicator in line=%s", data);
-            entry.setMarker(line.substring(1, pos));
-            line = line.substring(pos + 1).trim();
-        } else {
-            entry.setMarker(null);
-        }
-
-        int pos = line.indexOf(' ');
-        ValidateUtils.checkTrue(pos > 0, "Missing host patterns end delimiter in line=%s", data);
-        String hostPattern = line.substring(0, pos);
-        line = line.substring(pos + 1).trim();
-
-        if (hostPattern.charAt(0) == KnownHostHashValue.HASHED_HOST_DELIMITER) {
-            KnownHostHashValue hash =
-                ValidateUtils.checkNotNull(KnownHostHashValue.parse(hostPattern),
-                    "Failed to extract host hash value from line=%s", data);
-            entry.setHashedEntry(hash);
-            entry.setPatterns(null);
-        } else {
-            entry.setHashedEntry(null);
-            entry.setPatterns(parsePatterns(GenericUtils.split(hostPattern, ',')));
-        }
-
-        AuthorizedKeyEntry key =
-            ValidateUtils.checkNotNull(AuthorizedKeyEntry.parseAuthorizedKeyEntry(line),
-                "No valid key entry recovered from line=%s", data);
-        entry.setKeyEntry(key);
-        return entry;
-    }
-}


[11/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common utilities code from sshd-core into sshd-common (new artifact)

Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/Ed25519PublicKeyDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/Ed25519PublicKeyDecoder.java b/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/Ed25519PublicKeyDecoder.java
deleted file mode 100644
index 793965c..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/Ed25519PublicKeyDecoder.java
+++ /dev/null
@@ -1,97 +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.security.eddsa;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.security.GeneralSecurityException;
-import java.security.KeyFactory;
-import java.security.KeyPairGenerator;
-import java.util.Collections;
-import java.util.Objects;
-
-import org.apache.sshd.common.config.keys.KeyEntryResolver;
-import org.apache.sshd.common.config.keys.impl.AbstractPublicKeyEntryDecoder;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-import net.i2p.crypto.eddsa.EdDSAPrivateKey;
-import net.i2p.crypto.eddsa.EdDSAPublicKey;
-import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec;
-import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public final class Ed25519PublicKeyDecoder extends AbstractPublicKeyEntryDecoder<EdDSAPublicKey, EdDSAPrivateKey> {
-    public static final Ed25519PublicKeyDecoder INSTANCE = new Ed25519PublicKeyDecoder();
-
-    private Ed25519PublicKeyDecoder() {
-        super(EdDSAPublicKey.class, EdDSAPrivateKey.class, Collections.unmodifiableList(Collections.singletonList(KeyPairProvider.SSH_ED25519)));
-    }
-
-    @Override
-    public EdDSAPublicKey clonePublicKey(EdDSAPublicKey key) throws GeneralSecurityException {
-        if (key == null) {
-            return null;
-        } else {
-            return generatePublicKey(new EdDSAPublicKeySpec(key.getA(), key.getParams()));
-        }
-    }
-
-    @Override
-    public EdDSAPrivateKey clonePrivateKey(EdDSAPrivateKey key) throws GeneralSecurityException {
-        if (key == null) {
-            return null;
-        } else {
-            return generatePrivateKey(new EdDSAPrivateKeySpec(key.getSeed(), key.getParams()));
-        }
-    }
-
-    @Override
-    public KeyPairGenerator getKeyPairGenerator() throws GeneralSecurityException {
-        return SecurityUtils.getKeyPairGenerator(SecurityUtils.EDDSA);
-    }
-
-    @Override
-    public String encodePublicKey(OutputStream s, EdDSAPublicKey key) throws IOException {
-        Objects.requireNonNull(key, "No public key provided");
-        KeyEntryResolver.encodeString(s, KeyPairProvider.SSH_ED25519);
-        byte[] seed = getSeedValue(key);
-        KeyEntryResolver.writeRLEBytes(s, seed);
-        return KeyPairProvider.SSH_ED25519;
-    }
-
-    @Override
-    public KeyFactory getKeyFactoryInstance() throws GeneralSecurityException {
-        return SecurityUtils.getKeyFactory(SecurityUtils.EDDSA);
-    }
-
-    @Override
-    public EdDSAPublicKey decodePublicKey(String keyType, InputStream keyData) throws IOException, GeneralSecurityException {
-        byte[] seed = KeyEntryResolver.readRLEBytes(keyData);
-        return EdDSAPublicKey.class.cast(SecurityUtils.generateEDDSAPublicKey(keyType, seed));
-    }
-
-    public static byte[] getSeedValue(EdDSAPublicKey key) {
-        // a bit of reverse-engineering on the EdDSAPublicKeySpec
-        return (key == null) ? null : key.getAbyte();
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderRegistrar.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderRegistrar.java b/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderRegistrar.java
deleted file mode 100644
index 61f16e9..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderRegistrar.java
+++ /dev/null
@@ -1,104 +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.security.eddsa;
-
-import java.security.KeyFactory;
-import java.security.KeyPairGenerator;
-import java.security.Provider;
-import java.security.Signature;
-import java.util.Objects;
-import java.util.concurrent.atomic.AtomicReference;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ReflectionUtils;
-import org.apache.sshd.common.util.security.AbstractSecurityProviderRegistrar;
-import org.apache.sshd.common.util.security.SecurityUtils;
-import org.apache.sshd.common.util.threads.ThreadUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class EdDSASecurityProviderRegistrar extends AbstractSecurityProviderRegistrar {
-    public static final String PROVIDER_CLASS = "net.i2p.crypto.eddsa.EdDSASecurityProvider";
-    // Do not define a static registrar instance to minimize class loading issues
-    private final AtomicReference<Boolean> supportHolder = new AtomicReference<>(null);
-
-    public EdDSASecurityProviderRegistrar() {
-        super(SecurityUtils.EDDSA);
-    }
-
-    @Override
-    public boolean isEnabled() {
-        if (!super.isEnabled()) {
-            return false;
-        }
-
-        // For backward compatibility
-        return this.getBooleanProperty(SecurityUtils.EDDSA_SUPPORTED_PROP, true);
-    }
-
-    @Override
-    public Provider getSecurityProvider() {
-        try {
-            return getOrCreateProvider(PROVIDER_CLASS);
-        } catch (ReflectiveOperationException t) {
-            Throwable e = GenericUtils.peelException(t);
-            log.error("getSecurityProvider({}) failed ({}) to instantiate {}: {}",
-                      getName(), e.getClass().getSimpleName(), PROVIDER_CLASS, e.getMessage());
-            if (e instanceof RuntimeException) {
-                throw (RuntimeException) e;
-            }
-
-            throw new RuntimeException(e);
-        }
-    }
-
-    @Override
-    public boolean isSecurityEntitySupported(Class<?> entityType, String name) {
-        if (!isSupported()) {
-            return false;
-        }
-
-        if (KeyPairGenerator.class.isAssignableFrom(entityType)
-                || KeyFactory.class.isAssignableFrom(entityType)) {
-            return Objects.compare(name, getName(), String.CASE_INSENSITIVE_ORDER) == 0;
-        } else if (Signature.class.isAssignableFrom(entityType)) {
-            return Objects.compare(SecurityUtils.CURVE_ED25519_SHA512, name, String.CASE_INSENSITIVE_ORDER) == 0;
-        } else {
-            return false;
-        }
-    }
-
-    @Override
-    public boolean isSupported() {
-        Boolean supported;
-        synchronized (supportHolder) {
-            supported = supportHolder.get();
-            if (supported != null) {
-                return supported.booleanValue();
-            }
-
-            ClassLoader cl = ThreadUtils.resolveDefaultClassLoader(getClass());
-            supported = ReflectionUtils.isClassAvailable(cl, "net.i2p.crypto.eddsa.EdDSAKey");
-            supportHolder.set(supported);
-        }
-
-        return supported.booleanValue();
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderUtils.java
deleted file mode 100644
index 242f550..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderUtils.java
+++ /dev/null
@@ -1,201 +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.security.eddsa;
-
-import java.security.GeneralSecurityException;
-import java.security.InvalidKeyException;
-import java.security.Key;
-import java.security.KeyFactory;
-import java.security.NoSuchAlgorithmException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.util.Arrays;
-import java.util.Objects;
-
-import org.apache.sshd.common.config.keys.PrivateKeyEntryDecoder;
-import org.apache.sshd.common.config.keys.PublicKeyEntryDecoder;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.buffer.Buffer;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-import net.i2p.crypto.eddsa.EdDSAEngine;
-import net.i2p.crypto.eddsa.EdDSAKey;
-import net.i2p.crypto.eddsa.EdDSAPrivateKey;
-import net.i2p.crypto.eddsa.EdDSAPublicKey;
-import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
-import net.i2p.crypto.eddsa.spec.EdDSAParameterSpec;
-import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec;
-import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public final class EdDSASecurityProviderUtils {
-    // See EdDSANamedCurveTable
-    public static final String CURVE_ED25519_SHA512 = "Ed25519";
-
-    private EdDSASecurityProviderUtils() {
-        throw new UnsupportedOperationException("No instance");
-    }
-
-    public static Class<? extends PublicKey> getEDDSAPublicKeyType() {
-        return EdDSAPublicKey.class;
-    }
-
-    public static Class<? extends PrivateKey> getEDDSAPrivateKeyType() {
-        return EdDSAPrivateKey.class;
-    }
-
-    public static int getEDDSAKeySize(Key key) {
-        return (SecurityUtils.isEDDSACurveSupported() && (key instanceof EdDSAKey)) ? 256 : -1;
-    }
-
-    public static boolean compareEDDSAPPublicKeys(PublicKey k1, PublicKey k2) {
-        if (!SecurityUtils.isEDDSACurveSupported()) {
-            return false;
-        }
-
-        if ((k1 instanceof EdDSAPublicKey) && (k2 instanceof EdDSAPublicKey)) {
-            if (Objects.equals(k1, k2)) {
-                return true;
-            } else if (k1 == null || k2 == null) {
-                return false;   // both null is covered by Objects#equals
-            }
-
-            EdDSAPublicKey ed1 = (EdDSAPublicKey) k1;
-            EdDSAPublicKey ed2 = (EdDSAPublicKey) k2;
-            return Arrays.equals(ed1.getAbyte(), ed2.getAbyte())
-                && compareEDDSAKeyParams(ed1.getParams(), ed2.getParams());
-        }
-
-        return false;
-    }
-
-    public static boolean isEDDSASignatureAlgorithm(String algorithm) {
-        return EdDSAEngine.SIGNATURE_ALGORITHM.equalsIgnoreCase(algorithm);
-    }
-
-    public static EdDSAPublicKey recoverEDDSAPublicKey(PrivateKey key) throws GeneralSecurityException {
-        ValidateUtils.checkTrue(SecurityUtils.isEDDSACurveSupported(), SecurityUtils.EDDSA + " not supported");
-        if (!(key instanceof EdDSAPrivateKey)) {
-            throw new InvalidKeyException("Private key is not " + SecurityUtils.EDDSA);
-        }
-
-        EdDSAPrivateKey prvKey = (EdDSAPrivateKey) key;
-        EdDSAPublicKeySpec keySpec = new EdDSAPublicKeySpec(prvKey.getAbyte(), prvKey.getParams());
-        KeyFactory factory = SecurityUtils.getKeyFactory(SecurityUtils.EDDSA);
-        return EdDSAPublicKey.class.cast(factory.generatePublic(keySpec));
-    }
-
-    public static org.apache.sshd.common.signature.Signature getEDDSASignature() {
-        ValidateUtils.checkTrue(SecurityUtils.isEDDSACurveSupported(), SecurityUtils.EDDSA + " not supported");
-        return new SignatureEd25519();
-    }
-
-    public static boolean isEDDSAKeyFactoryAlgorithm(String algorithm) {
-        return SecurityUtils.EDDSA.equalsIgnoreCase(algorithm);
-    }
-
-    public static boolean isEDDSAKeyPairGeneratorAlgorithm(String algorithm) {
-        return SecurityUtils.EDDSA.equalsIgnoreCase(algorithm);
-    }
-
-    public static PublicKeyEntryDecoder<? extends PublicKey, ? extends PrivateKey> getEDDSAPublicKeyEntryDecoder() {
-        ValidateUtils.checkTrue(SecurityUtils.isEDDSACurveSupported(), SecurityUtils.EDDSA + " not supported");
-        return Ed25519PublicKeyDecoder.INSTANCE;
-    }
-
-    public static PrivateKeyEntryDecoder<? extends PublicKey, ? extends PrivateKey> getOpenSSHEDDSAPrivateKeyEntryDecoder() {
-        ValidateUtils.checkTrue(SecurityUtils.isEDDSACurveSupported(), SecurityUtils.EDDSA + " not supported");
-        return OpenSSHEd25519PrivateKeyEntryDecoder.INSTANCE;
-    }
-
-    public static boolean compareEDDSAPrivateKeys(PrivateKey k1, PrivateKey k2) {
-        if (!SecurityUtils.isEDDSACurveSupported()) {
-            return false;
-        }
-
-        if ((k1 instanceof EdDSAPrivateKey) && (k2 instanceof EdDSAPrivateKey)) {
-            if (Objects.equals(k1, k2)) {
-                return true;
-            } else if (k1 == null || k2 == null) {
-                return false;   // both null is covered by Objects#equals
-            }
-
-            EdDSAPrivateKey ed1 = (EdDSAPrivateKey) k1;
-            EdDSAPrivateKey ed2 = (EdDSAPrivateKey) k2;
-            return Arrays.equals(ed1.getSeed(), ed2.getSeed())
-                && compareEDDSAKeyParams(ed1.getParams(), ed2.getParams());
-        }
-
-        return false;
-    }
-
-    public static boolean compareEDDSAKeyParams(EdDSAParameterSpec s1, EdDSAParameterSpec s2) {
-        if (Objects.equals(s1, s2)) {
-            return true;
-        } else if (s1 == null || s2 == null) {
-            return false;   // both null is covered by Objects#equals
-        } else {
-            return Objects.equals(s1.getHashAlgorithm(), s2.getHashAlgorithm())
-                && Objects.equals(s1.getCurve(), s2.getCurve())
-                && Objects.equals(s1.getB(), s2.getB());
-        }
-    }
-
-    public static PublicKey generateEDDSAPublicKey(byte[] seed) throws GeneralSecurityException {
-        if (!SecurityUtils.isEDDSACurveSupported()) {
-            throw new NoSuchAlgorithmException(SecurityUtils.EDDSA + " not supported");
-        }
-
-        EdDSAParameterSpec params = EdDSANamedCurveTable.getByName(CURVE_ED25519_SHA512);
-        EdDSAPublicKeySpec keySpec = new EdDSAPublicKeySpec(seed, params);
-        KeyFactory factory = SecurityUtils.getKeyFactory(SecurityUtils.EDDSA);
-        return factory.generatePublic(keySpec);
-    }
-
-    public static PrivateKey generateEDDSAPrivateKey(byte[] seed) throws GeneralSecurityException {
-        if (!SecurityUtils.isEDDSACurveSupported()) {
-            throw new NoSuchAlgorithmException(SecurityUtils.EDDSA + " not supported");
-        }
-
-        EdDSAParameterSpec params = EdDSANamedCurveTable.getByName(CURVE_ED25519_SHA512);
-        EdDSAPrivateKeySpec keySpec = new EdDSAPrivateKeySpec(seed, params);
-        KeyFactory factory = SecurityUtils.getKeyFactory(SecurityUtils.EDDSA);
-        return factory.generatePrivate(keySpec);
-    }
-
-    public static <B extends Buffer> B putRawEDDSAPublicKey(B buffer, PublicKey key) {
-        ValidateUtils.checkTrue(SecurityUtils.isEDDSACurveSupported(), SecurityUtils.EDDSA + " not supported");
-        EdDSAPublicKey edKey = ValidateUtils.checkInstanceOf(key, EdDSAPublicKey.class, "Not an EDDSA public key: %s", key);
-        byte[] seed = Ed25519PublicKeyDecoder.getSeedValue(edKey);
-        ValidateUtils.checkNotNull(seed, "No seed extracted from key: %s", edKey.getA());
-        buffer.putString(KeyPairProvider.SSH_ED25519);
-        buffer.putBytes(seed);
-        return buffer;
-    }
-
-    public static <B extends Buffer> B putEDDSAKeyPair(B buffer, PublicKey pubKey, PrivateKey prvKey) {
-        ValidateUtils.checkTrue(SecurityUtils.isEDDSACurveSupported(), SecurityUtils.EDDSA + " not supported");
-        ValidateUtils.checkInstanceOf(pubKey, EdDSAPublicKey.class, "Not an EDDSA public key: %s", pubKey);
-        ValidateUtils.checkInstanceOf(prvKey, EdDSAPrivateKey.class, "Not an EDDSA private key: %s", prvKey);
-        throw new UnsupportedOperationException("Full SSHD-440 implementation N/A");
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/OpenSSHEd25519PrivateKeyEntryDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/OpenSSHEd25519PrivateKeyEntryDecoder.java b/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/OpenSSHEd25519PrivateKeyEntryDecoder.java
deleted file mode 100644
index 4888818..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/OpenSSHEd25519PrivateKeyEntryDecoder.java
+++ /dev/null
@@ -1,172 +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.security.eddsa;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.security.GeneralSecurityException;
-import java.security.InvalidKeyException;
-import java.security.KeyFactory;
-import java.security.KeyPairGenerator;
-import java.security.NoSuchAlgorithmException;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Locale;
-import java.util.Objects;
-
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.config.keys.KeyEntryResolver;
-import org.apache.sshd.common.config.keys.impl.AbstractPrivateKeyEntryDecoder;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-import net.i2p.crypto.eddsa.EdDSAPrivateKey;
-import net.i2p.crypto.eddsa.EdDSAPublicKey;
-import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
-import net.i2p.crypto.eddsa.spec.EdDSAParameterSpec;
-import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec;
-import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class OpenSSHEd25519PrivateKeyEntryDecoder extends AbstractPrivateKeyEntryDecoder<EdDSAPublicKey, EdDSAPrivateKey> {
-    public static final OpenSSHEd25519PrivateKeyEntryDecoder INSTANCE = new OpenSSHEd25519PrivateKeyEntryDecoder();
-    private static final int PK_SIZE = 32;
-    private static final int SK_SIZE = 32;
-    private static final int KEYPAIR_SIZE = PK_SIZE + SK_SIZE;
-
-    public OpenSSHEd25519PrivateKeyEntryDecoder() {
-        super(EdDSAPublicKey.class, EdDSAPrivateKey.class, Collections.unmodifiableList(Collections.singletonList(KeyPairProvider.SSH_ED25519)));
-    }
-
-    @Override
-    public EdDSAPrivateKey decodePrivateKey(String keyType, FilePasswordProvider passwordProvider, InputStream keyData)
-            throws IOException, GeneralSecurityException {
-        if (!KeyPairProvider.SSH_ED25519.equals(keyType)) {
-            throw new InvalidKeyException("Unsupported key type: " + keyType);
-        }
-
-        if (!SecurityUtils.isEDDSACurveSupported()) {
-            throw new NoSuchAlgorithmException(SecurityUtils.EDDSA + " provider not supported");
-        }
-
-        // ed25519 bernstein naming: pk .. public key, sk .. secret key
-        // we expect to find two byte arrays with the following structure (type:size):
-        // [pk:32], [sk:32,pk:32]
-
-        byte[] pk = KeyEntryResolver.readRLEBytes(keyData);
-        byte[] keypair = KeyEntryResolver.readRLEBytes(keyData);
-
-        if (pk.length != PK_SIZE) {
-            throw new InvalidKeyException(String.format(Locale.ENGLISH, "Unexpected pk size: %s (expected %s)", pk.length, PK_SIZE));
-        }
-
-        if (keypair.length != KEYPAIR_SIZE) {
-            throw new InvalidKeyException(String.format(Locale.ENGLISH, "Unexpected keypair size: %s (expected %s)", keypair.length, KEYPAIR_SIZE));
-        }
-
-        byte[] sk = Arrays.copyOf(keypair, SK_SIZE);
-
-        // verify that the keypair contains the expected pk
-        // yes, it's stored redundant, this seems to mimic the output structure of the keypair generation interface
-        if (!Arrays.equals(pk, Arrays.copyOfRange(keypair, SK_SIZE, KEYPAIR_SIZE))) {
-            throw new InvalidKeyException("Keypair did not contain the public key.");
-        }
-
-        // create the private key
-        EdDSAParameterSpec params = EdDSANamedCurveTable.getByName(EdDSASecurityProviderUtils.CURVE_ED25519_SHA512);
-        EdDSAPrivateKey privateKey = generatePrivateKey(new EdDSAPrivateKeySpec(sk, params));
-
-        // the private key class contains the calculated public key (Abyte)
-        // pointers to the corresponding code:
-        // EdDSAPrivateKeySpec.EdDSAPrivateKeySpec(byte[], EdDSAParameterSpec): A = spec.getB().scalarMultiply(a);
-        // EdDSAPrivateKey.EdDSAPrivateKey(EdDSAPrivateKeySpec): this.Abyte = this.A.toByteArray();
-
-        // we can now verify the generated pk matches the one we read
-        if (!Arrays.equals(privateKey.getAbyte(), pk)) {
-            throw new InvalidKeyException("The provided pk does NOT match the computed pk for the given sk.");
-        }
-
-        return privateKey;
-    }
-
-    @Override
-    public String encodePrivateKey(OutputStream s, EdDSAPrivateKey key) throws IOException {
-        Objects.requireNonNull(key, "No private key provided");
-
-        // ed25519 bernstein naming: pk .. public key, sk .. secret key
-        // we are expected to write the following arrays (type:size):
-        // [pk:32], [sk:32,pk:32]
-
-        byte[] sk = key.getSeed();
-        byte[] pk = key.getAbyte();
-
-        Objects.requireNonNull(sk, "No seed");
-
-        byte[] keypair = new byte[KEYPAIR_SIZE];
-        System.arraycopy(sk, 0, keypair, 0, SK_SIZE);
-        System.arraycopy(pk, 0, keypair, SK_SIZE, PK_SIZE);
-
-        KeyEntryResolver.writeRLEBytes(s, pk);
-        KeyEntryResolver.writeRLEBytes(s, keypair);
-
-        return KeyPairProvider.SSH_ED25519;
-    }
-
-    @Override
-    public boolean isPublicKeyRecoverySupported() {
-        return true;
-    }
-
-    @Override
-    public EdDSAPublicKey recoverPublicKey(EdDSAPrivateKey prvKey) throws GeneralSecurityException {
-        return EdDSASecurityProviderUtils.recoverEDDSAPublicKey(prvKey);
-    }
-
-    @Override
-    public EdDSAPublicKey clonePublicKey(EdDSAPublicKey key) throws GeneralSecurityException {
-        if (key == null) {
-            return null;
-        } else {
-            return generatePublicKey(new EdDSAPublicKeySpec(key.getA(), key.getParams()));
-        }
-    }
-
-    @Override
-    public EdDSAPrivateKey clonePrivateKey(EdDSAPrivateKey key) throws GeneralSecurityException {
-        if (key == null) {
-            return null;
-        } else {
-            return generatePrivateKey(new EdDSAPrivateKeySpec(key.getSeed(), key.getParams()));
-        }
-    }
-
-    @Override
-    public KeyPairGenerator getKeyPairGenerator() throws GeneralSecurityException {
-        return SecurityUtils.getKeyPairGenerator(SecurityUtils.EDDSA);
-    }
-
-    @Override
-    public KeyFactory getKeyFactoryInstance() throws GeneralSecurityException {
-        return SecurityUtils.getKeyFactory(SecurityUtils.EDDSA);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/SignatureEd25519.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/SignatureEd25519.java b/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/SignatureEd25519.java
deleted file mode 100644
index 012be95..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/SignatureEd25519.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.security.eddsa;
-
-import java.util.Map;
-
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.signature.AbstractSignature;
-import org.apache.sshd.common.util.ValidateUtils;
-
-import net.i2p.crypto.eddsa.EdDSAEngine;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class SignatureEd25519 extends AbstractSignature {
-    public SignatureEd25519() {
-        super(EdDSAEngine.SIGNATURE_ALGORITHM);
-    }
-
-    @Override
-    public boolean verify(byte[] sig) throws Exception {
-        byte[] data = sig;
-        Map.Entry<String, byte[]> encoding = extractEncodedSignature(data);
-        if (encoding != null) {
-            String keyType = encoding.getKey();
-            ValidateUtils.checkTrue(KeyPairProvider.SSH_ED25519.equals(keyType), "Mismatched key type: %s", keyType);
-            data = encoding.getValue();
-        }
-
-        return doVerify(data);
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/threads/CloseableExecutorService.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/threads/CloseableExecutorService.java b/sshd-core/src/main/java/org/apache/sshd/common/util/threads/CloseableExecutorService.java
deleted file mode 100644
index 3b9beeb..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/threads/CloseableExecutorService.java
+++ /dev/null
@@ -1,28 +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.threads;
-
-import java.util.concurrent.ExecutorService;
-
-import org.apache.sshd.common.Closeable;
-
-public interface CloseableExecutorService extends ExecutorService, Closeable {
-    // Nothing extra
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/threads/ExecutorServiceCarrier.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/threads/ExecutorServiceCarrier.java b/sshd-core/src/main/java/org/apache/sshd/common/util/threads/ExecutorServiceCarrier.java
deleted file mode 100644
index b44bd46..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/threads/ExecutorServiceCarrier.java
+++ /dev/null
@@ -1,31 +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.threads;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface ExecutorServiceCarrier {
-    /**
-     * @return The {@link CloseableExecutorService} to use
-     */
-    CloseableExecutorService getExecutorService();
-
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/threads/NoCloseExecutor.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/threads/NoCloseExecutor.java b/sshd-core/src/main/java/org/apache/sshd/common/util/threads/NoCloseExecutor.java
deleted file mode 100644
index cb42805..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/threads/NoCloseExecutor.java
+++ /dev/null
@@ -1,160 +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.threads;
-
-import java.io.IOException;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-import org.apache.sshd.common.future.CloseFuture;
-import org.apache.sshd.common.future.DefaultCloseFuture;
-import org.apache.sshd.common.future.SshFutureListener;
-import org.apache.sshd.common.util.ValidateUtils;
-
-/**
- * Wraps an {@link ExecutorService} as a {@link CloseableExecutorService}
- * and avoids calling its {@code shutdown} methods when the wrapper is shut down
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class NoCloseExecutor implements CloseableExecutorService {
-    protected final ExecutorService executor;
-    protected final CloseFuture closeFuture;
-
-    public NoCloseExecutor(ExecutorService executor) {
-        this.executor = executor;
-        closeFuture = new DefaultCloseFuture(null, null);
-    }
-
-    @Override
-    public <T> Future<T> submit(Callable<T> task) {
-        ValidateUtils.checkState(!isShutdown(), "Executor has been shut down");
-        return executor.submit(task);
-    }
-
-    @Override
-    public <T> Future<T> submit(Runnable task, T result) {
-        ValidateUtils.checkState(!isShutdown(), "Executor has been shut down");
-        return executor.submit(task, result);
-    }
-
-    @Override
-    public Future<?> submit(Runnable task) {
-        ValidateUtils.checkState(!isShutdown(), "Executor has been shut down");
-        return executor.submit(task);
-    }
-
-    @Override
-    public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
-            throws InterruptedException {
-        ValidateUtils.checkState(!isShutdown(), "Executor has been shut down");
-        return executor.invokeAll(tasks);
-    }
-
-    @Override
-    public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
-            throws InterruptedException {
-        ValidateUtils.checkState(!isShutdown(), "Executor has been shut down");
-        return executor.invokeAll(tasks, timeout, unit);
-    }
-
-    @Override
-    public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
-            throws InterruptedException, ExecutionException {
-        ValidateUtils.checkState(!isShutdown(), "Executor has been shut down");
-        return executor.invokeAny(tasks);
-    }
-
-    @Override
-    public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
-            throws InterruptedException, ExecutionException, TimeoutException {
-        ValidateUtils.checkState(!isShutdown(), "Executor has been shut down");
-        return executor.invokeAny(tasks, timeout, unit);
-    }
-
-    @Override
-    public void execute(Runnable command) {
-        ValidateUtils.checkState(!isShutdown(), "Executor has been shut down");
-        executor.execute(command);
-    }
-
-    @Override
-    public void shutdown() {
-        close(true);
-    }
-
-    @Override
-    public List<Runnable> shutdownNow() {
-        close(true);
-        return Collections.emptyList();
-    }
-
-    @Override
-    public boolean isShutdown() {
-        return isClosed();
-    }
-
-    @Override
-    public boolean isTerminated() {
-        return isClosed();
-    }
-
-    @Override
-    public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
-        try {
-            return closeFuture.await(timeout, unit);
-        } catch (IOException e) {
-            throw (InterruptedException) new InterruptedException().initCause(e);
-        }
-    }
-
-    @Override
-    public CloseFuture close(boolean immediately) {
-        closeFuture.setClosed();
-        return closeFuture;
-    }
-
-    @Override
-    public void addCloseFutureListener(SshFutureListener<CloseFuture> listener) {
-        closeFuture.addListener(listener);
-    }
-
-    @Override
-    public void removeCloseFutureListener(SshFutureListener<CloseFuture> listener) {
-        closeFuture.removeListener(listener);
-    }
-
-    @Override
-    public boolean isClosed() {
-        return closeFuture.isClosed();
-    }
-
-    @Override
-    public boolean isClosing() {
-        return isClosed();
-    }
-
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/threads/SshThreadPoolExecutor.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/threads/SshThreadPoolExecutor.java b/sshd-core/src/main/java/org/apache/sshd/common/util/threads/SshThreadPoolExecutor.java
deleted file mode 100644
index ccaa655..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/threads/SshThreadPoolExecutor.java
+++ /dev/null
@@ -1,138 +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.threads;
-
-import java.util.List;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.RejectedExecutionHandler;
-import java.util.concurrent.ThreadFactory;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-
-import org.apache.sshd.common.future.CloseFuture;
-import org.apache.sshd.common.future.SshFutureListener;
-import org.apache.sshd.common.util.closeable.AbstractCloseable;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class SshThreadPoolExecutor extends ThreadPoolExecutor implements CloseableExecutorService {
-    protected final DelegateCloseable closeable = new DelegateCloseable();
-
-    protected class DelegateCloseable extends AbstractCloseable {
-        protected DelegateCloseable() {
-            super();
-        }
-
-        @Override
-        protected CloseFuture doCloseGracefully() {
-            shutdown();
-            return closeFuture;
-        }
-
-        @Override
-        protected void doCloseImmediately() {
-            shutdownNow();
-            super.doCloseImmediately();
-        }
-
-        protected void setClosed() {
-            closeFuture.setClosed();
-        }
-    }
-
-    public SshThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
-        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
-    }
-
-    public SshThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
-            BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
-        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
-    }
-
-    public SshThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
-            BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {
-        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
-    }
-
-    public SshThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
-            BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
-        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
-    }
-
-    @Override
-    protected void terminated() {
-        closeable.doCloseImmediately();
-    }
-
-    @Override
-    public void shutdown() {
-        super.shutdown();
-    }
-
-    @Override
-    public List<Runnable> shutdownNow() {
-        return super.shutdownNow();
-    }
-
-    @Override
-    public boolean isShutdown() {
-        return super.isShutdown();
-    }
-
-    @Override
-    public boolean isTerminating() {
-        return super.isTerminating();
-    }
-
-    @Override
-    public boolean isTerminated() {
-        return super.isTerminated();
-    }
-
-    @Override
-    public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
-        return super.awaitTermination(timeout, unit);
-    }
-
-    @Override
-    public CloseFuture close(boolean immediately) {
-        return closeable.close(immediately);
-    }
-
-    @Override
-    public void addCloseFutureListener(SshFutureListener<CloseFuture> listener) {
-        closeable.addCloseFutureListener(listener);
-    }
-
-    @Override
-    public void removeCloseFutureListener(SshFutureListener<CloseFuture> listener) {
-        closeable.removeCloseFutureListener(listener);
-    }
-
-    @Override
-    public boolean isClosed() {
-        return closeable.isClosed();
-    }
-
-    @Override
-    public boolean isClosing() {
-        return closeable.isClosing();
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/threads/SshdThreadFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/threads/SshdThreadFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/util/threads/SshdThreadFactory.java
deleted file mode 100644
index 5dc0c7b..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/threads/SshdThreadFactory.java
+++ /dev/null
@@ -1,78 +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.threads;
-
-import java.security.AccessController;
-import java.security.PrivilegedActionException;
-import java.security.PrivilegedExceptionAction;
-import java.util.concurrent.ThreadFactory;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import org.apache.sshd.common.util.logging.AbstractLoggingBean;
-
-/**
- * Default {@link ThreadFactory} used by {@link ThreadUtils} to create
- * thread pools if user did provide one
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class SshdThreadFactory extends AbstractLoggingBean implements ThreadFactory {
-    private final ThreadGroup group;
-    private final AtomicInteger threadNumber = new AtomicInteger(1);
-    private final String namePrefix;
-
-    public SshdThreadFactory(String name) {
-        SecurityManager s = System.getSecurityManager();
-        group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
-        String effectiveName = name.replace(' ', '-');
-        namePrefix = "sshd-" + effectiveName + "-thread-";
-    }
-
-    @Override
-    public Thread newThread(Runnable r) {
-        Thread t;
-        try {
-            // see SSHD-668
-            if (System.getSecurityManager() != null) {
-                t = AccessController.doPrivileged((PrivilegedExceptionAction<Thread>) () ->
-                        new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0));
-            } else {
-                t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0);
-            }
-        } catch (PrivilegedActionException e) {
-            Exception err = e.getException();
-            if (err instanceof RuntimeException) {
-                throw (RuntimeException) err;
-            } else {
-                throw new RuntimeException(err);
-            }
-        }
-
-        if (!t.isDaemon()) {
-            t.setDaemon(true);
-        }
-        if (t.getPriority() != Thread.NORM_PRIORITY) {
-            t.setPriority(Thread.NORM_PRIORITY);
-        }
-        if (log.isTraceEnabled()) {
-            log.trace("newThread({})[{}] runnable={}", group, t.getName(), r);
-        }
-        return t;
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/threads/ThreadUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/threads/ThreadUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/threads/ThreadUtils.java
deleted file mode 100644
index c803389..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/threads/ThreadUtils.java
+++ /dev/null
@@ -1,185 +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.threads;
-
-import java.util.LinkedHashSet;
-import java.util.Set;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.ScheduledThreadPoolExecutor;
-import java.util.concurrent.SynchronousQueue;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Utility class for thread pools.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public final class ThreadUtils {
-    private ThreadUtils() {
-        throw new UnsupportedOperationException("No instance");
-    }
-
-    /**
-     * Wraps an {@link CloseableExecutorService} in such a way as to &quot;protect&quot;
-     * it for calls to the {@link CloseableExecutorService#shutdown()} or
-     * {@link CloseableExecutorService#shutdownNow()}. All other calls are delegated as-is
-     * to the original service. <B>Note:</B> the exposed wrapped proxy will
-     * answer correctly the {@link CloseableExecutorService#isShutdown()} query if indeed
-     * one of the {@code shutdown} methods was invoked.
-     *
-     * @param executorService The original service - ignored if {@code null}
-     * @param shutdownOnExit  If {@code true} then it is OK to shutdown the executor
-     *                        so no wrapping takes place.
-     * @return Either the original service or a wrapped one - depending on the
-     * value of the <tt>shutdownOnExit</tt> parameter
-     */
-    public static CloseableExecutorService protectExecutorServiceShutdown(CloseableExecutorService executorService, boolean shutdownOnExit) {
-        if (executorService == null || shutdownOnExit || executorService instanceof NoCloseExecutor) {
-            return executorService;
-        } else {
-            return new NoCloseExecutor(executorService);
-        }
-    }
-
-    public static CloseableExecutorService noClose(CloseableExecutorService executorService) {
-        return protectExecutorServiceShutdown(executorService, false);
-    }
-
-    public static ClassLoader resolveDefaultClassLoader(Object anchor) {
-        return resolveDefaultClassLoader(anchor == null ? null : anchor.getClass());
-    }
-
-    public static Iterable<ClassLoader> resolveDefaultClassLoaders(Object anchor) {
-        return resolveDefaultClassLoaders(anchor == null ? null : anchor.getClass());
-    }
-
-    public static <T> T createDefaultInstance(Class<?> anchor, Class<T> targetType, String className)
-            throws ReflectiveOperationException {
-        return createDefaultInstance(resolveDefaultClassLoaders(anchor), targetType, className);
-    }
-
-    public static <T> T createDefaultInstance(ClassLoader cl, Class<T> targetType, String className)
-            throws ReflectiveOperationException {
-        Class<?> instanceType = cl.loadClass(className);
-        Object instance = instanceType.newInstance();
-        return targetType.cast(instance);
-    }
-
-    public static <T> T createDefaultInstance(Iterable<ClassLoader> cls, Class<T> targetType, String className)
-            throws ReflectiveOperationException {
-        for (ClassLoader cl : cls) {
-            try {
-                return createDefaultInstance(cl, targetType, className);
-            } catch (ClassNotFoundException e) {
-                // Ignore
-            }
-        }
-        throw new ClassNotFoundException(className);
-    }
-
-    /**
-     * <P>Attempts to find the most suitable {@link ClassLoader} as follows:</P>
-     * <UL>
-     * <LI><P>
-     * Check the {@link Thread#getContextClassLoader()} value
-     * </P></LI>
-     *
-     * <LI><P>
-     * If no thread context class loader then check the anchor
-     * class (if given) for its class loader
-     * </P></LI>
-     *
-     * <LI><P>
-     * If still no loader available, then use {@link ClassLoader#getSystemClassLoader()}
-     * </P></LI>
-     * </UL>
-     *
-     * @param anchor The anchor {@link Class} to use if no current thread
-     *               - ignored if {@code null}
-     *               context class loader
-     * @return The resolver {@link ClassLoader}
-     */
-    public static ClassLoader resolveDefaultClassLoader(Class<?> anchor) {
-        Thread thread = Thread.currentThread();
-        ClassLoader cl = thread.getContextClassLoader();
-        if (cl != null) {
-            return cl;
-        }
-
-        if (anchor != null) {
-            cl = anchor.getClassLoader();
-        }
-
-        if (cl == null) {   // can happen for core Java classes
-            cl = ClassLoader.getSystemClassLoader();
-        }
-
-        return cl;
-    }
-
-    public static Iterable<ClassLoader> resolveDefaultClassLoaders(Class<?> anchor) {
-        Set<ClassLoader> cls = new LinkedHashSet<>();
-        Thread thread = Thread.currentThread();
-        ClassLoader cl = thread.getContextClassLoader();
-        if (cl != null) {
-            cls.add(cl);
-        }
-        if (anchor != null) {
-            cls.add(anchor.getClassLoader());
-        }
-        cls.add(ClassLoader.getSystemClassLoader());
-        return cls;
-    }
-
-    public static CloseableExecutorService newFixedThreadPoolIf(CloseableExecutorService executorService, String poolName, int nThreads) {
-        return executorService == null ? newFixedThreadPool(poolName, nThreads) : executorService;
-    }
-
-    public static CloseableExecutorService newFixedThreadPool(String poolName, int nThreads) {
-        return new SshThreadPoolExecutor(
-                nThreads, nThreads,
-                0L, TimeUnit.MILLISECONDS, // TODO make this configurable
-                new LinkedBlockingQueue<>(),
-                new SshdThreadFactory(poolName),
-                new ThreadPoolExecutor.CallerRunsPolicy());
-    }
-
-    public static CloseableExecutorService newCachedThreadPoolIf(CloseableExecutorService executorService, String poolName) {
-        return executorService == null ? newCachedThreadPool(poolName) : executorService;
-    }
-
-    public static CloseableExecutorService newCachedThreadPool(String poolName) {
-        return new SshThreadPoolExecutor(
-                0, Integer.MAX_VALUE, // TODO make this configurable
-                60L, TimeUnit.SECONDS, // TODO make this configurable
-                new SynchronousQueue<>(),
-                new SshdThreadFactory(poolName),
-                new ThreadPoolExecutor.CallerRunsPolicy());
-    }
-
-    public static ScheduledExecutorService newSingleThreadScheduledExecutor(String poolName) {
-        return new ScheduledThreadPoolExecutor(1, new SshdThreadFactory(poolName));
-    }
-
-    public static CloseableExecutorService newSingleThreadExecutor(String poolName) {
-        return newFixedThreadPool(poolName, 1);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/server/auth/pubkey/PublickeyAuthenticator.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/auth/pubkey/PublickeyAuthenticator.java b/sshd-core/src/main/java/org/apache/sshd/server/auth/pubkey/PublickeyAuthenticator.java
index 2d7a908..a498478 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/auth/pubkey/PublickeyAuthenticator.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/auth/pubkey/PublickeyAuthenticator.java
@@ -18,8 +18,14 @@
  */
 package org.apache.sshd.server.auth.pubkey;
 
+import java.io.IOException;
+import java.security.GeneralSecurityException;
 import java.security.PublicKey;
+import java.util.Collection;
 
+import org.apache.sshd.common.config.keys.AuthorizedKeyEntry;
+import org.apache.sshd.common.config.keys.PublicKeyEntryResolver;
+import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.server.auth.AsyncAuthException;
 import org.apache.sshd.server.session.ServerSession;
 
@@ -42,4 +48,15 @@ public interface PublickeyAuthenticator {
      * @throws AsyncAuthException If the authentication is performed asynchronously
      */
     boolean authenticate(String username, PublicKey key, ServerSession session) throws AsyncAuthException;
+
+    static PublickeyAuthenticator fromAuthorizedEntries(
+                PublicKeyEntryResolver fallbackResolver, Collection<? extends AuthorizedKeyEntry> entries)
+            throws IOException, GeneralSecurityException {
+        Collection<PublicKey> keys = AuthorizedKeyEntry.resolveAuthorizedKeys(fallbackResolver, entries);
+        if (GenericUtils.isEmpty(keys)) {
+            return RejectAllPublickeyAuthenticator.INSTANCE;
+        } else {
+            return new KeySetPublickeyAuthenticator(keys);
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/server/config/SshServerConfigFileReader.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/config/SshServerConfigFileReader.java b/sshd-core/src/main/java/org/apache/sshd/server/config/SshServerConfigFileReader.java
index 381d2a3..bf4a958 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/config/SshServerConfigFileReader.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/config/SshServerConfigFileReader.java
@@ -22,6 +22,7 @@ import java.nio.file.Paths;
 
 import org.apache.sshd.common.PropertyResolver;
 import org.apache.sshd.common.PropertyResolverUtils;
+import org.apache.sshd.common.config.ConfigFileReaderSupport;
 import org.apache.sshd.common.config.SshConfigFileReader;
 import org.apache.sshd.common.helpers.AbstractFactoryManager;
 import org.apache.sshd.common.util.GenericUtils;
@@ -62,7 +63,8 @@ public final class SshServerConfigFileReader {
         throw new UnsupportedOperationException("No instance allowed");
     }
 
-    public static <S extends SshServer> S configure(S server, PropertyResolver props, boolean lenient, boolean ignoreUnsupported) {
+    public static <S extends SshServer> S configure(
+            S server, PropertyResolver props, boolean lenient, boolean ignoreUnsupported) {
         SshConfigFileReader.configure((AbstractFactoryManager) server, props, lenient, ignoreUnsupported);
         SshConfigFileReader.configureKeyExchanges(server, props, lenient, ServerBuilder.DH2KEX, ignoreUnsupported);
         return server;
@@ -80,27 +82,31 @@ public final class SshServerConfigFileReader {
     }
 
     public static AgentForwardingFilter resolveAgentForwardingFilter(PropertyResolver options) {
-        String value = PropertyResolverUtils.getStringProperty(options, ALLOW_AGENT_FORWARDING_CONFIG_PROP, DEFAULT_AGENT_FORWARDING);
-        return AgentForwardingFilter.of(SshConfigFileReader.parseBooleanValue(value));
+        String value = PropertyResolverUtils.getStringProperty(options,
+            ALLOW_AGENT_FORWARDING_CONFIG_PROP, DEFAULT_AGENT_FORWARDING);
+        return AgentForwardingFilter.of(ConfigFileReaderSupport.parseBooleanValue(value));
     }
 
     public static TcpForwardingFilter resolveTcpForwardingFilter(PropertyResolver options) {
-        String value = PropertyResolverUtils.getStringProperty(options, ALLOW_TCP_FORWARDING_CONFIG_PROP, DEFAULT_TCP_FORWARDING);
+        String value = PropertyResolverUtils.getStringProperty(options,
+            ALLOW_TCP_FORWARDING_CONFIG_PROP, DEFAULT_TCP_FORWARDING);
         TcpForwardingFilter filter = AllowTcpForwardingValue.fromString(value);
         ValidateUtils.checkNotNull(filter, "Unknown %s value: %s", ALLOW_TCP_FORWARDING_CONFIG_PROP, value);
         return filter;
     }
 
     public static X11ForwardingFilter resolveX11ForwardingFilter(PropertyResolver options) {
-        String value = PropertyResolverUtils.getStringProperty(options, ALLOW_X11_FORWARDING_CONFIG_PROP, DEFAULT_X11_FORWARDING);
-        return X11ForwardingFilter.of(SshConfigFileReader.parseBooleanValue(value));
+        String value = PropertyResolverUtils.getStringProperty(options,
+            ALLOW_X11_FORWARDING_CONFIG_PROP, DEFAULT_X11_FORWARDING);
+        return X11ForwardingFilter.of(ConfigFileReaderSupport.parseBooleanValue(value));
     }
 
     public static Object resolveBanner(PropertyResolver options) {
         String bannerOption = PropertyResolverUtils.getString(options, BANNER_CONFIG_PROP);
         if (GenericUtils.isEmpty(bannerOption)) {
-            bannerOption = PropertyResolverUtils.getStringProperty(options, VISUAL_HOST_KEY, DEFAULT_VISUAL_HOST_KEY);
-            if (SshConfigFileReader.parseBooleanValue(bannerOption)) {
+            bannerOption = PropertyResolverUtils.getStringProperty(options,
+                VISUAL_HOST_KEY, DEFAULT_VISUAL_HOST_KEY);
+            if (ConfigFileReaderSupport.parseBooleanValue(bannerOption)) {
                 bannerOption = ServerAuthenticationManager.AUTO_WELCOME_BANNER_VALUE;
             } else {
                 bannerOption = null;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/server/config/keys/AuthorizedKeysAuthenticator.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/config/keys/AuthorizedKeysAuthenticator.java b/sshd-core/src/main/java/org/apache/sshd/server/config/keys/AuthorizedKeysAuthenticator.java
index cfc7ba1..b05837b 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/config/keys/AuthorizedKeysAuthenticator.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/config/keys/AuthorizedKeysAuthenticator.java
@@ -131,7 +131,9 @@ public class AuthorizedKeysAuthenticator extends ModifiableFileWatcher implement
             if (exists()) {
                 Collection<AuthorizedKeyEntry> entries = reloadAuthorizedKeys(path, username, session);
                 if (GenericUtils.size(entries) > 0) {
-                    delegateHolder.set(AuthorizedKeyEntry.fromAuthorizedEntries(getFallbackPublicKeyEntryResolver(), entries));
+                    PublickeyAuthenticator authDelegate =
+                        PublickeyAuthenticator.fromAuthorizedEntries(getFallbackPublicKeyEntryResolver(), entries);
+                    delegateHolder.set(authDelegate);
                 }
             } else {
                 log.info("resolvePublickeyAuthenticator(" + username + ")[" + session + "] no authorized keys file at " + path);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/server/keyprovider/AbstractGeneratorHostKeyProvider.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/keyprovider/AbstractGeneratorHostKeyProvider.java b/sshd-core/src/main/java/org/apache/sshd/server/keyprovider/AbstractGeneratorHostKeyProvider.java
deleted file mode 100644
index 9131f99..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/server/keyprovider/AbstractGeneratorHostKeyProvider.java
+++ /dev/null
@@ -1,293 +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.server.keyprovider;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.file.Files;
-import java.nio.file.LinkOption;
-import java.nio.file.OpenOption;
-import java.nio.file.Path;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.security.PublicKey;
-import java.security.spec.AlgorithmParameterSpec;
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
-import java.util.concurrent.atomic.AtomicReference;
-
-import org.apache.sshd.common.cipher.ECCurves;
-import org.apache.sshd.common.config.keys.BuiltinIdentities;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.keyprovider.AbstractKeyPairProvider;
-import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * Holds a <U>single</U> {@link KeyPair} which is generated the 1st time
- * {@link #loadKeys()} is called. If there is a file backing it up and the
- * file exists, the key is loaded from it. Otherwise a new key pair is
- * generated and saved (provided a path is configured and {@link #isOverwriteAllowed()}
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class AbstractGeneratorHostKeyProvider extends AbstractKeyPairProvider {
-    public static final String DEFAULT_ALGORITHM = KeyUtils.RSA_ALGORITHM;
-    public static final boolean DEFAULT_ALLOWED_TO_OVERWRITE = true;
-
-    private final AtomicReference<KeyPair> keyPairHolder = new AtomicReference<>();
-
-    private Path path;
-    private String algorithm = DEFAULT_ALGORITHM;
-    private int keySize;
-    private AlgorithmParameterSpec keySpec;
-    private boolean overwriteAllowed = DEFAULT_ALLOWED_TO_OVERWRITE;
-
-    protected AbstractGeneratorHostKeyProvider() {
-        super();
-    }
-
-    public Path getPath() {
-        return path;
-    }
-
-    public void setFile(File file) {
-        setPath((file == null) ? null : file.toPath());
-    }
-
-    public void setPath(Path path) {
-        this.path = (path == null) ? null : path.toAbsolutePath();
-    }
-
-    public String getAlgorithm() {
-        return algorithm;
-    }
-
-    public void setAlgorithm(String algorithm) {
-        this.algorithm = algorithm;
-    }
-
-    public int getKeySize() {
-        return keySize;
-    }
-
-    public void setKeySize(int keySize) {
-        this.keySize = keySize;
-    }
-
-    public AlgorithmParameterSpec getKeySpec() {
-        return keySpec;
-    }
-
-    public void setKeySpec(AlgorithmParameterSpec keySpec) {
-        this.keySpec = keySpec;
-    }
-
-    public boolean isOverwriteAllowed() {
-        return overwriteAllowed;
-    }
-
-    public void setOverwriteAllowed(boolean overwriteAllowed) {
-        this.overwriteAllowed = overwriteAllowed;
-    }
-
-    public void clearLoadedKeys() {
-        KeyPair kp;
-        synchronized (keyPairHolder) {
-            kp = keyPairHolder.getAndSet(null);
-        }
-
-        if ((kp != null) & log.isDebugEnabled()) {
-            PublicKey key = kp.getPublic();
-            log.debug("clearLoadedKeys({}) removed key={}-{}",
-                      getPath(), KeyUtils.getKeyType(key), KeyUtils.getFingerPrint(key));
-        }
-    }
-
-    @Override   // co-variant return
-    public synchronized List<KeyPair> loadKeys() {
-        Path keyPath = getPath();
-        KeyPair kp;
-        synchronized (keyPairHolder) {
-            kp = keyPairHolder.get();
-            if (kp == null) {
-                try {
-                    kp = resolveKeyPair(keyPath);
-                    if (kp != null) {
-                        keyPairHolder.set(kp);
-                    }
-                } catch (Throwable t) {
-                    log.warn("loadKeys({}) Failed ({}) to resolve: {}",
-                            keyPath, t.getClass().getSimpleName(), t.getMessage());
-                    if (log.isDebugEnabled()) {
-                        log.debug("loadKeys(" + keyPath + ") resolution failure details", t);
-                    }
-                }
-            }
-        }
-
-        if (kp == null) {
-            return Collections.emptyList();
-        } else {
-            return Collections.singletonList(kp);
-        }
-    }
-
-    protected KeyPair resolveKeyPair(Path keyPath) throws IOException, GeneralSecurityException {
-        String alg = getAlgorithm();
-        KeyPair kp;
-        if (keyPath != null) {
-            try {
-                kp = loadFromFile(alg, keyPath);
-                if (kp != null) {
-                    return kp;
-                }
-            } catch (Throwable e) {
-                log.warn("resolveKeyPair({}) Failed ({}) to load: {}",
-                        keyPath, e.getClass().getSimpleName(), e.getMessage());
-                if (log.isDebugEnabled()) {
-                    log.debug("resolveKeyPair(" + keyPath + ") load failure details", e);
-                }
-            }
-        }
-
-        // either no file specified or no key in file
-        try {
-            kp = generateKeyPair(alg);
-            if (kp == null) {
-                return null;
-            }
-
-            if (log.isDebugEnabled()) {
-                PublicKey key = kp.getPublic();
-                log.debug("resolveKeyPair({}) generated {} key={}-{}",
-                          keyPath, alg, KeyUtils.getKeyType(key), KeyUtils.getFingerPrint(key));
-            }
-        } catch (Throwable e) {
-            log.warn("resolveKeyPair({})[{}] Failed ({}) to generate {} key-pair: {}",
-                     keyPath, alg, e.getClass().getSimpleName(), alg, e.getMessage());
-            if (log.isDebugEnabled()) {
-                log.debug("resolveKeyPair(" + keyPath + ")[" + alg + "] key-pair generation failure details", e);
-            }
-
-            return null;
-        }
-
-        if (keyPath != null) {
-            try {
-                writeKeyPair(kp, keyPath);
-            } catch (Throwable e) {
-                log.warn("resolveKeyPair({})[{}] Failed ({}) to write {} key: {}",
-                         alg, keyPath, e.getClass().getSimpleName(), alg, e.getMessage());
-                if (log.isDebugEnabled()) {
-                    log.debug("resolveKeyPair(" + keyPath + ")[" + alg + "] write failure details", e);
-                }
-            }
-        }
-
-        return kp;
-    }
-
-    protected KeyPair loadFromFile(String alg, Path keyPath) throws IOException, GeneralSecurityException {
-        LinkOption[] options = IoUtils.getLinkOptions(true);
-        if ((!Files.exists(keyPath, options)) || (!Files.isRegularFile(keyPath, options))) {
-            return null;
-        }
-
-        KeyPair kp = readKeyPair(keyPath, IoUtils.EMPTY_OPEN_OPTIONS);
-        if (kp == null) {
-            return null;
-        }
-
-        PublicKey key = kp.getPublic();
-        String keyAlgorithm = key.getAlgorithm();
-        if (BuiltinIdentities.Constants.ECDSA.equalsIgnoreCase(keyAlgorithm)) {
-            keyAlgorithm = KeyUtils.EC_ALGORITHM;
-        } else if (BuiltinIdentities.Constants.ED25519.equalsIgnoreCase(keyAlgorithm)) {
-            keyAlgorithm = SecurityUtils.EDDSA;
-        }
-
-        if (Objects.equals(alg, keyAlgorithm)) {
-            if (log.isDebugEnabled()) {
-                log.debug("resolveKeyPair({}) loaded key={}-{}",
-                          keyPath, KeyUtils.getKeyType(key), KeyUtils.getFingerPrint(key));
-            }
-            return kp;
-        }
-
-        // Not same algorithm - start again
-        if (log.isDebugEnabled()) {
-            log.debug("resolveKeyPair({}) mismatched loaded key algorithm: expected={}, loaded={}",
-                      keyPath, alg, keyAlgorithm);
-        }
-        Files.deleteIfExists(keyPath);
-        return null;
-    }
-
-    protected KeyPair readKeyPair(Path keyPath, OpenOption... options) throws IOException, GeneralSecurityException {
-        try (InputStream inputStream = Files.newInputStream(keyPath, options)) {
-            return doReadKeyPair(keyPath.toString(), inputStream);
-        }
-    }
-
-    protected KeyPair doReadKeyPair(String resourceKey, InputStream inputStream) throws IOException, GeneralSecurityException {
-        return SecurityUtils.loadKeyPairIdentity(resourceKey, inputStream, null);
-    }
-
-    protected void writeKeyPair(KeyPair kp, Path keyPath, OpenOption... options) throws IOException, GeneralSecurityException {
-        if ((!Files.exists(keyPath)) || isOverwriteAllowed()) {
-            try (OutputStream os = Files.newOutputStream(keyPath, options)) {
-                doWriteKeyPair(keyPath.toString(), kp, os);
-            } catch (Throwable e) {
-                log.warn("writeKeyPair({}) failed ({}) to write key {}: {}",
-                         keyPath, e.getClass().getSimpleName(), e.getMessage());
-                if (log.isDebugEnabled()) {
-                    log.debug("writeKeyPair(" + keyPath + ") write failure details", e);
-                }
-            }
-        } else {
-            log.error("Overwriting key ({}) is disabled: using throwaway {}: {}",
-                      keyPath, KeyUtils.getKeyType(kp), KeyUtils.getFingerPrint((kp == null) ? null : kp.getPublic()));
-        }
-    }
-
-    protected abstract void doWriteKeyPair(String resourceKey, KeyPair kp, OutputStream outputStream) throws IOException, GeneralSecurityException;
-
-    protected KeyPair generateKeyPair(String algorithm) throws GeneralSecurityException {
-        KeyPairGenerator generator = SecurityUtils.getKeyPairGenerator(algorithm);
-        if (keySpec != null) {
-            generator.initialize(keySpec);
-            log.info("generateKeyPair(" + algorithm + ") generating host key - spec=" + keySpec.getClass().getSimpleName());
-        } else if (keySize != 0) {
-            generator.initialize(keySize);
-            log.info("generateKeyPair(" + algorithm + ") generating host key - size=" + keySize);
-        } else if (KeyUtils.EC_ALGORITHM.equals(algorithm)) {
-            // If left to our own devices choose the biggest key size possible
-            int numCurves = ECCurves.SORTED_KEY_SIZE.size();
-            ECCurves curve = ECCurves.SORTED_KEY_SIZE.get(numCurves - 1);
-            generator.initialize(curve.getParameters());
-        }
-
-        return generator.generateKeyPair();
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProvider.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProvider.java b/sshd-core/src/main/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProvider.java
deleted file mode 100644
index 3bccde8..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProvider.java
+++ /dev/null
@@ -1,67 +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.server.keyprovider;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.io.OutputStream;
-import java.nio.file.Path;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.security.spec.InvalidKeySpecException;
-
-/**
- * TODO Add javadoc
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class SimpleGeneratorHostKeyProvider extends AbstractGeneratorHostKeyProvider {
-    public SimpleGeneratorHostKeyProvider() {
-        super();
-    }
-
-    public SimpleGeneratorHostKeyProvider(File file) {
-        this((file == null) ? null : file.toPath());
-    }
-
-    public SimpleGeneratorHostKeyProvider(Path path) {
-        setPath(path);
-    }
-
-    @Override
-    protected KeyPair doReadKeyPair(String resourceKey, InputStream inputStream) throws IOException, GeneralSecurityException {
-        try (ObjectInputStream r = new ObjectInputStream(inputStream)) {
-            try {
-                return (KeyPair) r.readObject();
-            } catch (ClassNotFoundException e) {
-                throw new InvalidKeySpecException("Missing classes: " + e.getMessage(), e);
-            }
-        }
-    }
-
-    @Override
-    protected void doWriteKeyPair(String resourceKey, KeyPair kp, OutputStream outputStream) throws IOException, GeneralSecurityException {
-        try (ObjectOutputStream w = new ObjectOutputStream(outputStream)) {
-            w.writeObject(kp);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/KeepAliveTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/KeepAliveTest.java b/sshd-core/src/test/java/org/apache/sshd/KeepAliveTest.java
index 774620b..906df28 100644
--- a/sshd-core/src/test/java/org/apache/sshd/KeepAliveTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/KeepAliveTest.java
@@ -35,9 +35,9 @@ import org.apache.sshd.common.channel.Channel;
 import org.apache.sshd.server.SshServer;
 import org.apache.sshd.server.command.Command;
 import org.apache.sshd.util.test.BaseTestSupport;
+import org.apache.sshd.util.test.CoreTestSupportUtils;
 import org.apache.sshd.util.test.EchoShell;
 import org.apache.sshd.util.test.EchoShellFactory;
-import org.apache.sshd.util.test.Utils;
 import org.junit.After;
 import org.junit.AfterClass;
 import org.junit.Before;
@@ -65,12 +65,12 @@ public class KeepAliveTest extends BaseTestSupport {
 
     @BeforeClass
     public static void setupClientAndServer() throws Exception {
-        sshd = Utils.setupTestServer(KeepAliveTest.class);
+        sshd = CoreTestSupportUtils.setupTestServer(KeepAliveTest.class);
         sshd.setShellFactory(new TestEchoShellFactory());
         sshd.start();
         port = sshd.getPort();
 
-        client = Utils.setupTestClient(KeepAliveTest.class);
+        client = CoreTestSupportUtils.setupTestClient(KeepAliveTest.class);
         client.start();
     }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/agent/AgentTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/agent/AgentTest.java b/sshd-core/src/test/java/org/apache/sshd/agent/AgentTest.java
index 7deebfc..dffb00b 100644
--- a/sshd-core/src/test/java/org/apache/sshd/agent/AgentTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/agent/AgentTest.java
@@ -43,9 +43,9 @@ import org.apache.sshd.server.SshServer;
 import org.apache.sshd.server.command.Command;
 import org.apache.sshd.server.forward.AcceptAllForwardingFilter;
 import org.apache.sshd.util.test.BaseTestSupport;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
 import org.apache.sshd.util.test.EchoShell;
 import org.apache.sshd.util.test.EchoShellFactory;
-import org.apache.sshd.util.test.Utils;
 import org.junit.Assume;
 import org.junit.BeforeClass;
 import org.junit.FixMethodOrder;
@@ -109,7 +109,7 @@ public class AgentTest extends BaseTestSupport {
         ProxyAgentFactory agentFactory = new ProxyAgentFactory();
         LocalAgentFactory localAgentFactory = new LocalAgentFactory();
         String username = getCurrentTestName();
-        KeyPair pair = Utils.createTestKeyPairProvider("dsaprivkey.pem").loadKey(KeyPairProvider.SSH_DSS);
+        KeyPair pair = CommonTestSupportUtils.createTestKeyPairProvider("dsaprivkey.pem").loadKey(KeyPairProvider.SSH_DSS);
         localAgentFactory.getAgent().addIdentity(pair, username);
 
         try (SshServer sshd1 = setupTestServer()) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/client/ClientSessionListenerTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/ClientSessionListenerTest.java b/sshd-core/src/test/java/org/apache/sshd/client/ClientSessionListenerTest.java
index 9989dd1..e5e928a 100644
--- a/sshd-core/src/test/java/org/apache/sshd/client/ClientSessionListenerTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/client/ClientSessionListenerTest.java
@@ -40,7 +40,7 @@ import org.apache.sshd.common.session.SessionListener;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.server.SshServer;
 import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.Utils;
+import org.apache.sshd.util.test.CoreTestSupportUtils;
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
 import org.junit.FixMethodOrder;
@@ -62,11 +62,11 @@ public class ClientSessionListenerTest extends BaseTestSupport {
 
     @BeforeClass
     public static void setupClientAndServer() throws Exception {
-        sshd = Utils.setupTestServer(ClientSessionListenerTest.class);
+        sshd = CoreTestSupportUtils.setupTestServer(ClientSessionListenerTest.class);
         sshd.start();
         port = sshd.getPort();
 
-        client = Utils.setupTestClient(ClientSessionListenerTest.class);
+        client = CoreTestSupportUtils.setupTestClient(ClientSessionListenerTest.class);
         client.start();
     }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/client/auth/PasswordIdentityProviderTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/auth/PasswordIdentityProviderTest.java b/sshd-core/src/test/java/org/apache/sshd/client/auth/PasswordIdentityProviderTest.java
deleted file mode 100644
index 4eaeed6..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/client/auth/PasswordIdentityProviderTest.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.client.auth;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Date;
-import java.util.LinkedList;
-import java.util.List;
-
-import org.apache.sshd.client.auth.password.PasswordIdentityProvider;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class PasswordIdentityProviderTest extends BaseTestSupport {
-    public PasswordIdentityProviderTest() {
-        super();
-    }
-
-    @Test
-    public void testMultiProvider() {
-        String[][] values = {
-            {getClass().getSimpleName(), getCurrentTestName()},
-            {new Date(System.currentTimeMillis()).toString()},
-            {getClass().getPackage().getName()}
-        };
-        List<String> expected = new ArrayList<>();
-        Collection<PasswordIdentityProvider> providers = new LinkedList<>();
-        for (String[] va : values) {
-            Collection<String> passwords = Arrays.asList(va);
-            expected.addAll(passwords);
-
-            PasswordIdentityProvider p = PasswordIdentityProvider.wrapPasswords(passwords);
-            assertProviderContents("Wrapped", p, passwords);
-            providers.add(p);
-        }
-
-        PasswordIdentityProvider p = PasswordIdentityProvider.multiProvider(providers);
-        assertProviderContents("Multi", p, expected);
-    }
-
-    private static void assertProviderContents(String message, PasswordIdentityProvider p, Iterable<String> expected) {
-        assertNotNull(message + ": no provider", p);
-        assertEquals(message, expected, p.loadPasswords());
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/client/channel/ChannelExecTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/channel/ChannelExecTest.java b/sshd-core/src/test/java/org/apache/sshd/client/channel/ChannelExecTest.java
index 038ce55..6bc5213 100644
--- a/sshd-core/src/test/java/org/apache/sshd/client/channel/ChannelExecTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/client/channel/ChannelExecTest.java
@@ -28,7 +28,7 @@ import org.apache.sshd.client.session.ClientSession;
 import org.apache.sshd.server.SshServer;
 import org.apache.sshd.util.test.BaseTestSupport;
 import org.apache.sshd.util.test.CommandExecutionHelper;
-import org.apache.sshd.util.test.Utils;
+import org.apache.sshd.util.test.CoreTestSupportUtils;
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
 import org.junit.FixMethodOrder;
@@ -50,7 +50,7 @@ public class ChannelExecTest extends BaseTestSupport {
 
     @BeforeClass
     public static void setupClientAndServer() throws Exception {
-        sshd = Utils.setupTestServer(ChannelExecTest.class);
+        sshd = CoreTestSupportUtils.setupTestServer(ChannelExecTest.class);
         sshd.setCommandFactory(command -> new CommandExecutionHelper(command) {
             @Override
             protected boolean handleCommandLine(String command) throws Exception {
@@ -63,7 +63,7 @@ public class ChannelExecTest extends BaseTestSupport {
         sshd.start();
         port = sshd.getPort();
 
-        client = Utils.setupTestClient(ChannelExecTest.class);
+        client = CoreTestSupportUtils.setupTestClient(ChannelExecTest.class);
         client.start();
     }
 


[30/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common utilities code from sshd-core into sshd-common (new artifact)

Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/io/IoUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/io/IoUtilsTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/io/IoUtilsTest.java
new file mode 100644
index 0000000..a94811d
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/io/IoUtilsTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.nio.file.LinkOption;
+
+import org.apache.sshd.common.util.NumberUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class IoUtilsTest extends JUnitTestSupport {
+    public IoUtilsTest() {
+        super();
+    }
+
+    @Test
+    public void testFollowLinks() {
+        assertTrue("Null ?", IoUtils.followLinks((LinkOption[]) null));
+        assertTrue("Empty ?", IoUtils.followLinks(IoUtils.EMPTY_LINK_OPTIONS));
+        assertFalse("No-follow ?", IoUtils.followLinks(IoUtils.getLinkOptions(false)));
+    }
+
+    @Test
+    public void testGetEOLBytes() {
+        byte[] expected = IoUtils.getEOLBytes();
+        assertTrue("Empty bytes", NumberUtils.length(expected) > 0);
+
+        for (int index = 1; index < Byte.SIZE; index++) {
+            byte[] actual = IoUtils.getEOLBytes();
+            assertNotSame("Same bytes received at iteration " + index, expected, actual);
+            assertArrayEquals("Mismatched bytes at iteration " + index, expected, actual);
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/io/LimitInputStreamTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/io/LimitInputStreamTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/io/LimitInputStreamTest.java
new file mode 100644
index 0000000..e009527
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/io/LimitInputStreamTest.java
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class LimitInputStreamTest extends JUnitTestSupport {
+    public LimitInputStreamTest() {
+        super();
+    }
+
+    @Test
+    public void testReadLimit() throws IOException {
+        Path targetPath = detectTargetFolder();
+        Path rootFolder = assertHierarchyTargetFolderExists(targetPath.resolve(getClass().getSimpleName()));
+        Path inputFile = rootFolder.resolve(getCurrentTestName() + ".bin");
+        byte[] data = (getClass().getName() + "#" + getCurrentTestName()).getBytes(StandardCharsets.UTF_8);
+        Files.write(inputFile, data);
+
+        try (InputStream in = Files.newInputStream(inputFile)) {
+            int maxLen = data.length / 2;
+            byte[] expected = new byte[maxLen];
+            System.arraycopy(data, 0, expected, 0, expected.length);
+
+            byte[] actual = new byte[expected.length];
+            try (LimitInputStream limited = new LimitInputStream(in, expected.length)) {
+                assertTrue("Limited stream not marked as open", limited.isOpen());
+                assertEquals("Mismatched initial available data size", expected.length, limited.available());
+
+                int readLen = limited.read(actual);
+                assertEquals("Incomplete actual data read", actual.length, readLen);
+                assertArrayEquals("Mismatched read data", expected, actual);
+                assertEquals("Mismatched remaining available data size", 0, limited.available());
+
+                readLen = limited.read();
+                assertTrue("Unexpected success to read one more byte: " + readLen, readLen < 0);
+
+                readLen = limited.read(actual);
+                assertTrue("Unexpected success to read extra buffer: " + readLen, readLen < 0);
+
+                limited.close();
+                assertFalse("Limited stream still marked as open", limited.isOpen());
+
+                try {
+                    readLen = limited.read();
+                    fail("Unexpected one byte read success after close");
+                } catch (IOException e) {
+                    // expected
+                }
+
+                try {
+                    readLen = limited.read(actual);
+                    fail("Unexpected buffer read success after close: " + readLen);
+                } catch (IOException e) {
+                    // expected
+                }
+
+                try {
+                    readLen = limited.read(actual);
+                    fail("Unexpected buffer read success after close: " + readLen);
+                } catch (IOException e) {
+                    // expected
+                }
+
+                try {
+                    readLen = (int) limited.skip(Byte.SIZE);
+                    fail("Unexpected skip success after close: " + readLen);
+                } catch (IOException e) {
+                    // expected
+                }
+
+                try {
+                    readLen = limited.available();
+                    fail("Unexpected available success after close: " + readLen);
+                } catch (IOException e) {
+                    // expected
+                }
+            }
+
+            // make sure underlying stream not closed
+            int readLen = in.read(actual);
+            assertEquals("Incomplete extra data read", Math.min(actual.length, data.length - expected.length), readLen);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/io/ModifiableFileWatcherTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/io/ModifiableFileWatcherTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/io/ModifiableFileWatcherTest.java
new file mode 100644
index 0000000..05343af
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/io/ModifiableFileWatcherTest.java
@@ -0,0 +1,82 @@
+/*
+ * 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.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.attribute.PosixFilePermission;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Map;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.OsUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class ModifiableFileWatcherTest extends JUnitTestSupport {
+    public ModifiableFileWatcherTest() {
+        super();
+    }
+
+    @Test   // see SSHD-606
+    public void testValidateStrictConfigFilePermissions() throws IOException {
+        Path file = getTempTargetRelativeFile(getClass().getSimpleName(), getCurrentTestName());
+        outputDebugMessage("%s deletion result=%s", file, Files.deleteIfExists(file));
+        assertNull("Unexpected violation for non-existent file: " + file, ModifiableFileWatcher.validateStrictConfigFilePermissions(file));
+
+        assertHierarchyTargetFolderExists(file.getParent());
+        try (OutputStream output = Files.newOutputStream(file)) {
+            output.write((getClass().getName() + "#" + getCurrentTestName() + "@" + new Date(System.currentTimeMillis())).getBytes(StandardCharsets.UTF_8));
+        }
+
+        Collection<PosixFilePermission> perms = IoUtils.getPermissions(file);
+        if (GenericUtils.isEmpty(perms)) {
+            assertNull("Unexpected violation for no permissions file: " + file, ModifiableFileWatcher.validateStrictConfigFilePermissions(file));
+        } else if (OsUtils.isUNIX()) {
+            Map.Entry<String, Object> violation = null;
+            for (PosixFilePermission p : ModifiableFileWatcher.STRICTLY_PROHIBITED_FILE_PERMISSION) {
+                if (perms.contains(p)) {
+                    violation = ModifiableFileWatcher.validateStrictConfigFilePermissions(file);
+                    assertNotNull("Unexpected success for permission=" + p + " of file " + file + " permissions=" + perms, violation);
+                    break;
+                }
+            }
+
+            if (violation == null) {    // we do not expected a failure if no permissions have been violated
+                assertNull("Unexpected UNIX violation for file " + file + " permissions=" + perms, ModifiableFileWatcher.validateStrictConfigFilePermissions(file));
+            }
+        } else {
+            assertNull("Unexpected Windows violation for file " + file + " permissions=" + perms, ModifiableFileWatcher.validateStrictConfigFilePermissions(file));
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseInputStreamTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseInputStreamTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseInputStreamTest.java
new file mode 100644
index 0000000..564dc42
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseInputStreamTest.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Date;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class NoCloseInputStreamTest extends JUnitTestSupport {
+    public NoCloseInputStreamTest() {
+        super();
+    }
+
+    @Test
+    public void testCanKeepReadingAfterClose() throws IOException {
+        byte[] expected = (getClass().getName() + "#" + getCurrentTestName() + "@" + new Date()).getBytes(StandardCharsets.UTF_8);
+        Path dir = createTempClassFolder();
+        Path file = Files.write(dir.resolve(getCurrentTestName() + ".txt"), expected);
+        try (InputStream fileStream = Files.newInputStream(file);
+             InputStream shielded = new NoCloseInputStream(fileStream)) {
+            int index = 0;
+
+            for (; index < (expected.length / 2); index++) {
+                shielded.close();
+
+                int readValue = shielded.read();
+                if (readValue == -1) {
+                    fail("Premature EOF after shield read of " + index + " bytes");
+                }
+
+                byte expValue = expected[index];
+                byte actValue = (byte) (readValue & 0xFF);
+                if (expValue != actValue) {
+                    fail("Mismatched shielded read value after " + index + " bytes");
+                }
+            }
+
+            for (; index < expected.length; index++) {
+                int readValue = fileStream.read();
+                if (readValue == -1) {
+                    fail("Premature EOF after original read of " + index + " bytes");
+                }
+                byte expValue = expected[index];
+                byte actValue = (byte) (readValue & 0xFF);
+                if (expValue != actValue) {
+                    fail("Mismatched original read value after " + index + " bytes");
+                }
+            }
+
+            int readValue = shielded.read();
+            assertEquals("Shielded EOF not signalled", -1, readValue);
+
+            readValue = fileStream.read();
+            assertEquals("Original EOF not signalled", -1, readValue);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseOutputStreamTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseOutputStreamTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseOutputStreamTest.java
new file mode 100644
index 0000000..f01f132
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseOutputStreamTest.java
@@ -0,0 +1,69 @@
+/*
+ * 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.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Date;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class NoCloseOutputStreamTest extends JUnitTestSupport {
+    public NoCloseOutputStreamTest() {
+        super();
+    }
+
+    @Test
+    public void testCanKeepWritingAfterClose() throws IOException {
+        Path dir = createTempClassFolder();
+        Path file = dir.resolve(getCurrentTestName() + ".txt");
+        Files.deleteIfExists(file);
+
+        String expectedOutput = getClass().getName() + "#" + getCurrentTestName() + "@" + new Date();
+        byte[] expected = expectedOutput.getBytes(StandardCharsets.UTF_8);
+        try (OutputStream fileStream = Files.newOutputStream(file);
+             OutputStream shielded = new NoCloseOutputStream(fileStream)) {
+            int index = 0;
+            for (; index < (expected.length / 2); index++) {
+                shielded.close();
+                shielded.write(expected[index] & 0xFF);
+            }
+
+            fileStream.write(expected, index, expected.length - index);
+        }
+
+        byte[] actual = Files.readAllBytes(file);
+        String actualOutput = new String(actual, StandardCharsets.UTF_8);
+        assertEquals(expectedOutput, actualOutput);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseReaderTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseReaderTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseReaderTest.java
new file mode 100644
index 0000000..14a72fc
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseReaderTest.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Date;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class NoCloseReaderTest extends JUnitTestSupport {
+    public NoCloseReaderTest() {
+        super();
+    }
+
+    @Test
+    public void testCanKeepReadingAfterClose() throws IOException {
+        String expected = getClass().getName() + "#" + getCurrentTestName() + "@" + new Date();
+        Path dir = createTempClassFolder();
+        Path file = Files.write(dir.resolve(getCurrentTestName() + ".txt"), expected.getBytes(StandardCharsets.UTF_8));
+        try (InputStream fileStream = Files.newInputStream(file);
+             Reader rdr = new InputStreamReader(fileStream, StandardCharsets.UTF_8);
+             Reader shielded = new NoCloseReader(rdr)) {
+            int index = 0;
+
+            int availLen = expected.length();
+            for (; index < (availLen / 2); index++) {
+                shielded.close();
+
+                int readValue = shielded.read();
+                if (readValue == -1) {
+                    fail("Premature EOF after shield read of " + index + " bytes");
+                }
+
+                char expValue = expected.charAt(index);
+                char actValue = (char) (readValue & 0xFFFF);
+                if (expValue != actValue) {
+                    fail("Mismatched shielded read value after " + index + " bytes");
+                }
+            }
+
+            for (; index < availLen; index++) {
+                int readValue = rdr.read();
+                if (readValue == -1) {
+                    fail("Premature EOF after original read of " + index + " bytes");
+                }
+
+                char expValue = expected.charAt(index);
+                char actValue = (char) (readValue & 0xFFFF);
+                if (expValue != actValue) {
+                    fail("Mismatched original read value after " + index + " bytes");
+                }
+            }
+
+            int readValue = shielded.read();
+            assertEquals("Shielded EOF not signalled", -1, readValue);
+
+            readValue = rdr.read();
+            assertEquals("Original EOF not signalled", -1, readValue);
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseWriterTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseWriterTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseWriterTest.java
new file mode 100644
index 0000000..b49678e
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseWriterTest.java
@@ -0,0 +1,73 @@
+/*
+ * 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.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Date;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class NoCloseWriterTest extends JUnitTestSupport {
+    public NoCloseWriterTest() {
+        super();
+    }
+
+    @Test
+    public void testCanKeepWritingAfterClose() throws IOException {
+        Path dir = createTempClassFolder();
+        Path file = dir.resolve(getCurrentTestName() + ".txt");
+        Files.deleteIfExists(file);
+
+        String expected = getClass().getName() + "#" + getCurrentTestName() + "@" + new Date();
+        try (OutputStream fileStream = Files.newOutputStream(file);
+             Writer w = new OutputStreamWriter(fileStream, StandardCharsets.UTF_8);
+                Writer shielded = new NoCloseWriter(w)) {
+            int index = 0;
+            int availLen = expected.length();
+            for (; index < (availLen / 2); index++) {
+                shielded.close();
+                shielded.write(expected.charAt(index));
+            }
+
+            w.write(expected, index, availLen - index);
+        }
+
+        byte[] actualBytes = Files.readAllBytes(file);
+        String actual = new String(actualBytes, StandardCharsets.UTF_8);
+        assertEquals(expected, actual);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/io/NullInputStreamTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/io/NullInputStreamTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/io/NullInputStreamTest.java
new file mode 100644
index 0000000..31982da
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/io/NullInputStreamTest.java
@@ -0,0 +1,118 @@
+/*
+ * 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.EOFException;
+import java.io.IOException;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class NullInputStreamTest extends JUnitTestSupport {
+    private static final NullInputStream INSTANCE = new NullInputStream();
+
+    public NullInputStreamTest() {
+        super();
+    }
+
+    @Test
+    public void testReadOneChar() throws IOException {
+        assertEquals(-1, INSTANCE.read());
+    }
+
+    @Test
+    public void testReadFullBuffer() throws IOException {
+        assertEquals(-1, INSTANCE.read(new byte[Byte.SIZE]));
+    }
+
+    @Test
+    public void testReadPartialBuffer() throws IOException {
+        byte[] buf = new byte[Byte.SIZE];
+        assertEquals(-1, INSTANCE.read(buf, buf.length / 2, (buf.length / 2) - 1));
+    }
+
+    @Test
+    public void testSkip() throws IOException {
+        assertEquals(0L, INSTANCE.skip(Long.SIZE));
+    }
+
+    @Test
+    public void testAvailable() throws IOException {
+        assertEquals(0, INSTANCE.available());
+    }
+
+    @Test
+    public void testNotAllowedToAccessAfterClose() throws IOException {
+        NullInputStream stream = new NullInputStream();
+        stream.close();
+        assertFalse("Stream not marked as closed", stream.isOpen());
+
+        try {
+            int nRead = stream.read();
+            fail("Unexpected single byte read: " + nRead);
+        } catch (EOFException e) {
+            // expected
+        }
+
+        byte[] buf = new byte[Byte.SIZE];
+        try {
+            int nRead = stream.read(buf);
+            fail("Unexpected full buffer read: " + nRead);
+        } catch (EOFException e) {
+            // expected
+        }
+
+        try {
+            int nRead = stream.read(buf, buf.length / 2, (buf.length / 2) - 1);
+            fail("Unexpected partial buffer read: " + nRead);
+        } catch (EOFException e) {
+            // expected
+        }
+
+        try {
+            long skip = stream.skip(Long.SIZE);
+            fail("Unexpected skip result: " + skip);
+        } catch (EOFException e) {
+            // expected
+        }
+
+        try {
+            int nRead = stream.available();
+            fail("Unexpected available count: " + nRead);
+        } catch (IOException e) {
+            // expected
+        }
+        try {
+            stream.reset();
+            fail("Unexpected reset success");
+        } catch (EOFException e) {
+            // expected
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/io/NullOutputStreamTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/io/NullOutputStreamTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/io/NullOutputStreamTest.java
new file mode 100644
index 0000000..0fe845f
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/io/NullOutputStreamTest.java
@@ -0,0 +1,80 @@
+/*
+ * 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.EOFException;
+import java.io.IOException;
+import java.util.Arrays;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class NullOutputStreamTest extends JUnitTestSupport {
+    public NullOutputStreamTest() {
+        super();
+    }
+
+    @Test
+    public void testNoAccessAllowedAfterClose() throws IOException {
+        NullOutputStream stream = new NullOutputStream();
+        stream.close();
+        assertFalse("Stream not marked as closed", stream.isOpen());
+
+        try {
+            stream.write('a');
+            fail("Unexpected single value write success");
+        } catch (EOFException e) {
+            // expected
+        }
+
+        byte[] buf = new byte[Byte.SIZE];
+        try {
+            Arrays.fill(buf, (byte) 0x41);
+            stream.write(buf);
+            fail("Unexpected full buffer write success");
+        } catch (EOFException e) {
+            // expected
+        }
+
+        try {
+            Arrays.fill(buf, (byte) 0x42);
+            stream.write(buf, buf.length / 2, (buf.length / 2) - 1);
+            fail("Unexpected partial buffer write success");
+        } catch (EOFException e) {
+            // expected
+        }
+
+        try {
+            stream.flush();
+            fail("Unexpected flush success");
+        } catch (EOFException e) {
+            // expected
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/io/der/ASN1ClassTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/io/der/ASN1ClassTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/io/der/ASN1ClassTest.java
new file mode 100644
index 0000000..5f30529
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/io/der/ASN1ClassTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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.der;
+
+import java.util.List;
+
+import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.junit.runners.Parameterized.UseParametersRunnerFactory;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RunWith(Parameterized.class)   // see https://github.com/junit-team/junit/wiki/Parameterized-tests
+@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
+@Category({ NoIoTestCase.class })
+public class ASN1ClassTest extends JUnitTestSupport {
+    private final ASN1Class expected;
+
+    public ASN1ClassTest(ASN1Class expected) {
+        this.expected = expected;
+    }
+
+    @Parameters(name = "{0}")
+    public static List<Object[]> parameters() {
+        return parameterize(ASN1Class.VALUES);
+    }
+
+    @Test
+    public void testFromName() {
+        String name = expected.name();
+        for (int index = 1, count = name.length(); index <= count; index++) {
+            assertSame(name, expected, ASN1Class.fromName(name));
+            name = shuffleCase(name);
+        }
+    }
+
+    @Test // NOTE: this also tests "fromTypeValue" since "fromDERValue" invokes it
+    public void testFromDERValue() {
+        assertSame(expected, ASN1Class.fromDERValue((expected.getClassValue() << 6) & 0xFF));
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/io/der/ASN1TypeTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/io/der/ASN1TypeTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/io/der/ASN1TypeTest.java
new file mode 100644
index 0000000..06034e2
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/io/der/ASN1TypeTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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.der;
+
+import java.util.List;
+
+import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.junit.runners.Parameterized.UseParametersRunnerFactory;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RunWith(Parameterized.class)   // see https://github.com/junit-team/junit/wiki/Parameterized-tests
+@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
+@Category({ NoIoTestCase.class })
+public class ASN1TypeTest extends JUnitTestSupport {
+    private final ASN1Type expected;
+
+    public ASN1TypeTest(ASN1Type expected) {
+        this.expected = expected;
+    }
+
+    @Parameters(name = "{0}")
+    public static List<Object[]> parameters() {
+        return parameterize(ASN1Type.VALUES);
+    }
+
+    @Test
+    public void testFromName() {
+        String name = expected.name();
+        for (int index = 1, count = name.length(); index <= count; index++) {
+            assertSame(name, expected, ASN1Type.fromName(name));
+            name = shuffleCase(name);
+        }
+    }
+
+    @Test
+    public void testFromTypeValue() {
+        assertSame(expected, ASN1Type.fromTypeValue(expected.getTypeValue()));
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/io/der/DERParserTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/io/der/DERParserTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/io/der/DERParserTest.java
new file mode 100644
index 0000000..ecc5b31
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/io/der/DERParserTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.der;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.StreamCorruptedException;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class DERParserTest extends JUnitTestSupport {
+    public DERParserTest() {
+        super();
+    }
+
+    @Test
+    public void testReadLengthConstraint() throws IOException {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        try {
+            try (DERWriter w = new DERWriter(baos)) {
+                w.writeLength(DERParser.MAX_DER_VALUE_LENGTH + 1);
+            }
+        } finally {
+            baos.close();
+        }
+
+        try (DERParser parser = new DERParser(baos.toByteArray())) {
+            int len = parser.readLength();
+            fail("Unexpected success: len=" + len);
+        } catch (StreamCorruptedException e) {
+            // expected ignored
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/io/der/DERWriterTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/io/der/DERWriterTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/io/der/DERWriterTest.java
new file mode 100644
index 0000000..3064caa
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/io/der/DERWriterTest.java
@@ -0,0 +1,64 @@
+/*
+ * 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.der;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class DERWriterTest extends JUnitTestSupport {
+    public DERWriterTest() {
+        super();
+    }
+
+    @Test
+    public void testWriteStripLeadingZeroes() throws IOException {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        try {
+            try (DERWriter w = new DERWriter(baos)) {
+                w.writeBigInteger(BigInteger.valueOf(-1));
+                w.writeBigInteger(BigInteger.valueOf(129));
+                w.writeBigInteger(new byte[] {0, 0}, 0, 2);
+                w.writeBigInteger(new byte[] {0, 1}, 0, 2);
+            }
+        } finally {
+            baos.close();
+        }
+
+        try (DERParser parser = new DERParser(baos.toByteArray())) {
+            assertEquals(BigInteger.valueOf(255), parser.readBigInteger());
+            assertEquals(BigInteger.valueOf(129), parser.readBigInteger());
+            assertEquals(BigInteger.valueOf(0), parser.readBigInteger());
+            assertEquals(BigInteger.valueOf(1), parser.readBigInteger());
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/net/SshdSocketIpv6AddressTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/net/SshdSocketIpv6AddressTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/net/SshdSocketIpv6AddressTest.java
new file mode 100644
index 0000000..a38db34
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/net/SshdSocketIpv6AddressTest.java
@@ -0,0 +1,88 @@
+/*
+ * 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.net;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.junit.runners.Parameterized.UseParametersRunnerFactory;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@RunWith(Parameterized.class)   // see https://github.com/junit-team/junit/wiki/Parameterized-tests
+@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class SshdSocketIpv6AddressTest extends JUnitTestSupport {
+    public static final List<String> VALID_ADDRESSES =
+        Collections.unmodifiableList(
+            Arrays.asList(
+                "2001:0db8:85a3:0000:0000:8a2e:0370:7334", "2001:db8:85a3:0:0:8a2e:370:7334",
+                "2001:db8:85a3::8a2e:370:7334",
+                "2001:0db8::0001", "2001:db8::1",
+                "2001:db8:0:0:0:0:2:1", "2001:db8::2:1",
+                "2001:db8:0000:1:1:1:1:1", "2001:db8:0:1:1:1:1:1",
+                "2001:db8:85a3:8d3:1319:8a2e:370:7348",
+                "fe80::1ff:fe23:4567:890a", "fe80::1ff:fe23:4567:890a%eth2",
+                "fe80::1ff:fe23:4567:890a%3", "fe80:3::1ff:fe23:4567:890a",
+                "::ffff:c000:0280", "::ffff:192.0.2.128"));
+
+    private final String address;
+    private final boolean matches;
+
+    public SshdSocketIpv6AddressTest(String address, boolean matches) {
+        this.address = address;
+        this.matches = matches;
+    }
+
+    @Parameters(name = "{0}")
+    public static List<Object[]> parameters() {
+        return Stream
+                .concat(SshdSocketAddress.WELL_KNOWN_IPV6_ADDRESSES.stream(), VALID_ADDRESSES.stream())
+                .map(address -> new Object[] {address, Boolean.TRUE})
+                .collect(Collectors.toList());
+    }
+
+    @Test
+    public void testIPv6AddressValidity() {
+        assertEquals(address, matches, SshdSocketAddress.isIPv6Address(address));
+    }
+
+    @Override
+    public String toString() {
+        return getClass().getSimpleName()
+            + "[address=" + address
+            + " , matches=" + matches
+            + "]";
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/security/SecurityProviderRegistrarCipherNameTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/security/SecurityProviderRegistrarCipherNameTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/security/SecurityProviderRegistrarCipherNameTest.java
new file mode 100644
index 0000000..5d40c59
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/security/SecurityProviderRegistrarCipherNameTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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.security;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.crypto.Cipher;
+
+import org.apache.sshd.common.cipher.BuiltinCiphers;
+import org.apache.sshd.common.cipher.CipherInformation;
+import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.junit.runners.Parameterized.UseParametersRunnerFactory;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RunWith(Parameterized.class)   // see https://github.com/junit-team/junit/wiki/Parameterized-tests
+@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
+@Category({ NoIoTestCase.class })
+public class SecurityProviderRegistrarCipherNameTest extends JUnitTestSupport {
+    private final CipherInformation cipherInfo;
+
+    public SecurityProviderRegistrarCipherNameTest(CipherInformation cipherInfo) {
+        this.cipherInfo = cipherInfo;
+    }
+
+    @Parameters(name = "{0}")
+    public static List<Object[]> parameters() {
+        List<Object[]> params = new ArrayList<>();
+        for (CipherInformation cipherInfo : BuiltinCiphers.VALUES) {
+            String algorithm = cipherInfo.getAlgorithm();
+            String xform = cipherInfo.getTransformation();
+            if (!xform.startsWith(algorithm)) {
+                continue;
+            }
+
+            params.add(new Object[]{cipherInfo});
+        }
+        return params;
+    }
+
+    @Test
+    public void testGetEffectiveSecurityEntityName() {
+        String expected = cipherInfo.getAlgorithm();
+        String actual = SecurityProviderRegistrar.getEffectiveSecurityEntityName(Cipher.class, cipherInfo.getTransformation());
+        assertEquals("Mismatched pure cipher name", expected, actual);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/security/SecurityProviderRegistrarTestSupport.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/security/SecurityProviderRegistrarTestSupport.java b/sshd-common/src/test/java/org/apache/sshd/common/util/security/SecurityProviderRegistrarTestSupport.java
new file mode 100644
index 0000000..9958916
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/security/SecurityProviderRegistrarTestSupport.java
@@ -0,0 +1,62 @@
+/*
+ * 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.security;
+
+import java.security.Provider;
+import java.util.Arrays;
+import java.util.Collection;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.experimental.categories.Category;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@Category({ NoIoTestCase.class })
+public abstract class SecurityProviderRegistrarTestSupport extends JUnitTestSupport {
+    protected SecurityProviderRegistrarTestSupport() {
+        super();
+    }
+
+    public static Provider testGetSecurityProviderCaching(String prefix, SecurityProviderRegistrar registrar) {
+        return testGetSecurityProviderCaching(prefix, registrar, registrar.getSecurityProvider());
+    }
+
+    public static <P extends Provider> P testGetSecurityProviderCaching(String prefix, SecurityProviderRegistrar registrar, P expected) {
+        for (int index = 1; index <= Byte.SIZE; index++) {
+            Provider actual = registrar.getSecurityProvider();
+            assertSame(prefix + ": Mismatched provider instance at invocation #" + index, expected, actual);
+        }
+
+        return expected;
+    }
+
+    public static void assertSecurityEntitySupportState(
+            String prefix, SecurityProviderRegistrar registrar, boolean expected, String name, Class<?>... entities) {
+        assertSecurityEntitySupportState(prefix, registrar, expected, name, Arrays.asList(entities));
+    }
+
+    public static void assertSecurityEntitySupportState(
+            String prefix, SecurityProviderRegistrar registrar, boolean expected, String name, Collection<Class<?>> entities) {
+        for (Class<?> entity : entities) {
+            assertEquals(prefix + "[" + entity.getSimpleName() + "]", expected, registrar.isSecurityEntitySupported(entity, name));
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/security/SecurityUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/security/SecurityUtilsTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/security/SecurityUtilsTest.java
new file mode 100644
index 0000000..46ca075
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/security/SecurityUtilsTest.java
@@ -0,0 +1,231 @@
+/*
+ * 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.security;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.interfaces.DSAPrivateKey;
+import java.security.interfaces.DSAPublicKey;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.sshd.common.cipher.BuiltinCiphers;
+import org.apache.sshd.common.cipher.ECCurves;
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.config.keys.loader.KeyPairResourceLoader;
+import org.apache.sshd.common.keyprovider.AbstractResourceKeyPairProvider;
+import org.apache.sshd.common.keyprovider.ClassLoadableResourceKeyPairProvider;
+import org.apache.sshd.common.keyprovider.FileKeyPairProvider;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.AfterClass;
+import org.junit.Assume;
+import org.junit.BeforeClass;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+@SuppressWarnings("checkstyle:MethodCount")
+public class SecurityUtilsTest extends JUnitTestSupport {
+    public static final String BC_NAMED_USAGE_PROP =
+            SecurityProviderRegistrar.CONFIG_PROP_BASE
+          + "." + SecurityUtils.BOUNCY_CASTLE
+          + "." + SecurityProviderRegistrar.NAMED_PROVIDER_PROPERTY;
+
+    private static final String DEFAULT_PASSWORD = "super secret passphrase";
+    private static final FilePasswordProvider TEST_PASSWORD_PROVIDER = file -> DEFAULT_PASSWORD;
+
+    public SecurityUtilsTest() {
+        super();
+    }
+
+    // NOTE: Using the BouncyCastle provider instead of the name does not work as expected so we take no chances
+    @BeforeClass
+    public static void useNamedBouncyCastleProvider() {
+        System.setProperty(BC_NAMED_USAGE_PROP, Boolean.TRUE.toString());
+    }
+
+    @AfterClass
+    public static void unsetBouncyCastleProviderUsagePreference() {
+        System.clearProperty(BC_NAMED_USAGE_PROP);
+    }
+
+    @Test
+    public void testLoadEncryptedDESPrivateKey() throws Exception {
+        testLoadEncryptedRSAPrivateKey("DES-EDE3");
+    }
+
+    @Test
+    public void testLoadEncryptedAESPrivateKey() {
+        for (BuiltinCiphers c : new BuiltinCiphers[]{
+            BuiltinCiphers.aes128cbc, BuiltinCiphers.aes192cbc, BuiltinCiphers.aes256cbc
+        }) {
+            if (!c.isSupported()) {
+                System.out.println("Skip unsupported encryption scheme: " + c.getName());
+                continue;
+            }
+
+            try {
+                testLoadEncryptedRSAPrivateKey("AES-" + c.getKeySize());
+            } catch (Exception e) {
+                fail("Failed (" + e.getClass().getSimpleName() + " to load key for " + c.getName() + ": " + e.getMessage());
+            }
+        }
+    }
+
+    private KeyPair testLoadEncryptedRSAPrivateKey(String algorithm) throws IOException, GeneralSecurityException {
+        return testLoadRSAPrivateKey(DEFAULT_PASSWORD.replace(' ', '-') + "-RSA-" + algorithm.toUpperCase() + "-key");
+    }
+
+    @Test
+    public void testLoadUnencryptedRSAPrivateKey() throws Exception {
+        testLoadRSAPrivateKey(getClass().getSimpleName() + "-RSA-KeyPair");
+    }
+
+    @Test
+    public void testLoadUnencryptedDSSPrivateKey() throws Exception {
+        testLoadDSSPrivateKey(getClass().getSimpleName() + "-DSA-KeyPair");
+    }
+
+    private KeyPair testLoadDSSPrivateKey(String name) throws Exception {
+        return testLoadPrivateKey(name, DSAPublicKey.class, DSAPrivateKey.class);
+    }
+
+    @Test
+    public void testLoadUnencryptedECPrivateKey() throws Exception {
+        Assume.assumeTrue("EC not supported", SecurityUtils.isECCSupported());
+        for (ECCurves c : ECCurves.VALUES) {
+            if (!c.isSupported()) {
+                System.out.println("Skip unsupported curve: " + c.getName());
+                continue;
+            }
+
+            testLoadECPrivateKey(getClass().getSimpleName() + "-EC-" + c.getKeySize() + "-KeyPair");
+        }
+    }
+
+    private KeyPair testLoadECPrivateKey(String name) throws IOException, GeneralSecurityException {
+        return testLoadPrivateKey(name, ECPublicKey.class, ECPrivateKey.class);
+    }
+
+    private KeyPair testLoadRSAPrivateKey(String name) throws IOException, GeneralSecurityException {
+        return testLoadPrivateKey(name, RSAPublicKey.class, RSAPrivateKey.class);
+    }
+
+    private KeyPair testLoadPrivateKey(String name, Class<? extends PublicKey> pubType, Class<? extends PrivateKey> prvType)
+            throws IOException, GeneralSecurityException {
+        Path folder = getTestResourcesFolder();
+        Path file = folder.resolve(name);
+        KeyPair kpFile = testLoadPrivateKeyFile(file, pubType, prvType);
+        if (SecurityUtils.isBouncyCastleRegistered()) {
+            KeyPairResourceLoader bcLoader = SecurityUtils.getBouncycastleKeyPairResourceParser();
+            Collection<KeyPair> kpList = bcLoader.loadKeyPairs(file, TEST_PASSWORD_PROVIDER);
+            assertEquals(name + ": Mismatched loaded BouncyCastle keys count", 1, GenericUtils.size(kpList));
+
+            KeyPair kpBC = kpList.iterator().next();
+            assertTrue(name + ": Mismatched BouncyCastle vs. file values", KeyUtils.compareKeyPairs(kpFile, kpBC));
+        }
+
+        Class<?> clazz = getClass();
+        Package pkg = clazz.getPackage();
+        KeyPair kpResource = testLoadPrivateKeyResource(pkg.getName().replace('.', '/') + "/" + name, pubType, prvType);
+        assertTrue(name + ": Mismatched key file vs. resource values", KeyUtils.compareKeyPairs(kpFile, kpResource));
+        return kpResource;
+    }
+
+    private static KeyPair testLoadPrivateKeyResource(String name, Class<? extends PublicKey> pubType, Class<? extends PrivateKey> prvType) {
+        return testLoadPrivateKey(name, new ClassLoadableResourceKeyPairProvider(name), pubType, prvType);
+    }
+
+    private static KeyPair testLoadPrivateKeyFile(Path file, Class<? extends PublicKey> pubType, Class<? extends PrivateKey> prvType) {
+        return testLoadPrivateKey(file.toString(), new FileKeyPairProvider(file), pubType, prvType);
+    }
+
+    private static KeyPair testLoadPrivateKey(String resourceKey, AbstractResourceKeyPairProvider<?> provider,
+            Class<? extends PublicKey> pubType, Class<? extends PrivateKey> prvType) {
+        provider.setPasswordFinder(TEST_PASSWORD_PROVIDER);
+        Iterable<KeyPair> iterator = provider.loadKeys();
+        List<KeyPair> pairs = new ArrayList<>();
+        for (KeyPair kp : iterator) {
+            pairs.add(kp);
+        }
+
+        assertEquals("Mismatched loaded pairs count for " + resourceKey, 1, pairs.size());
+
+        KeyPair kp = pairs.get(0);
+        PublicKey pub = kp.getPublic();
+        assertNotNull("No public key extracted", pub);
+        assertTrue("Not an " + pubType.getSimpleName() + " public key for " + resourceKey, pubType.isAssignableFrom(pub.getClass()));
+
+        PrivateKey prv = kp.getPrivate();
+        assertNotNull("No private key extracted", prv);
+        assertTrue("Not an " + prvType.getSimpleName() + " private key for " + resourceKey, prvType.isAssignableFrom(prv.getClass()));
+
+        return kp;
+    }
+
+    @Test
+    public void testSetMaxDHGroupExchangeKeySizeByProperty() {
+        try {
+            for (int expected = SecurityUtils.MIN_DHGEX_KEY_SIZE; expected <= SecurityUtils.MAX_DHGEX_KEY_SIZE; expected += 1024) {
+                SecurityUtils.setMaxDHGroupExchangeKeySize(0);  // force detection
+                try {
+                    System.setProperty(SecurityUtils.MAX_DHGEX_KEY_SIZE_PROP, Integer.toString(expected));
+                    assertTrue("DH group not supported for key size=" + expected, SecurityUtils.isDHGroupExchangeSupported());
+                    assertEquals("Mismatched values", expected, SecurityUtils.getMaxDHGroupExchangeKeySize());
+                } finally {
+                    System.clearProperty(SecurityUtils.MAX_DHGEX_KEY_SIZE_PROP);
+                }
+            }
+        } finally {
+            SecurityUtils.setMaxDHGroupExchangeKeySize(0);  // force detection
+        }
+    }
+
+    @Test
+    public void testSetMaxDHGroupExchangeKeySizeProgrammatically() {
+        try {
+            for (int expected = SecurityUtils.MIN_DHGEX_KEY_SIZE; expected <= SecurityUtils.MAX_DHGEX_KEY_SIZE; expected += 1024) {
+                SecurityUtils.setMaxDHGroupExchangeKeySize(expected);
+                assertTrue("DH group not supported for key size=" + expected, SecurityUtils.isDHGroupExchangeSupported());
+                assertEquals("Mismatched values", expected, SecurityUtils.getMaxDHGroupExchangeKeySize());
+            }
+        } finally {
+            SecurityUtils.setMaxDHGroupExchangeKeySize(0);  // force detection
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/security/eddsa/EDDSAProviderTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/security/eddsa/EDDSAProviderTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/security/eddsa/EDDSAProviderTest.java
new file mode 100644
index 0000000..7da51c5
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/security/eddsa/EDDSAProviderTest.java
@@ -0,0 +1,126 @@
+/*
+ * 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.security.eddsa;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.Signature;
+
+import org.apache.sshd.common.config.keys.AuthorizedKeyEntry;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
+import org.apache.sshd.common.util.security.SecurityUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.Assume;
+import org.junit.BeforeClass;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+import net.i2p.crypto.eddsa.EdDSAEngine;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class EDDSAProviderTest extends JUnitTestSupport {
+    private static KeyPair keyPair;
+
+    public EDDSAProviderTest() {
+        super();
+    }
+
+    @BeforeClass
+    public static void checkProviderSupported() throws GeneralSecurityException {
+        Assume.assumeTrue(SecurityUtils.EDDSA + " not supported", SecurityUtils.isEDDSACurveSupported());
+        KeyPairGenerator g = SecurityUtils.getKeyPairGenerator(SecurityUtils.EDDSA);
+        assertNotNull("No generator instance", g);
+
+        keyPair = g.generateKeyPair();
+        assertNotNull("No key pair generated", keyPair);
+
+        PublicKey pubKey = keyPair.getPublic();
+        assertNotNull("No public key", pubKey);
+        assertEquals("Mismatched public key algorithm", SecurityUtils.EDDSA, pubKey.getAlgorithm());
+        assertEquals("Mismatched public key type", KeyPairProvider.SSH_ED25519, KeyUtils.getKeyType(pubKey));
+
+        PrivateKey prvKey = keyPair.getPrivate();
+        assertNotNull("No private key", prvKey);
+        assertEquals("Mismatched key-pair algorithm", pubKey.getAlgorithm(), prvKey.getAlgorithm());
+        assertEquals("Mismatched private key type", KeyPairProvider.SSH_ED25519, KeyUtils.getKeyType(prvKey));
+    }
+
+    @Test
+    public void testSignature() throws GeneralSecurityException {
+        Signature s = SecurityUtils.getSignature(EdDSAEngine.SIGNATURE_ALGORITHM);
+        assertNotNull("No signature instance", s);
+        s.initSign(keyPair.getPrivate());
+
+        byte[] data = (getClass().getName() + "#" + getCurrentTestName()).getBytes(StandardCharsets.UTF_8);
+        s.update(data);
+        byte[] signed = s.sign();
+
+        s = SecurityUtils.getSignature(EdDSAEngine.SIGNATURE_ALGORITHM);
+        s.initVerify(keyPair.getPublic());
+        s.update(data);
+        assertTrue("Failed to verify", s.verify(signed));
+    }
+
+    @Test
+    public void testPublicKeyEntryDecoder() throws IOException, GeneralSecurityException {
+        String comment = getCurrentTestName() + "@" + getClass().getSimpleName();
+        String expected = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGPKSUTyz1HwHReFVvD5obVsALAgJRNarH4TRpNePnAS " + comment;
+        AuthorizedKeyEntry keyEntry = AuthorizedKeyEntry.parseAuthorizedKeyEntry(expected);
+        assertNotNull("No extracted key entry", keyEntry);
+
+        assertEquals("Mismatched key type", KeyPairProvider.SSH_ED25519, keyEntry.getKeyType());
+        assertEquals("Mismatched comment", comment, keyEntry.getComment());
+
+        StringBuilder sb = new StringBuilder(expected.length());
+        PublicKey pubKey = keyEntry.appendPublicKey(sb, null);
+        assertEquals("Mismatched encoded result", expected, sb.toString());
+
+        testPublicKeyRecovery(pubKey);
+    }
+
+    @Test
+    public void testGeneratedPublicKeyRecovery() throws IOException, GeneralSecurityException {
+        testPublicKeyRecovery(keyPair.getPublic());
+    }
+
+    private void testPublicKeyRecovery(PublicKey pubKey) throws IOException, GeneralSecurityException {
+        assertNotNull("No public key generated", pubKey);
+        assertEquals("Mismatched public key algorithm", SecurityUtils.EDDSA, pubKey.getAlgorithm());
+
+        Buffer buf = SecurityUtils.putRawEDDSAPublicKey(new ByteArrayBuffer(), pubKey);
+        PublicKey actual = buf.getRawPublicKey();
+        assertEquals("Mismatched key algorithm", pubKey.getAlgorithm(), actual.getAlgorithm());
+        assertEquals("Mismatched recovered key", pubKey, actual);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/security/eddsa/Ed25519VectorsTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/security/eddsa/Ed25519VectorsTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/security/eddsa/Ed25519VectorsTest.java
new file mode 100644
index 0000000..cfebb85
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/security/eddsa/Ed25519VectorsTest.java
@@ -0,0 +1,238 @@
+/*
+ * 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.security.eddsa;
+
+import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.sshd.common.signature.Signature;
+import org.apache.sshd.common.util.buffer.BufferUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.Assume;
+import org.junit.BeforeClass;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.junit.runners.Parameterized.UseParametersRunnerFactory;
+
+import net.i2p.crypto.eddsa.EdDSAPrivateKey;
+import net.i2p.crypto.eddsa.EdDSAPublicKey;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ * @see <A HREF="https://tools.ietf.org/html/draft-josefsson-eddsa-ed25519-02#section-6">
+ * EdDSA and Ed25519 draft-josefsson-eddsa-ed25519-02 - section 6 - Test Vectors for Ed25519</A>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RunWith(Parameterized.class)   // see https://github.com/junit-team/junit/wiki/Parameterized-tests
+@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
+@Category({ NoIoTestCase.class })
+public class Ed25519VectorsTest extends JUnitTestSupport {
+    private final byte[] prvBytes;
+    private final PrivateKey privateKey;
+    private final byte[] pubBytes;
+    private final PublicKey publicKey;
+    private final byte[] msgBytes;
+    private final byte[] expSignature;
+
+    public Ed25519VectorsTest(String name, String prvKey, String pubKey, String msg, String signature)
+            throws GeneralSecurityException {
+        prvBytes = BufferUtils.decodeHex(BufferUtils.EMPTY_HEX_SEPARATOR, prvKey);
+        privateKey = EdDSASecurityProviderUtils.generateEDDSAPrivateKey(prvBytes.clone());
+        pubBytes = BufferUtils.decodeHex(BufferUtils.EMPTY_HEX_SEPARATOR, pubKey);
+        publicKey = EdDSASecurityProviderUtils.generateEDDSAPublicKey(pubBytes.clone());
+        msgBytes = BufferUtils.decodeHex(BufferUtils.EMPTY_HEX_SEPARATOR, msg);
+        expSignature = BufferUtils.decodeHex(BufferUtils.EMPTY_HEX_SEPARATOR, signature);
+    }
+
+    @Parameters(name = "{0}")
+    @SuppressWarnings("checkstyle:anoninnerlength")
+    public static List<Object[]> parameters() {
+        return new ArrayList<>(Arrays.asList(
+                new Object[]{
+                    "TEST1 - empty message",
+                    "9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60",
+                    "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a",
+                    "",
+                    "e5564300c360ac729086e2cc806e828a"
+                      + "84877f1eb8e5d974d873e06522490155"
+                      + "5fb8821590a33bacc61e39701cf9b46b"
+                      + "d25bf5f0595bbe24655141438e7a100b"
+                },
+                new Object[]{
+                    "TEST2 - one byte",
+                    "4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb",
+                    "3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c",
+                    "72",
+                    "92a009a9f0d4cab8720e820b5f642540"
+                      + "a2b27b5416503f8fb3762223ebdb69da"
+                      + "085ac1e43e15996e458f3613d0f11d8c"
+                      + "387b2eaeb4302aeeb00d291612bb0c00"
+                },
+                new Object[]{
+                    "TEST3 - 2 bytes",
+                    "c5aa8df43f9f837bedb7442f31dcb7b166d38535076f094b85ce3a2e0b4458f7",
+                    "fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025",
+                    "af82",
+                    "6291d657deec24024827e69c3abe01a3"
+                      + "0ce548a284743a445e3680d7db5ac3ac"
+                      + "18ff9b538d16f290ae67f760984dc659"
+                      + "4a7c15e9716ed28dc027beceea1ec40a"
+                },
+                new Object[]{
+                    "TEST1024 - large message",
+                    "f5e5767cf153319517630f226876b86c8160cc583bc013744c6bf255f5cc0ee5",
+                    "278117fc144c72340f67d0f2316e8386ceffbf2b2428c9c51fef7c597f1d426e",
+                    "08b8b2b733424243760fe426a4b54908"
+                      + "632110a66c2f6591eabd3345e3e4eb98"
+                      + "fa6e264bf09efe12ee50f8f54e9f77b1"
+                      + "e355f6c50544e23fb1433ddf73be84d8"
+                      + "79de7c0046dc4996d9e773f4bc9efe57"
+                      + "38829adb26c81b37c93a1b270b20329d"
+                      + "658675fc6ea534e0810a4432826bf58c"
+                      + "941efb65d57a338bbd2e26640f89ffbc"
+                      + "1a858efcb8550ee3a5e1998bd177e93a"
+                      + "7363c344fe6b199ee5d02e82d522c4fe"
+                      + "ba15452f80288a821a579116ec6dad2b"
+                      + "3b310da903401aa62100ab5d1a36553e"
+                      + "06203b33890cc9b832f79ef80560ccb9"
+                      + "a39ce767967ed628c6ad573cb116dbef"
+                      + "efd75499da96bd68a8a97b928a8bbc10"
+                      + "3b6621fcde2beca1231d206be6cd9ec7"
+                      + "aff6f6c94fcd7204ed3455c68c83f4a4"
+                      + "1da4af2b74ef5c53f1d8ac70bdcb7ed1"
+                      + "85ce81bd84359d44254d95629e9855a9"
+                      + "4a7c1958d1f8ada5d0532ed8a5aa3fb2"
+                      + "d17ba70eb6248e594e1a2297acbbb39d"
+                      + "502f1a8c6eb6f1ce22b3de1a1f40cc24"
+                      + "554119a831a9aad6079cad88425de6bd"
+                      + "e1a9187ebb6092cf67bf2b13fd65f270"
+                      + "88d78b7e883c8759d2c4f5c65adb7553"
+                      + "878ad575f9fad878e80a0c9ba63bcbcc"
+                      + "2732e69485bbc9c90bfbd62481d9089b"
+                      + "eccf80cfe2df16a2cf65bd92dd597b07"
+                      + "07e0917af48bbb75fed413d238f5555a"
+                      + "7a569d80c3414a8d0859dc65a46128ba"
+                      + "b27af87a71314f318c782b23ebfe808b"
+                      + "82b0ce26401d2e22f04d83d1255dc51a"
+                      + "ddd3b75a2b1ae0784504df543af8969b"
+                      + "e3ea7082ff7fc9888c144da2af58429e"
+                      + "c96031dbcad3dad9af0dcbaaaf268cb8"
+                      + "fcffead94f3c7ca495e056a9b47acdb7"
+                      + "51fb73e666c6c655ade8297297d07ad1"
+                      + "ba5e43f1bca32301651339e22904cc8c"
+                      + "42f58c30c04aafdb038dda0847dd988d"
+                      + "cda6f3bfd15c4b4c4525004aa06eeff8"
+                      + "ca61783aacec57fb3d1f92b0fe2fd1a8"
+                      + "5f6724517b65e614ad6808d6f6ee34df"
+                      + "f7310fdc82aebfd904b01e1dc54b2927"
+                      + "094b2db68d6f903b68401adebf5a7e08"
+                      + "d78ff4ef5d63653a65040cf9bfd4aca7"
+                      + "984a74d37145986780fc0b16ac451649"
+                      + "de6188a7dbdf191f64b5fc5e2ab47b57"
+                      + "f7f7276cd419c17a3ca8e1b939ae49e4"
+                      + "88acba6b965610b5480109c8b17b80e1"
+                      + "b7b750dfc7598d5d5011fd2dcc5600a3"
+                      + "2ef5b52a1ecc820e308aa342721aac09"
+                      + "43bf6686b64b2579376504ccc493d97e"
+                      + "6aed3fb0f9cd71a43dd497f01f17c0e2"
+                      + "cb3797aa2a2f256656168e6c496afc5f"
+                      + "b93246f6b1116398a346f1a641f3b041"
+                      + "e989f7914f90cc2c7fff357876e506b5"
+                      + "0d334ba77c225bc307ba537152f3f161"
+                      + "0e4eafe595f6d9d90d11faa933a15ef1"
+                      + "369546868a7f3a45a96768d40fd9d034"
+                      + "12c091c6315cf4fde7cb68606937380d"
+                      + "b2eaaa707b4c4185c32eddcdd306705e"
+                      + "4dc1ffc872eeee475a64dfac86aba41c"
+                      + "0618983f8741c5ef68d3a101e8a3b8ca"
+                      + "c60c905c15fc910840b94c00a0b9d0",
+                    "0aab4c900501b3e24d7cdf4663326a3a"
+                      + "87df5e4843b2cbdb67cbf6e460fec350"
+                      + "aa5371b1508f9f4528ecea23c436d94b"
+                      + "5e8fcd4f681e30a6ac00a9704a188a03"
+                }));
+    }
+
+    @BeforeClass
+    public static void checkEDDSASupported() {
+        Assume.assumeTrue("EDDSA N/A", SecurityUtils.isEDDSACurveSupported());
+    }
+
+    @Test
+    public void testPublicKeyBytes() {
+        byte[] publicSeed = Ed25519PublicKeyDecoder.getSeedValue((EdDSAPublicKey) publicKey);
+        assertArrayEquals("Mismatched public seed value", pubBytes, publicSeed);
+    }
+
+    @Test
+    public void testPrivateKeyBytes() {
+        assertArrayEquals("Mismatched private seed value", prvBytes, ((EdDSAPrivateKey) privateKey).getSeed());
+    }
+
+    @Test
+    public void testSignature() throws Exception {
+        Signature signer = EdDSASecurityProviderUtils.getEDDSASignature();
+        signer.initSigner(privateKey);
+        signer.update(msgBytes.clone());
+
+        byte[] actSignature = signer.sign();
+        assertArrayEquals("Mismatched signature", expSignature, actSignature);
+
+        Signature verifier = EdDSASecurityProviderUtils.getEDDSASignature();
+        verifier.initVerifier(publicKey);
+        verifier.update(msgBytes.clone());
+        assertTrue("Verification failed", verifier.verify(expSignature));
+    }
+
+    @Test
+    public void testPartialBufferSignature() throws Exception {
+        byte[] extraData = getCurrentTestName().getBytes(StandardCharsets.UTF_8);
+        byte[] dataBuf = new byte[msgBytes.length + extraData.length];
+        int offset = extraData.length / 2;
+        System.arraycopy(extraData, 0, dataBuf, 0, offset);
+        System.arraycopy(msgBytes, 0, dataBuf, offset, msgBytes.length);
+        System.arraycopy(extraData, offset, dataBuf, offset + msgBytes.length, extraData.length - offset);
+
+        Signature signer = EdDSASecurityProviderUtils.getEDDSASignature();
+        signer.initSigner(privateKey);
+        signer.update(dataBuf.clone(), offset, msgBytes.length);
+
+        byte[] actSignature = signer.sign();
+        assertArrayEquals("Mismatched signature", expSignature, actSignature);
+
+        Signature verifier = EdDSASecurityProviderUtils.getEDDSASignature();
+        verifier.initVerifier(publicKey);
+        verifier.update(dataBuf.clone(), offset, msgBytes.length);
+        assertTrue("Verification failed", verifier.verify(expSignature));
+    }
+}


[03/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common utilities code from sshd-core into sshd-common (new artifact)

Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpTest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpTest.java b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpTest.java
index 5e7b360..b58cfb2 100644
--- a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpTest.java
+++ b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpTest.java
@@ -99,8 +99,8 @@ import org.apache.sshd.server.subsystem.sftp.SftpEventListenerManager;
 import org.apache.sshd.server.subsystem.sftp.SftpFileSystemAccessor;
 import org.apache.sshd.server.subsystem.sftp.SftpSubsystemEnvironment;
 import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
 import org.apache.sshd.util.test.SimpleUserInfo;
-import org.apache.sshd.util.test.Utils;
 import org.junit.After;
 import org.junit.Assume;
 import org.junit.Before;
@@ -145,7 +145,7 @@ public class SftpTest extends AbstractSftpClientTestSupport {
     public void testWriteOffsetIgnoredForAppendMode() throws IOException {
         Path targetPath = detectTargetFolder();
         Path parentPath = targetPath.getParent();
-        Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
+        Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
         Path testFile = assertHierarchyTargetFolderExists(lclSftp).resolve("file.txt");
         Files.deleteIfExists(testFile);
 
@@ -160,7 +160,7 @@ public class SftpTest extends AbstractSftpClientTestSupport {
             session.auth().verify(5L, TimeUnit.SECONDS);
 
             try (SftpClient sftp = createSftpClient(session)) {
-                String file = Utils.resolveRelativeRemotePath(parentPath, testFile);
+                String file = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, testFile);
 
                 try (CloseableHandle handle = sftp.open(file, OpenMode.Create, OpenMode.Write, OpenMode.Read, OpenMode.Append)) {
                     sftp.write(handle, 7365L, expectedRandom);
@@ -192,7 +192,7 @@ public class SftpTest extends AbstractSftpClientTestSupport {
     public void testReadBufferLimit() throws Exception {
         Path targetPath = detectTargetFolder();
         Path parentPath = targetPath.getParent();
-        Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
+        Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
         Path testFile = assertHierarchyTargetFolderExists(lclSftp).resolve("file.txt");
         byte[] expected = new byte[1024];
 
@@ -206,7 +206,7 @@ public class SftpTest extends AbstractSftpClientTestSupport {
             session.auth().verify(5L, TimeUnit.SECONDS);
 
             try (SftpClient sftp = createSftpClient(session)) {
-                String file = Utils.resolveRelativeRemotePath(parentPath, testFile);
+                String file = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, testFile);
                 byte[] actual = new byte[expected.length];
                 int maxAllowed = actual.length / 4;
                 // allow less than actual
@@ -263,7 +263,7 @@ public class SftpTest extends AbstractSftpClientTestSupport {
 
     private void testCannotEscapeRoot(boolean useAbsolutePath) throws Exception {
         Path targetPath = detectTargetFolder();
-        Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
+        Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
         assertHierarchyTargetFolderExists(lclSftp);
         sshd.setFileSystemFactory(new VirtualFileSystemFactory(lclSftp));
 
@@ -327,9 +327,9 @@ public class SftpTest extends AbstractSftpClientTestSupport {
     public void testNormalizeRemotePathsValues() throws Exception {
         Path targetPath = detectTargetFolder();
         Path parentPath = targetPath.getParent();
-        Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
+        Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
         Path testFile = assertHierarchyTargetFolderExists(lclSftp).resolve("file.txt");
-        String file = Utils.resolveRelativeRemotePath(parentPath, testFile);
+        String file = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, testFile);
         String[] comps = GenericUtils.split(file, '/');
 
         Factory<? extends Random> factory = client.getRandomFactory();
@@ -379,10 +379,10 @@ public class SftpTest extends AbstractSftpClientTestSupport {
     public void testOpen() throws Exception {
         Path targetPath = detectTargetFolder();
         Path parentPath = targetPath.getParent();
-        Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
+        Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
         Path clientFolder = lclSftp.resolve("client");
         Path testFile = clientFolder.resolve("file.txt");
-        String file = Utils.resolveRelativeRemotePath(parentPath, testFile);
+        String file = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, testFile);
 
         File javaFile = testFile.toFile();
         assertHierarchyTargetFolderExists(javaFile.getParentFile());
@@ -477,7 +477,7 @@ public class SftpTest extends AbstractSftpClientTestSupport {
     public void testInputStreamSkipAndReset() throws Exception {
         Path targetPath = detectTargetFolder();
         Path parentPath = targetPath.getParent();
-        Path localFile = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
+        Path localFile = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
         Files.createDirectories(localFile.getParent());
         byte[] data = (getClass().getName() + "#" + getCurrentTestName() + "[" + localFile + "]").getBytes(StandardCharsets.UTF_8);
         Files.write(localFile, data, StandardOpenOption.CREATE);
@@ -486,7 +486,7 @@ public class SftpTest extends AbstractSftpClientTestSupport {
             session.auth().verify(5L, TimeUnit.SECONDS);
 
             try (SftpClient sftp = createSftpClient(session);
-                 InputStream stream = sftp.read(Utils.resolveRelativeRemotePath(parentPath, localFile), OpenMode.Read)) {
+                 InputStream stream = sftp.read(CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, localFile), OpenMode.Read)) {
                 assertFalse("Stream reported mark supported", stream.markSupported());
                 try {
                     stream.mark(data.length);
@@ -563,7 +563,7 @@ public class SftpTest extends AbstractSftpClientTestSupport {
 
             Path targetPath = detectTargetFolder();
             Path parentPath = targetPath.getParent();
-            Path localFile = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
+            Path localFile = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
             Files.createDirectories(localFile.getParent());
             byte[] expected = (getClass().getName() + "#" + getCurrentTestName() + "[" + localFile + "]").getBytes(StandardCharsets.UTF_8);
             Files.write(localFile, expected, StandardOpenOption.CREATE);
@@ -573,7 +573,7 @@ public class SftpTest extends AbstractSftpClientTestSupport {
 
                 try (SftpClient sftp = createSftpClient(session)) {
                     byte[] actual = new byte[expected.length];
-                    try (InputStream stream = sftp.read(Utils.resolveRelativeRemotePath(parentPath, localFile), OpenMode.Read)) {
+                    try (InputStream stream = sftp.read(CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, localFile), OpenMode.Read)) {
                         IoUtils.readFully(stream, actual);
                     }
 
@@ -584,7 +584,7 @@ public class SftpTest extends AbstractSftpClientTestSupport {
 
                     Path localParent = localFile.getParent();
                     String localName = Objects.toString(localFile.getFileName(), null);
-                    try (CloseableHandle handle = sftp.openDir(Utils.resolveRelativeRemotePath(parentPath, localParent))) {
+                    try (CloseableHandle handle = sftp.openDir(CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, localParent))) {
                         List<DirEntry> entries = sftp.readDir(handle);
                         Path remoteParent = dirHolder.getAndSet(null);
                         assertNotNull("No remote folder holder value", remoteParent);
@@ -824,12 +824,12 @@ public class SftpTest extends AbstractSftpClientTestSupport {
             session.auth().verify(5L, TimeUnit.SECONDS);
 
             Path targetPath = detectTargetFolder();
-            Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
-            Utils.deleteRecursive(lclSftp);
+            Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
+            CommonTestSupportUtils.deleteRecursive(lclSftp);
 
             Path parentPath = targetPath.getParent();
             Path clientFolder = assertHierarchyTargetFolderExists(lclSftp).resolve("client");
-            String dir = Utils.resolveRelativeRemotePath(parentPath, clientFolder);
+            String dir = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, clientFolder);
 
             try (SftpClient sftp = createSftpClient(session)) {
                 sftp.mkdir(dir);
@@ -848,7 +848,7 @@ public class SftpTest extends AbstractSftpClientTestSupport {
 
                 // test erroneous calls that check for negative values
                 Path invalidPath = clientFolder.resolve(getCurrentTestName() + "-invalid");
-                testInvalidParams(sftp, invalidPath, Utils.resolveRelativeRemotePath(parentPath, invalidPath));
+                testInvalidParams(sftp, invalidPath, CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, invalidPath));
 
                 // cleanup
                 sftp.rmdir(dir);
@@ -920,11 +920,11 @@ public class SftpTest extends AbstractSftpClientTestSupport {
         String d = getCurrentTestName() + "\n";
 
         Path targetPath = detectTargetFolder();
-        Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
-        Utils.deleteRecursive(lclSftp);
+        Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
+        CommonTestSupportUtils.deleteRecursive(lclSftp);
 
         Path target = assertHierarchyTargetFolderExists(lclSftp).resolve("file.txt");
-        String remotePath = Utils.resolveRelativeRemotePath(targetPath.getParent(), target);
+        String remotePath = CommonTestSupportUtils.resolveRelativeRemotePath(targetPath.getParent(), target);
 
         final int numIterations = 10;
         StringBuilder sb = new StringBuilder(d.length() * numIterations * numIterations);
@@ -946,11 +946,11 @@ public class SftpTest extends AbstractSftpClientTestSupport {
     @Test
     public void testReadWriteWithOffset() throws Exception {
         Path targetPath = detectTargetFolder();
-        Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
-        Utils.deleteRecursive(lclSftp);
+        Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
+        CommonTestSupportUtils.deleteRecursive(lclSftp);
 
         Path localPath = assertHierarchyTargetFolderExists(lclSftp).resolve("file.txt");
-        String remotePath = Utils.resolveRelativeRemotePath(targetPath.getParent(), localPath);
+        String remotePath = CommonTestSupportUtils.resolveRelativeRemotePath(targetPath.getParent(), localPath);
         String data = getCurrentTestName();
         String extraData = "@" + getClass().getSimpleName();
         int appendOffset = -5;
@@ -1024,8 +1024,8 @@ public class SftpTest extends AbstractSftpClientTestSupport {
     @Test
     public void testRename() throws Exception {
         Path targetPath = detectTargetFolder();
-        Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
-        Utils.deleteRecursive(lclSftp);
+        Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
+        CommonTestSupportUtils.deleteRecursive(lclSftp);
 
         Path parentPath = targetPath.getParent();
         Path clientFolder = assertHierarchyTargetFolderExists(lclSftp.resolve("client"));
@@ -1035,15 +1035,15 @@ public class SftpTest extends AbstractSftpClientTestSupport {
 
             try (SftpClient sftp = createSftpClient(session)) {
                 Path file1 = clientFolder.resolve("file-1.txt");
-                String file1Path = Utils.resolveRelativeRemotePath(parentPath, file1);
+                String file1Path = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, file1);
                 try (OutputStream os = sftp.write(file1Path, SftpClient.MIN_WRITE_BUFFER_SIZE)) {
                     os.write((getCurrentTestName() + "\n").getBytes(StandardCharsets.UTF_8));
                 }
 
                 Path file2 = clientFolder.resolve("file-2.txt");
-                String file2Path = Utils.resolveRelativeRemotePath(parentPath, file2);
+                String file2Path = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, file2);
                 Path file3 = clientFolder.resolve("file-3.txt");
-                String file3Path = Utils.resolveRelativeRemotePath(parentPath, file3);
+                String file3Path = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, file3);
                 try {
                     sftp.rename(file2Path, file3Path);
                     fail("Unxpected rename success of " + file2Path + " => " + file3Path);
@@ -1223,12 +1223,12 @@ public class SftpTest extends AbstractSftpClientTestSupport {
 
     private void testClient(FactoryManager manager, SftpClient sftp) throws Exception {
         Path targetPath = detectTargetFolder();
-        Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
-        Utils.deleteRecursive(lclSftp);
+        Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
+        CommonTestSupportUtils.deleteRecursive(lclSftp);
 
         Path parentPath = targetPath.getParent();
         Path clientFolder = assertHierarchyTargetFolderExists(lclSftp).resolve("client");
-        String dir = Utils.resolveRelativeRemotePath(parentPath, clientFolder);
+        String dir = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, clientFolder);
         sftp.mkdir(dir);
 
         String file = dir + "/" + getCurrentTestName() + "-file.txt";
@@ -1343,8 +1343,8 @@ public class SftpTest extends AbstractSftpClientTestSupport {
         };
 
         Path targetPath = detectTargetFolder();
-        Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
-        Utils.deleteRecursive(lclSftp);
+        Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
+        CommonTestSupportUtils.deleteRecursive(lclSftp);
 
         /*
          * NOTE !!! according to Jsch documentation
@@ -1360,7 +1360,7 @@ public class SftpTest extends AbstractSftpClientTestSupport {
          */
         Path parentPath = targetPath.getParent();
         Path sourcePath = assertHierarchyTargetFolderExists(lclSftp).resolve("src.txt");
-        String remSrcPath = "/" + Utils.resolveRelativeRemotePath(parentPath, sourcePath);
+        String remSrcPath = "/" + CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, sourcePath);
 
         factory.addSftpEventListener(listener);
         try {
@@ -1376,7 +1376,7 @@ public class SftpTest extends AbstractSftpClientTestSupport {
                 assertEquals("Mismatched stored data in " + remSrcPath, data, readFile(remSrcPath));
 
                 Path linkPath = lclSftp.resolve("link-" + sourcePath.getFileName());
-                String remLinkPath = "/" + Utils.resolveRelativeRemotePath(parentPath, linkPath);
+                String remLinkPath = "/" + CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, linkPath);
                 LinkOption[] options = IoUtils.getLinkOptions(false);
                 if (Files.exists(linkPath, options)) {
                     Files.delete(linkPath);
@@ -1405,7 +1405,7 @@ public class SftpTest extends AbstractSftpClientTestSupport {
     @Test   // see SSHD-697
     public void testFileChannel() throws IOException {
         Path targetPath = detectTargetFolder();
-        Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName());
+        Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName());
         Path lclFile = lclSftp.resolve(getCurrentTestName() + ".txt");
         Files.deleteIfExists(lclFile);
         byte[] expected = (getClass().getName() + "#" + getCurrentTestName() + "(" + new Date() + ")").getBytes(StandardCharsets.UTF_8);
@@ -1416,7 +1416,7 @@ public class SftpTest extends AbstractSftpClientTestSupport {
 
             try (SftpClient sftp = createSftpClient(session)) {
                 Path parentPath = targetPath.getParent();
-                String remFilePath = Utils.resolveRelativeRemotePath(parentPath, lclFile);
+                String remFilePath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, lclFile);
 
                 try (FileChannel fc = sftp.openRemotePathChannel(remFilePath, EnumSet.of(StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE))) {
                     int writeLen = fc.write(ByteBuffer.wrap(expected));

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpVersionSelectorTest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpVersionSelectorTest.java b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpVersionSelectorTest.java
index afc1944..e02aa30 100644
--- a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpVersionSelectorTest.java
+++ b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpVersionSelectorTest.java
@@ -27,7 +27,7 @@ import java.util.Random;
 
 import org.apache.sshd.client.session.ClientSession;
 import org.apache.sshd.server.subsystem.sftp.SftpSubsystemEnvironment;
-import org.apache.sshd.util.test.BaseTestSupport;
+import org.apache.sshd.util.test.JUnitTestSupport;
 import org.apache.sshd.util.test.NoIoTestCase;
 import org.junit.FixMethodOrder;
 import org.junit.Test;
@@ -40,7 +40,7 @@ import org.mockito.Mockito;
  */
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 @Category({ NoIoTestCase.class })
-public class SftpVersionSelectorTest extends BaseTestSupport {
+public class SftpVersionSelectorTest extends JUnitTestSupport {
     public SftpVersionSelectorTest() {
         super();
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpVersionsTest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpVersionsTest.java b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpVersionsTest.java
index 0e82f6e..1051c50 100644
--- a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpVersionsTest.java
+++ b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpVersionsTest.java
@@ -61,8 +61,8 @@ import org.apache.sshd.server.subsystem.sftp.SftpEventListener;
 import org.apache.sshd.server.subsystem.sftp.SftpSubsystem;
 import org.apache.sshd.server.subsystem.sftp.SftpSubsystemEnvironment;
 import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
 import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
-import org.apache.sshd.util.test.Utils;
 import org.junit.Before;
 import org.junit.FixMethodOrder;
 import org.junit.Test;
@@ -111,13 +111,13 @@ public class SftpVersionsTest extends AbstractSftpClientTestSupport {
     @Test   // See SSHD-749
     public void testSftpOpenFlags() throws Exception {
         Path targetPath = detectTargetFolder();
-        Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName());
+        Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName());
         Path lclParent = assertHierarchyTargetFolderExists(lclSftp);
         Path lclFile = lclParent.resolve(getCurrentTestName() + "-" + getTestedVersion() + ".txt");
         Files.deleteIfExists(lclFile);
 
         Path parentPath = targetPath.getParent();
-        String remotePath = Utils.resolveRelativeRemotePath(parentPath, lclFile);
+        String remotePath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, lclFile);
         try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port)
                     .verify(CONNECT_TIMEOUT, TimeUnit.SECONDS)
                     .getSession()) {
@@ -150,11 +150,11 @@ public class SftpVersionsTest extends AbstractSftpClientTestSupport {
     @Test   // see SSHD-572
     public void testSftpFileTimesUpdate() throws Exception {
         Path targetPath = detectTargetFolder();
-        Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName());
+        Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName());
         Path lclFile = assertHierarchyTargetFolderExists(lclSftp).resolve(getCurrentTestName() + "-" + getTestedVersion() + ".txt");
         Files.write(lclFile, getClass().getName().getBytes(StandardCharsets.UTF_8));
         Path parentPath = targetPath.getParent();
-        String remotePath = Utils.resolveRelativeRemotePath(parentPath, lclFile);
+        String remotePath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, lclFile);
         try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port)
                     .verify(CONNECT_TIMEOUT, TimeUnit.SECONDS)
                     .getSession()) {
@@ -186,7 +186,7 @@ public class SftpVersionsTest extends AbstractSftpClientTestSupport {
     @Test   // see SSHD-573
     public void testSftpFileTypeAndPermissionsUpdate() throws Exception {
         Path targetPath = detectTargetFolder();
-        Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName());
+        Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName());
         Path subFolder = Files.createDirectories(lclSftp.resolve("sub-folder"));
         String subFolderName = subFolder.getFileName().toString();
         Path lclFile = assertHierarchyTargetFolderExists(lclSftp).resolve(getCurrentTestName() + "-" + getTestedVersion() + ".txt");
@@ -194,7 +194,7 @@ public class SftpVersionsTest extends AbstractSftpClientTestSupport {
         Files.write(lclFile, getClass().getName().getBytes(StandardCharsets.UTF_8));
 
         Path parentPath = targetPath.getParent();
-        String remotePath = Utils.resolveRelativeRemotePath(parentPath, lclSftp);
+        String remotePath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, lclSftp);
         try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port)
                     .verify(CONNECT_TIMEOUT, TimeUnit.SECONDS)
                     .getSession()) {
@@ -299,13 +299,13 @@ public class SftpVersionsTest extends AbstractSftpClientTestSupport {
         });
 
         Path targetPath = detectTargetFolder();
-        Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName());
+        Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName());
         Files.createDirectories(lclSftp.resolve("sub-folder"));
         Path lclFile = assertHierarchyTargetFolderExists(lclSftp).resolve(getCurrentTestName() + "-" + getTestedVersion() + ".txt");
         Files.write(lclFile, getClass().getName().getBytes(StandardCharsets.UTF_8));
 
         Path parentPath = targetPath.getParent();
-        String remotePath = Utils.resolveRelativeRemotePath(parentPath, lclSftp);
+        String remotePath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, lclSftp);
         int numInvoked = 0;
 
         List<NamedFactory<Command>> factories = sshd.getSubsystemFactories();
@@ -418,13 +418,13 @@ public class SftpVersionsTest extends AbstractSftpClientTestSupport {
         });
 
         Path targetPath = detectTargetFolder();
-        Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName());
+        Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName());
         Files.createDirectories(lclSftp.resolve("sub-folder"));
         Path lclFile = assertHierarchyTargetFolderExists(lclSftp).resolve(getCurrentTestName() + "-" + getTestedVersion() + ".txt");
         Files.write(lclFile, getClass().getName().getBytes(StandardCharsets.UTF_8));
 
         Path parentPath = targetPath.getParent();
-        String remotePath = Utils.resolveRelativeRemotePath(parentPath, lclSftp);
+        String remotePath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, lclSftp);
         int numInvoked = 0;
 
         List<NamedFactory<Command>> factories = sshd.getSubsystemFactories();
@@ -471,7 +471,7 @@ public class SftpVersionsTest extends AbstractSftpClientTestSupport {
                 int version = sftp.getVersion();
                 Path targetPath = detectTargetFolder();
                 Path parentPath = targetPath.getParent();
-                String remotePath = Utils.resolveRelativeRemotePath(parentPath, targetPath);
+                String remotePath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, targetPath);
 
                 try (CloseableHandle handle = sftp.openDir(remotePath)) {
                     List<DirEntry> entries = sftp.readDir(handle, eolIndicator);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SimpleSftpClientTest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SimpleSftpClientTest.java b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SimpleSftpClientTest.java
index 2f41c0f..7663485 100644
--- a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SimpleSftpClientTest.java
+++ b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SimpleSftpClientTest.java
@@ -33,7 +33,7 @@ import org.apache.sshd.common.session.Session;
 import org.apache.sshd.common.subsystem.sftp.SftpConstants;
 import org.apache.sshd.common.util.io.IoUtils;
 import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory;
-import org.apache.sshd.util.test.Utils;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
 import org.apache.sshd.util.test.client.simple.BaseSimpleClientTestSupport;
 import org.junit.FixMethodOrder;
 import org.junit.Test;
@@ -80,11 +80,11 @@ public class SimpleSftpClientTest extends BaseSimpleClientTestSupport {
 
     @Test
     public void testSftpProxyCalls() throws Exception {
-        Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
-        Utils.deleteRecursive(lclSftp);
+        Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
+        CommonTestSupportUtils.deleteRecursive(lclSftp);
         Path clientFolder = assertHierarchyTargetFolderExists(lclSftp).resolve("client");
         Path clientFile = clientFolder.resolve("file.txt");
-        String remoteFileDir = Utils.resolveRelativeRemotePath(parentPath, clientFolder);
+        String remoteFileDir = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, clientFolder);
         String clientFileName = clientFile.getFileName().toString();
         String remoteFilePath = remoteFileDir + "/" + clientFileName;
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/AbstractCheckFileExtensionTest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/AbstractCheckFileExtensionTest.java b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/AbstractCheckFileExtensionTest.java
index e3537ea..1ddd789 100644
--- a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/AbstractCheckFileExtensionTest.java
+++ b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/AbstractCheckFileExtensionTest.java
@@ -49,8 +49,8 @@ import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.NumberUtils;
 import org.apache.sshd.common.util.buffer.BufferUtils;
 import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
 import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
-import org.apache.sshd.util.test.Utils;
 import org.junit.Before;
 import org.junit.FixMethodOrder;
 import org.junit.Test;
@@ -159,7 +159,7 @@ public class AbstractCheckFileExtensionTest extends AbstractSftpClientTestSuppor
     @SuppressWarnings("checkstyle:nestedtrydepth")
     private void testCheckFileExtension(NamedFactory<? extends Digest> factory, byte[] data, int hashBlockSize, byte[] expectedHash) throws Exception {
         Path targetPath = detectTargetFolder();
-        Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName());
+        Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName());
         Path srcFile = assertHierarchyTargetFolderExists(lclSftp).resolve(factory.getName() + "-data-" + data.length + "-" + hashBlockSize + ".txt");
         Files.write(srcFile, data, IoUtils.EMPTY_OPEN_OPTIONS);
 
@@ -175,8 +175,8 @@ public class AbstractCheckFileExtensionTest extends AbstractSftpClientTestSuppor
         }
 
         Path parentPath = targetPath.getParent();
-        String srcPath = Utils.resolveRelativeRemotePath(parentPath, srcFile);
-        String srcFolder = Utils.resolveRelativeRemotePath(parentPath, srcFile.getParent());
+        String srcPath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, srcFile);
+        String srcFolder = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, srcFile.getParent());
         try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) {
             session.addPasswordIdentity(getCurrentTestName());
             session.auth().verify(5L, TimeUnit.SECONDS);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/AbstractMD5HashExtensionTest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/AbstractMD5HashExtensionTest.java b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/AbstractMD5HashExtensionTest.java
index ea2783a..c195f15 100644
--- a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/AbstractMD5HashExtensionTest.java
+++ b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/AbstractMD5HashExtensionTest.java
@@ -43,8 +43,8 @@ import org.apache.sshd.common.subsystem.sftp.SftpException;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.buffer.BufferUtils;
 import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
 import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
-import org.apache.sshd.util.test.Utils;
 import org.junit.Assume;
 import org.junit.Before;
 import org.junit.BeforeClass;
@@ -127,13 +127,13 @@ public class AbstractMD5HashExtensionTest extends AbstractSftpClientTestSupport
         }
 
         Path targetPath = detectTargetFolder();
-        Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName());
+        Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName());
         Path srcFile = assertHierarchyTargetFolderExists(lclSftp).resolve("data-" + data.length + ".txt");
         Files.write(srcFile, data, IoUtils.EMPTY_OPEN_OPTIONS);
 
         Path parentPath = targetPath.getParent();
-        String srcPath = Utils.resolveRelativeRemotePath(parentPath, srcFile);
-        String srcFolder = Utils.resolveRelativeRemotePath(parentPath, srcFile.getParent());
+        String srcPath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, srcFile);
+        String srcFolder = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, srcFile.getParent());
         try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) {
             session.addPasswordIdentity(getCurrentTestName());
             session.auth().verify(5L, TimeUnit.SECONDS);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/CopyDataExtensionImplTest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/CopyDataExtensionImplTest.java b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/CopyDataExtensionImplTest.java
index 01d3334..6b78607 100644
--- a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/CopyDataExtensionImplTest.java
+++ b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/CopyDataExtensionImplTest.java
@@ -43,8 +43,8 @@ import org.apache.sshd.common.Factory;
 import org.apache.sshd.common.random.Random;
 import org.apache.sshd.common.subsystem.sftp.SftpConstants;
 import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
 import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
-import org.apache.sshd.util.test.Utils;
 import org.junit.Before;
 import org.junit.FixMethodOrder;
 import org.junit.Test;
@@ -135,18 +135,18 @@ public class CopyDataExtensionImplTest extends AbstractSftpClientTestSupport {
     private void testCopyDataExtension(byte[] data, int readOffset, int readLength, long writeOffset) throws Exception {
         Path targetPath = detectTargetFolder();
         Path parentPath = targetPath.getParent();
-        Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName());
+        Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName());
         LinkOption[] options = IoUtils.getLinkOptions(true);
         String baseName = readOffset + "-" + readLength + "-" + writeOffset;
         Path srcFile = assertHierarchyTargetFolderExists(lclSftp, options).resolve(baseName + "-src.txt");
         Files.write(srcFile, data, IoUtils.EMPTY_OPEN_OPTIONS);
-        String srcPath = Utils.resolveRelativeRemotePath(parentPath, srcFile);
+        String srcPath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, srcFile);
 
         Path dstFile = srcFile.getParent().resolve(baseName + "-dst.txt");
         if (Files.exists(dstFile, options)) {
             Files.delete(dstFile);
         }
-        String dstPath = Utils.resolveRelativeRemotePath(parentPath, dstFile);
+        String dstPath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, dstFile);
         if (writeOffset > 0L) {
             Factory<? extends Random> factory = client.getRandomFactory();
             Random randomizer = factory.create();

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/CopyFileExtensionImplTest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/CopyFileExtensionImplTest.java b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/CopyFileExtensionImplTest.java
index b21da13..74d3573 100644
--- a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/CopyFileExtensionImplTest.java
+++ b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/CopyFileExtensionImplTest.java
@@ -33,7 +33,7 @@ import org.apache.sshd.client.subsystem.sftp.extensions.CopyFileExtension;
 import org.apache.sshd.common.subsystem.sftp.SftpConstants;
 import org.apache.sshd.common.subsystem.sftp.SftpException;
 import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.util.test.Utils;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
 import org.junit.Before;
 import org.junit.FixMethodOrder;
 import org.junit.Test;
@@ -56,17 +56,18 @@ public class CopyFileExtensionImplTest extends AbstractSftpClientTestSupport {
     @Test
     public void testCopyFileExtension() throws Exception {
         Path targetPath = detectTargetFolder();
-        Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
-        Utils.deleteRecursive(lclSftp);
+        Path lclSftp = CommonTestSupportUtils.resolve(targetPath,
+            SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
+        CommonTestSupportUtils.deleteRecursive(lclSftp);
 
         byte[] data = (getClass().getName() + "#" + getCurrentTestName()).getBytes(StandardCharsets.UTF_8);
         Path srcFile = assertHierarchyTargetFolderExists(lclSftp).resolve("src.txt");
         Files.write(srcFile, data, IoUtils.EMPTY_OPEN_OPTIONS);
 
         Path parentPath = targetPath.getParent();
-        String srcPath = Utils.resolveRelativeRemotePath(parentPath, srcFile);
+        String srcPath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, srcFile);
         Path dstFile = lclSftp.resolve("dst.txt");
-        String dstPath = Utils.resolveRelativeRemotePath(parentPath, dstFile);
+        String dstPath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, dstFile);
 
         LinkOption[] options = IoUtils.getLinkOptions(true);
         assertFalse("Destination file unexpectedly exists", Files.exists(dstFile, options));

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/SpaceAvailableExtensionImplTest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/SpaceAvailableExtensionImplTest.java b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/SpaceAvailableExtensionImplTest.java
index af83656..a528278 100644
--- a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/SpaceAvailableExtensionImplTest.java
+++ b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/SpaceAvailableExtensionImplTest.java
@@ -38,7 +38,7 @@ import org.apache.sshd.common.subsystem.sftp.extensions.SpaceAvailableExtensionI
 import org.apache.sshd.server.command.Command;
 import org.apache.sshd.server.subsystem.sftp.SftpSubsystem;
 import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory;
-import org.apache.sshd.util.test.Utils;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
 import org.junit.Before;
 import org.junit.FixMethodOrder;
 import org.junit.Test;
@@ -61,10 +61,10 @@ public class SpaceAvailableExtensionImplTest extends AbstractSftpClientTestSuppo
     @Test
     public void testFileStoreReport() throws Exception {
         Path targetPath = detectTargetFolder();
-        Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
+        Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
         Path parentPath = targetPath.getParent();
         FileStore store = Files.getFileStore(lclSftp.getRoot());
-        final String queryPath = Utils.resolveRelativeRemotePath(parentPath, lclSftp);
+        final String queryPath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, lclSftp);
         final SpaceAvailableExtensionInfo expected = new SpaceAvailableExtensionInfo(store);
 
         List<NamedFactory<Command>> factories = sshd.getSubsystemFactories();

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/openssh/helpers/OpenSSHExtensionsTest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/openssh/helpers/OpenSSHExtensionsTest.java b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/openssh/helpers/OpenSSHExtensionsTest.java
index 83ef70f..d778393 100644
--- a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/openssh/helpers/OpenSSHExtensionsTest.java
+++ b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/openssh/helpers/OpenSSHExtensionsTest.java
@@ -52,7 +52,7 @@ import org.apache.sshd.server.command.Command;
 import org.apache.sshd.server.session.ServerSession;
 import org.apache.sshd.server.subsystem.sftp.SftpSubsystem;
 import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory;
-import org.apache.sshd.util.test.Utils;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
 import org.junit.Before;
 import org.junit.FixMethodOrder;
 import org.junit.Test;
@@ -75,12 +75,12 @@ public class OpenSSHExtensionsTest extends AbstractSftpClientTestSupport {
     @Test
     public void testFsync() throws IOException {
         Path targetPath = detectTargetFolder();
-        Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName());
+        Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName());
         Path srcFile = assertHierarchyTargetFolderExists(lclSftp).resolve(getCurrentTestName() + ".txt");
         byte[] expected = (getClass().getName() + "#" + getCurrentTestName()).getBytes(StandardCharsets.UTF_8);
 
         Path parentPath = targetPath.getParent();
-        String srcPath = Utils.resolveRelativeRemotePath(parentPath, srcFile);
+        String srcPath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, srcFile);
         try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) {
             session.addPasswordIdentity(getCurrentTestName());
             session.auth().verify(5L, TimeUnit.SECONDS);
@@ -101,11 +101,11 @@ public class OpenSSHExtensionsTest extends AbstractSftpClientTestSupport {
     @Test
     public void testStat() throws Exception {
         Path targetPath = detectTargetFolder();
-        Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName());
+        Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName());
         Path srcFile = assertHierarchyTargetFolderExists(lclSftp).resolve(getCurrentTestName() + ".txt");
         Files.write(srcFile, (getClass().getName() + "#" + getCurrentTestName()).getBytes(StandardCharsets.UTF_8), IoUtils.EMPTY_OPEN_OPTIONS);
         Path parentPath = targetPath.getParent();
-        String srcPath = Utils.resolveRelativeRemotePath(parentPath, srcFile);
+        String srcPath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, srcFile);
 
         final AtomicReference<String> extensionHolder = new AtomicReference<>(null);
         final OpenSSHStatExtensionInfo expected = new OpenSSHStatExtensionInfo();

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-sftp/src/test/java/org/apache/sshd/common/subsystem/sftp/SftpConstantsTest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/test/java/org/apache/sshd/common/subsystem/sftp/SftpConstantsTest.java b/sshd-sftp/src/test/java/org/apache/sshd/common/subsystem/sftp/SftpConstantsTest.java
index d059d36..52f7096 100644
--- a/sshd-sftp/src/test/java/org/apache/sshd/common/subsystem/sftp/SftpConstantsTest.java
+++ b/sshd-sftp/src/test/java/org/apache/sshd/common/subsystem/sftp/SftpConstantsTest.java
@@ -20,7 +20,7 @@
 package org.apache.sshd.common.subsystem.sftp;
 
 import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
+import org.apache.sshd.util.test.JUnitTestSupport;
 import org.apache.sshd.util.test.NoIoTestCase;
 import org.junit.FixMethodOrder;
 import org.junit.Test;
@@ -32,7 +32,7 @@ import org.junit.runners.MethodSorters;
  */
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 @Category({ NoIoTestCase.class })
-public class SftpConstantsTest extends BaseTestSupport {
+public class SftpConstantsTest extends JUnitTestSupport {
     public SftpConstantsTest() {
         super();
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-sftp/src/test/java/org/apache/sshd/common/subsystem/sftp/SftpUniversalOwnerAndGroupTest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/test/java/org/apache/sshd/common/subsystem/sftp/SftpUniversalOwnerAndGroupTest.java b/sshd-sftp/src/test/java/org/apache/sshd/common/subsystem/sftp/SftpUniversalOwnerAndGroupTest.java
index 704aa05..3075f01 100644
--- a/sshd-sftp/src/test/java/org/apache/sshd/common/subsystem/sftp/SftpUniversalOwnerAndGroupTest.java
+++ b/sshd-sftp/src/test/java/org/apache/sshd/common/subsystem/sftp/SftpUniversalOwnerAndGroupTest.java
@@ -20,7 +20,7 @@
 package org.apache.sshd.common.subsystem.sftp;
 
 import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
+import org.apache.sshd.util.test.JUnitTestSupport;
 import org.apache.sshd.util.test.NoIoTestCase;
 import org.junit.FixMethodOrder;
 import org.junit.Test;
@@ -32,7 +32,7 @@ import org.junit.runners.MethodSorters;
  */
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 @Category({ NoIoTestCase.class })
-public class SftpUniversalOwnerAndGroupTest extends BaseTestSupport {
+public class SftpUniversalOwnerAndGroupTest extends JUnitTestSupport {
     public SftpUniversalOwnerAndGroupTest() {
         super();
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-sftp/src/test/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystemFactoryTest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/test/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystemFactoryTest.java b/sshd-sftp/src/test/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystemFactoryTest.java
index 436c84b..da722f2 100644
--- a/sshd-sftp/src/test/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystemFactoryTest.java
+++ b/sshd-sftp/src/test/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystemFactoryTest.java
@@ -20,7 +20,7 @@
 package org.apache.sshd.server.subsystem.sftp;
 
 import org.apache.sshd.common.util.threads.CloseableExecutorService;
-import org.apache.sshd.util.test.BaseTestSupport;
+import org.apache.sshd.util.test.JUnitTestSupport;
 import org.apache.sshd.util.test.NoIoTestCase;
 import org.junit.FixMethodOrder;
 import org.junit.Test;
@@ -33,7 +33,7 @@ import org.mockito.Mockito;
  */
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 @Category({ NoIoTestCase.class })
-public class SftpSubsystemFactoryTest extends BaseTestSupport {
+public class SftpSubsystemFactoryTest extends JUnitTestSupport {
     public SftpSubsystemFactoryTest() {
         super();
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-spring-sftp/pom.xml
----------------------------------------------------------------------
diff --git a/sshd-spring-sftp/pom.xml b/sshd-spring-sftp/pom.xml
index c5b4f56..63ec070 100644
--- a/sshd-spring-sftp/pom.xml
+++ b/sshd-spring-sftp/pom.xml
@@ -100,6 +100,13 @@
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>org.apache.sshd</groupId>
+            <artifactId>sshd-common</artifactId>
+            <version>${project.version}</version>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-log4j12</artifactId>
             <scope>test</scope>

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-spring-sftp/src/main/java/org/apache/sshd/spring/integration/sftp/ApacheSshdSftpSessionFactory.java
----------------------------------------------------------------------
diff --git a/sshd-spring-sftp/src/main/java/org/apache/sshd/spring/integration/sftp/ApacheSshdSftpSessionFactory.java b/sshd-spring-sftp/src/main/java/org/apache/sshd/spring/integration/sftp/ApacheSshdSftpSessionFactory.java
index 1d04f60..7dc2eb0 100644
--- a/sshd-spring-sftp/src/main/java/org/apache/sshd/spring/integration/sftp/ApacheSshdSftpSessionFactory.java
+++ b/sshd-spring-sftp/src/main/java/org/apache/sshd/spring/integration/sftp/ApacheSshdSftpSessionFactory.java
@@ -37,7 +37,7 @@ import org.apache.sshd.client.subsystem.sftp.SftpClient.DirEntry;
 import org.apache.sshd.client.subsystem.sftp.SftpClientFactory;
 import org.apache.sshd.client.subsystem.sftp.SftpVersionSelector;
 import org.apache.sshd.common.PropertyResolverUtils;
-import org.apache.sshd.common.config.SshConfigFileReader;
+import org.apache.sshd.common.config.ConfigFileReaderSupport;
 import org.apache.sshd.common.config.keys.FilePasswordProvider;
 import org.apache.sshd.common.config.keys.KeyUtils;
 import org.apache.sshd.common.config.keys.loader.pem.PEMResourceParserUtils;
@@ -72,7 +72,7 @@ public class ApacheSshdSftpSessionFactory
     private final AtomicReference<ClientSession> sharedSessionHolder = new AtomicReference<>();
 
     private volatile String hostValue;
-    private volatile int portValue = SshConfigFileReader.DEFAULT_PORT;
+    private volatile int portValue = ConfigFileReaderSupport.DEFAULT_PORT;
     private volatile String userValue;
     private volatile String passwordValue;
     private volatile Resource privateKey;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-spring-sftp/src/test/java/org/apache/sshd/spring/integration/sftp/ApacheSshdSftpSessionFactoryTest.java
----------------------------------------------------------------------
diff --git a/sshd-spring-sftp/src/test/java/org/apache/sshd/spring/integration/sftp/ApacheSshdSftpSessionFactoryTest.java b/sshd-spring-sftp/src/test/java/org/apache/sshd/spring/integration/sftp/ApacheSshdSftpSessionFactoryTest.java
index e59ebd8..75d7195 100644
--- a/sshd-spring-sftp/src/test/java/org/apache/sshd/spring/integration/sftp/ApacheSshdSftpSessionFactoryTest.java
+++ b/sshd-spring-sftp/src/test/java/org/apache/sshd/spring/integration/sftp/ApacheSshdSftpSessionFactoryTest.java
@@ -47,8 +47,9 @@ import org.apache.sshd.common.util.io.IoUtils;
 import org.apache.sshd.server.SshServer;
 import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory;
 import org.apache.sshd.util.test.BaseTestSupport;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
+import org.apache.sshd.util.test.CoreTestSupportUtils;
 import org.apache.sshd.util.test.JSchLogger;
-import org.apache.sshd.util.test.Utils;
 import org.junit.AfterClass;
 import org.junit.Before;
 import org.junit.BeforeClass;
@@ -110,12 +111,12 @@ public class ApacheSshdSftpSessionFactoryTest extends BaseTestSupport {
     @BeforeClass
     public static void setupClientAndServer() throws Exception {
         JSchLogger.init();
-        sshd = Utils.setupTestServer(ApacheSshdSftpSessionFactoryTest.class);
+        sshd = CoreTestSupportUtils.setupTestServer(ApacheSshdSftpSessionFactoryTest.class);
         sshd.setSubsystemFactories(Collections.singletonList(new SftpSubsystemFactory()));
         sshd.start();
         port = sshd.getPort();
 
-        client = Utils.setupTestClient(ApacheSshdSftpSessionFactoryTest.class);
+        client = CoreTestSupportUtils.setupTestClient(ApacheSshdSftpSessionFactoryTest.class);
         client.start();
     }
 
@@ -174,7 +175,7 @@ public class ApacheSshdSftpSessionFactoryTest extends BaseTestSupport {
     @Test
     public void testWriteRemoteFileContents() throws Exception {
         Path targetPath = detectTargetFolder();
-        Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
+        Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
         Path srcFile = Files.createDirectories(lclSftp).resolve("source.txt");
         List<String> expectedLines = Arrays.asList(getClass().getPackage().getName(), getClass().getSimpleName(), getCurrentTestName());
         Files.deleteIfExists(srcFile);
@@ -182,7 +183,7 @@ public class ApacheSshdSftpSessionFactoryTest extends BaseTestSupport {
 
         Path dstFile = srcFile.getParent().resolve("destination.txt");
         Path parentPath = targetPath.getParent();
-        String remoteFile = Utils.resolveRelativeRemotePath(parentPath, dstFile);
+        String remoteFile = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, dstFile);
         SessionFactory<LsEntry> legacyFactory = getLegacySessionFactory();
         SessionFactory<SftpClient.DirEntry> sshdFactory = getSshdSessionFactory();
         try (Session<LsEntry> legacySession = legacyFactory.getSession();
@@ -208,14 +209,14 @@ public class ApacheSshdSftpSessionFactoryTest extends BaseTestSupport {
     @Test
     public void testRetrieveRemoteFileContents() throws Exception {
         Path targetPath = detectTargetFolder();
-        Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
+        Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
         Path lclFile = Files.createDirectories(lclSftp).resolve("source.txt");
         List<String> expectedLines = Arrays.asList(getClass().getPackage().getName(), getClass().getSimpleName(), getCurrentTestName());
         Files.deleteIfExists(lclFile);
         Files.write(lclFile, expectedLines, StandardCharsets.UTF_8);
 
         Path parentPath = targetPath.getParent();
-        String remoteFile = Utils.resolveRelativeRemotePath(parentPath, lclFile);
+        String remoteFile = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, lclFile);
         SessionFactory<LsEntry> legacyFactory = getLegacySessionFactory();
         SessionFactory<SftpClient.DirEntry> sshdFactory = getSshdSessionFactory();
         try (Session<LsEntry> legacySession = legacyFactory.getSession();
@@ -253,8 +254,8 @@ public class ApacheSshdSftpSessionFactoryTest extends BaseTestSupport {
     @Test
     public void testListContents() throws Exception {
         Path targetPath = detectTargetFolder();
-        Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
-        Utils.deleteRecursive(lclSftp); // start clean
+        Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
+        CommonTestSupportUtils.deleteRecursive(lclSftp); // start clean
 
         List<Path> subFolders = new ArrayList<>();
         for (int index = 1; index <= Byte.SIZE; index++) {
@@ -272,7 +273,7 @@ public class ApacheSshdSftpSessionFactoryTest extends BaseTestSupport {
         Collections.sort(subFiles, BY_CASE_INSENSITIVE_FILE_PART);
 
         Path parentPath = targetPath.getParent();
-        String remotePath = Utils.resolveRelativeRemotePath(parentPath, lclSftp);
+        String remotePath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, lclSftp);
         SessionFactory<LsEntry> legacyFactory = getLegacySessionFactory();
         SessionFactory<SftpClient.DirEntry> sshdFactory = getSshdSessionFactory();
         try (Session<LsEntry> legacySession = legacyFactory.getSession();


[14/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common utilities code from sshd-core into sshd-common (new artifact)

Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/io/DirectoryScanner.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/io/DirectoryScanner.java b/sshd-core/src/main/java/org/apache/sshd/common/util/io/DirectoryScanner.java
deleted file mode 100644
index 8b4e9ba..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/io/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.io;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.SelectorUtils;
-
-/**
- * <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() {
-        super();
-    }
-
-    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 (GenericUtils.isEmpty(newfiles)) {
-            newfiles = GenericUtils.EMPTY_STRING_ARRAY;
-        }
-
-        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()];
-        return filesIncluded.toArray(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
-     */
-    @SuppressWarnings("PMD.AssignmentInOperand")
-    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;
-        for (int end = text.indexOf(repl, start); end != -1; end = text.indexOf(repl, start)) {
-            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/10de190e/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
deleted file mode 100644
index 8a1a68d..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/io/EmptyInputStream.java
+++ /dev/null
@@ -1,66 +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.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
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/io/FileInfoExtractor.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/io/FileInfoExtractor.java b/sshd-core/src/main/java/org/apache/sshd/common/util/io/FileInfoExtractor.java
deleted file mode 100644
index feafd18..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/io/FileInfoExtractor.java
+++ /dev/null
@@ -1,53 +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.io;
-
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.LinkOption;
-import java.nio.file.Path;
-import java.nio.file.attribute.FileTime;
-import java.nio.file.attribute.PosixFilePermission;
-import java.util.Set;
-
-/**
- * @param <T> Type of information being extracted
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FunctionalInterface
-public interface FileInfoExtractor<T> {
-
-    FileInfoExtractor<Boolean> EXISTS = Files::exists;
-
-    FileInfoExtractor<Boolean> ISDIR = Files::isDirectory;
-
-    FileInfoExtractor<Boolean> ISREG = Files::isRegularFile;
-
-    FileInfoExtractor<Boolean> ISSYMLINK = (file, options) -> Files.isSymbolicLink(file);
-
-    FileInfoExtractor<Long> SIZE = (file, options) -> Files.size(file);
-
-    FileInfoExtractor<Set<PosixFilePermission>> PERMISSIONS = IoUtils::getPermissions;
-
-    FileInfoExtractor<FileTime> LASTMODIFIED = Files::getLastModifiedTime;
-
-    T infoOf(Path file, LinkOption... options) throws IOException;
-
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/io/InputStreamWithChannel.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/io/InputStreamWithChannel.java b/sshd-core/src/main/java/org/apache/sshd/common/util/io/InputStreamWithChannel.java
deleted file mode 100644
index d847079..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/io/InputStreamWithChannel.java
+++ /dev/null
@@ -1,32 +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.io;
-
-import java.io.InputStream;
-import java.nio.channels.Channel;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class InputStreamWithChannel extends InputStream implements Channel {
-    protected InputStreamWithChannel() {
-        super();
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/io/IoUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/io/IoUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/io/IoUtils.java
deleted file mode 100644
index 10aa59a..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/io/IoUtils.java
+++ /dev/null
@@ -1,556 +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.io;
-
-import java.io.BufferedReader;
-import java.io.ByteArrayOutputStream;
-import java.io.Closeable;
-import java.io.EOFException;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.io.Reader;
-import java.net.URL;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.CopyOption;
-import java.nio.file.FileSystem;
-import java.nio.file.Files;
-import java.nio.file.LinkOption;
-import java.nio.file.OpenOption;
-import java.nio.file.Path;
-import java.nio.file.StandardOpenOption;
-import java.nio.file.attribute.FileAttribute;
-import java.nio.file.attribute.PosixFilePermission;
-import java.nio.file.attribute.UserPrincipal;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.List;
-import java.util.Objects;
-import java.util.Set;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.OsUtils;
-
-/**
- * TODO Add javadoc
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public final class IoUtils {
-
-    public static final OpenOption[] EMPTY_OPEN_OPTIONS = new OpenOption[0];
-    public static final CopyOption[] EMPTY_COPY_OPTIONS = new CopyOption[0];
-    public static final LinkOption[] EMPTY_LINK_OPTIONS = new LinkOption[0];
-    public static final FileAttribute<?>[] EMPTY_FILE_ATTRIBUTES = new FileAttribute<?>[0];
-
-    public static final List<String> WINDOWS_EXECUTABLE_EXTENSIONS = Collections.unmodifiableList(Arrays.asList(".bat", ".exe", ".cmd"));
-
-    /**
-     * Size of preferred work buffer when reading / writing data to / from streams
-     */
-    public static final int DEFAULT_COPY_SIZE = 8192;
-
-    /**
-     * The local O/S line separator
-     */
-    public static final String EOL = System.lineSeparator();
-
-    /**
-     * A {@link Set} of {@link StandardOpenOption}-s that indicate an intent
-     * to create/modify a file
-     */
-    public static final Set<StandardOpenOption> WRITEABLE_OPEN_OPTIONS =
-        Collections.unmodifiableSet(
-            EnumSet.of(
-                StandardOpenOption.APPEND, StandardOpenOption.CREATE,
-                StandardOpenOption.CREATE_NEW, StandardOpenOption.DELETE_ON_CLOSE,
-                StandardOpenOption.DSYNC, StandardOpenOption.SYNC,
-                StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE));
-
-    private static final byte[] EOL_BYTES = EOL.getBytes(StandardCharsets.UTF_8);
-
-    private static final LinkOption[] NO_FOLLOW_OPTIONS = new LinkOption[]{LinkOption.NOFOLLOW_LINKS};
-
-    /**
-     * Private Constructor
-     */
-    private IoUtils() {
-        throw new UnsupportedOperationException("No instance allowed");
-    }
-
-    /**
-     * @return The local platform line separator bytes as UTF-8. <B>Note:</B>
-     * each call returns a <U>new</U> instance in order to avoid inadvertent
-     * changes in shared objects
-     * @see #EOL
-     */
-    public static byte[] getEOLBytes() {
-        return EOL_BYTES.clone();
-    }
-
-    public static LinkOption[] getLinkOptions(boolean followLinks) {
-        if (followLinks) {
-            return EMPTY_LINK_OPTIONS;
-        } else {    // return a clone that modifications to the array will not affect others
-            return NO_FOLLOW_OPTIONS.clone();
-        }
-    }
-
-    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];
-        for (int n = source.read(buf); n > 0; n = source.read(buf)) {
-            sink.write(buf, 0, n);
-            nread += n;
-        }
-
-        return nread;
-    }
-
-    /**
-     * Closes a bunch of resources suppressing any {@link IOException}s their
-     * {@link Closeable#close()} method may have thrown
-     *
-     * @param closeables The {@link Closeable}s to close
-     * @return The <U>first</U> {@link IOException} that occurred during closing
-     * of a resource - if more than one exception occurred, they are added as
-     * suppressed exceptions to the first one
-     * @see Throwable#getSuppressed()
-     */
-    @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
-    public static IOException closeQuietly(Closeable... closeables) {
-        IOException err = null;
-        for (Closeable c : closeables) {
-            try {
-                if (c != null) {
-                    c.close();
-                }
-            } catch (IOException e) {
-                err = GenericUtils.accumulateException(err, e);
-            }
-        }
-
-        return err;
-    }
-
-    /**
-     * @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}
-     * @param options The {@link LinkOption}s to use when querying the permissions
-     * @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, LinkOption... options) throws IOException {
-        FileSystem fs = path.getFileSystem();
-        Collection<String> views = fs.supportedFileAttributeViews();
-        if (views.contains("posix")) {
-            return Files.getPosixFilePermissions(path, options);
-        } 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 (isExecutable(f)) {
-            perms.add(PosixFilePermission.OWNER_EXECUTE);
-            perms.add(PosixFilePermission.GROUP_EXECUTE);
-            perms.add(PosixFilePermission.OTHERS_EXECUTE);
-        }
-
-        return perms;
-    }
-
-    public static boolean isExecutable(File f) {
-        if (f == null) {
-            return false;
-        }
-
-        if (OsUtils.isWin32()) {
-            return isWindowsExecutable(f.getName());
-        } else {
-            return f.canExecute();
-        }
-    }
-
-    /**
-     * 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>Get file owner.</P>
-     *
-     * @param path  The {@link Path}
-     * @param options The {@link LinkOption}s to use when querying the owner
-     * @return Owner of the file or null if unsupported. <B>Note:</B> for
-     * <I>Windows</I> it strips any prepended domain or group name
-     * @throws IOException If failed to access the file system
-     * @see Files#getOwner(Path, LinkOption...)
-     */
-    public static String getFileOwner(Path path, LinkOption... options) throws IOException {
-        try {
-            UserPrincipal principal = Files.getOwner(path, options);
-            String owner = (principal == null) ? null : principal.getName();
-            return OsUtils.getCanonicalUser(owner);
-        } catch (UnsupportedOperationException e) {
-            return null;
-        }
-    }
-
-    /**
-     * <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>
-     *
-     * <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 &ge; 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;
-    }
-
-    /**
-     * @param perms    The current {@link PosixFilePermission}s - ignored if {@code null}/empty
-     * @param excluded The permissions <U>not</U> allowed to exist - ignored if {@code null}/empty
-     * @return The violating {@link PosixFilePermission} - {@code null}
-     * if no violating permission found
-     */
-    public static PosixFilePermission validateExcludedPermissions(Collection<PosixFilePermission> perms, Collection<PosixFilePermission> excluded) {
-        if (GenericUtils.isEmpty(perms) || GenericUtils.isEmpty(excluded)) {
-            return null;
-        }
-
-        for (PosixFilePermission p : excluded) {
-            if (perms.contains(p)) {
-                return p;
-            }
-        }
-
-        return null;
-    }
-
-    /**
-     * @param path    The {@link Path} to check
-     * @param options The {@link LinkOption}s to use when checking if path is a directory
-     * @return The same input path if it is a directory
-     * @throws UnsupportedOperationException if input path not a directory
-     */
-    public static Path ensureDirectory(Path path, LinkOption... options) {
-        if (!Files.isDirectory(path, options)) {
-            throw new UnsupportedOperationException("Not a directory: " + path);
-        }
-        return path;
-    }
-
-    /**
-     * @param options The {@link LinkOption}s - OK if {@code null}/empty
-     * @return {@code true} if the link options are {@code null}/empty or do
-     * not contain {@link LinkOption#NOFOLLOW_LINKS}, {@code false} otherwise
-     * (i.e., the array is not empty and contains the special value)
-     */
-    public static boolean followLinks(LinkOption... options) {
-        if (GenericUtils.isEmpty(options)) {
-            return true;
-        }
-
-        for (LinkOption localLinkOption : options) {
-            if (localLinkOption == LinkOption.NOFOLLOW_LINKS) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    public static String appendPathComponent(String prefix, String component) {
-        if (GenericUtils.isEmpty(prefix)) {
-            return component;
-        }
-
-        if (GenericUtils.isEmpty(component)) {
-            return prefix;
-        }
-
-        StringBuilder sb = new StringBuilder(prefix.length() + component.length() + File.separator.length()).append(prefix);
-
-        if (sb.charAt(prefix.length() - 1) == File.separatorChar) {
-            if (component.charAt(0) == File.separatorChar) {
-                sb.append(component.substring(1));
-            } else {
-                sb.append(component);
-            }
-        } else {
-            if (component.charAt(0) != File.separatorChar) {
-                sb.append(File.separatorChar);
-            }
-            sb.append(component);
-        }
-
-        return sb.toString();
-    }
-
-    public static byte[] toByteArray(InputStream inStream) throws IOException {
-        try (ByteArrayOutputStream baos = new ByteArrayOutputStream(DEFAULT_COPY_SIZE)) {
-            copy(inStream, baos);
-            return baos.toByteArray();
-        }
-    }
-
-    /**
-     * Reads all lines until no more available
-     *
-     * @param url The {@link URL} to read from
-     * @return The {@link List} of lines in the same <U>order</U> as it was read
-     * @throws IOException If failed to read the lines
-     * @see #readAllLines(InputStream)
-     */
-    public static List<String> readAllLines(URL url) throws IOException {
-        try (InputStream stream = Objects.requireNonNull(url, "No URL").openStream()) {
-            return readAllLines(stream);
-        }
-    }
-
-    /**
-     * Reads all lines until no more available
-     *
-     * @param stream The {@link InputStream} - <B>Note:</B> assumed to
-     * contain {@code UTF-8} encoded data
-     * @return The {@link List} of lines in the same <U>order</U> as it was read
-     * @throws IOException If failed to read the lines
-     * @see #readAllLines(Reader)
-     */
-    public static List<String> readAllLines(InputStream stream) throws IOException {
-        try (Reader reader = new InputStreamReader(Objects.requireNonNull(stream, "No stream instance"), StandardCharsets.UTF_8)) {
-            return readAllLines(reader);
-        }
-    }
-
-    public static List<String> readAllLines(Reader reader) throws IOException {
-        try (BufferedReader br = new BufferedReader(Objects.requireNonNull(reader, "No reader instance"), DEFAULT_COPY_SIZE)) {
-            return readAllLines(br);
-        }
-    }
-
-    /**
-     * Reads all lines until no more available
-     *
-     * @param reader The {@link BufferedReader} to read all lines
-     * @return The {@link List} of lines in the same <U>order</U> as it was read
-     * @throws IOException If failed to read the lines
-     * @see #readAllLines(BufferedReader, int)
-     */
-    public static List<String> readAllLines(BufferedReader reader) throws IOException {
-        return readAllLines(reader, -1);
-    }
-
-    /**
-     * Reads all lines until no more available
-     *
-     * @param reader The {@link BufferedReader} to read all lines
-     * @param lineCountHint A hint as to the expected number of lines - non-positive
-     * means unknown - in which case some initial default value will be used to
-     * initialize the list used to accumulate the lines.
-     * @return The {@link List} of lines in the same <U>order</U> as it was read
-     * @throws IOException If failed to read the lines
-     */
-    public static List<String> readAllLines(BufferedReader reader, int lineCountHint) throws IOException {
-        List<String> result = new ArrayList<>(Math.max(lineCountHint, Short.SIZE));
-        for (String line = reader.readLine(); line != null; line = reader.readLine()) {
-            result.add(line);
-        }
-        return result;
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/io/LimitInputStream.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/io/LimitInputStream.java b/sshd-core/src/main/java/org/apache/sshd/common/util/io/LimitInputStream.java
deleted file mode 100644
index bbf956a..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/io/LimitInputStream.java
+++ /dev/null
@@ -1,113 +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.io;
-
-import java.io.FilterInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.channels.Channel;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-/**
- * Reads from another {@link InputStream} up to specified max. length
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class LimitInputStream extends FilterInputStream implements Channel {
-    private final AtomicBoolean open = new AtomicBoolean(true);
-    private long remaining;
-
-    public LimitInputStream(InputStream in, long length) {
-        super(in);
-        remaining = length;
-    }
-
-    @Override
-    public boolean isOpen() {
-        return open.get();
-    }
-
-    @Override
-    public int read() throws IOException {
-        if (!isOpen()) {
-            throw new IOException("read() - stream is closed (remaining=" + remaining + ")");
-        }
-
-        if (remaining > 0) {
-            remaining--;
-            return super.read();
-        } else {
-            return -1;
-        }
-    }
-
-    @Override
-    public int read(byte[] b, int off, int len) throws IOException {
-        if (!isOpen()) {
-            throw new IOException("read(len=" + len + ") stream is closed (remaining=" + remaining + ")");
-        }
-
-        int nb = len;
-        if (nb > remaining) {
-            nb = (int) remaining;
-        }
-        if (nb > 0) {
-            int read = super.read(b, off, nb);
-            remaining -= read;
-            return read;
-        } else {
-            return -1;
-        }
-    }
-
-    @Override
-    public long skip(long n) throws IOException {
-        if (!isOpen()) {
-            throw new IOException("skip(" + n + ") stream is closed (remaining=" + remaining + ")");
-        }
-
-        long skipped = super.skip(n);
-        remaining -= skipped;
-        return skipped;
-    }
-
-    @Override
-    public int available() throws IOException {
-        if (!isOpen()) {
-            throw new IOException("available() stream is closed (remaining=" + remaining + ")");
-        }
-
-        int av = super.available();
-        if (av > remaining) {
-            return (int) remaining;
-        } else {
-            return av;
-        }
-    }
-
-    @Override
-    public void close() throws IOException {
-        // do not close the original input stream since it serves for ACK(s)
-        if (open.getAndSet(false)) {
-            //noinspection UnnecessaryReturnStatement
-            return; // debug breakpoint
-        }
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/io/LoggingFilterOutputStream.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/io/LoggingFilterOutputStream.java b/sshd-core/src/main/java/org/apache/sshd/common/util/io/LoggingFilterOutputStream.java
deleted file mode 100644
index ff4dbb6..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/io/LoggingFilterOutputStream.java
+++ /dev/null
@@ -1,67 +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.io;
-
-import java.io.FilterOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import org.apache.sshd.common.PropertyResolver;
-import org.apache.sshd.common.util.buffer.BufferUtils;
-import org.apache.sshd.common.util.logging.LoggingUtils;
-import org.apache.sshd.common.util.logging.SimplifiedLog;
-import org.slf4j.Logger;
-
-/**
- * Dumps everything that is written to the stream to the logger
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class LoggingFilterOutputStream extends FilterOutputStream {
-
-    private final String msg;
-    private final SimplifiedLog log;
-    private final int chunkSize;
-    private final AtomicInteger writeCount = new AtomicInteger(0);
-
-    public LoggingFilterOutputStream(OutputStream out, String msg, Logger log, PropertyResolver resolver) {
-        this(out, msg, log, resolver.getIntProperty(BufferUtils.HEXDUMP_CHUNK_SIZE, BufferUtils.DEFAULT_HEXDUMP_CHUNK_SIZE));
-    }
-
-    public LoggingFilterOutputStream(OutputStream out, String msg, Logger log, int chunkSize) {
-        super(out);
-        this.msg = msg;
-        this.log = LoggingUtils.wrap(log);
-        this.chunkSize = chunkSize;
-    }
-
-    @Override
-    public void write(int b) throws IOException {
-        byte[] d = new byte[1];
-        d[0] = (byte) b;
-        write(d, 0, 1);
-    }
-
-    @Override
-    public void write(byte[] b, int off, int len) throws IOException {
-        int count = writeCount.incrementAndGet();
-        BufferUtils.dumpHex(log, BufferUtils.DEFAULT_HEXDUMP_LEVEL, msg + "[" + count + "]", BufferUtils.DEFAULT_HEX_SEPARATOR, chunkSize, b, off, len);
-        out.write(b, off, len);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/io/ModifiableFileWatcher.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/io/ModifiableFileWatcher.java b/sshd-core/src/main/java/org/apache/sshd/common/util/io/ModifiableFileWatcher.java
deleted file mode 100644
index 032260b..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/io/ModifiableFileWatcher.java
+++ /dev/null
@@ -1,258 +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.io;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.LinkOption;
-import java.nio.file.Path;
-import java.nio.file.attribute.BasicFileAttributes;
-import java.nio.file.attribute.FileTime;
-import java.nio.file.attribute.PosixFilePermission;
-import java.util.AbstractMap.SimpleImmutableEntry;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.HashSet;
-import java.util.Objects;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicLong;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.OsUtils;
-import org.apache.sshd.common.util.logging.AbstractLoggingBean;
-
-/**
- * Watches over changes for a file and re-loads them if file has changed - including
- * if file is deleted or (re-)created
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class ModifiableFileWatcher extends AbstractLoggingBean {
-
-    /**
-     * The {@link Set} of {@link PosixFilePermission} <U>not</U> allowed if strict
-     * permissions are enforced on key files
-     */
-    public static final Set<PosixFilePermission> STRICTLY_PROHIBITED_FILE_PERMISSION =
-            Collections.unmodifiableSet(
-                    EnumSet.of(PosixFilePermission.GROUP_WRITE, PosixFilePermission.OTHERS_WRITE));
-
-    protected final LinkOption[] options;
-
-    private final Path file;
-    private final AtomicBoolean lastExisted = new AtomicBoolean(false);
-    private final AtomicLong lastSize = new AtomicLong(Long.MIN_VALUE);
-    private final AtomicLong lastModified = new AtomicLong(-1L);
-
-    public ModifiableFileWatcher(File file) {
-        this(Objects.requireNonNull(file, "No file to watch").toPath());
-    }
-
-    public ModifiableFileWatcher(Path file) {
-        this(file, IoUtils.getLinkOptions(true));
-    }
-
-    public ModifiableFileWatcher(Path file, LinkOption... options) {
-        this.file = Objects.requireNonNull(file, "No path to watch");
-        // use a clone to avoid being sensitive to changes in the passed array
-        this.options = (options == null) ? IoUtils.EMPTY_LINK_OPTIONS : options.clone();
-    }
-
-    /**
-     * @return The watched {@link Path}
-     */
-    public final Path getPath() {
-        return file;
-    }
-
-    public final boolean exists() throws IOException {
-        return Files.exists(getPath(), options);
-    }
-
-    public final long size() throws IOException {
-        if (exists()) {
-            return Files.size(getPath());
-        } else {
-            return -1L;
-        }
-    }
-
-    public final FileTime lastModified() throws IOException {
-        if (exists()) {
-            BasicFileAttributes attrs = Files.readAttributes(getPath(), BasicFileAttributes.class, options);
-            return attrs.lastModifiedTime();
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * @return {@code true} if the watched file has probably been changed
-     * @throws IOException If failed to query file data
-     */
-    public boolean checkReloadRequired() throws IOException {
-        boolean exists = exists();
-        // if existence state changed from last time
-        if (exists != lastExisted.getAndSet(exists)) {
-            return true;
-        }
-
-        if (!exists) {
-            // file did not exist and still does not exist
-            resetReloadAttributes();
-            return false;
-        }
-
-        long size = size();
-        if (size < 0L) {
-            // means file no longer exists
-            resetReloadAttributes();
-            return true;
-        }
-
-        // if size changed then obviously need reload
-        if (size != lastSize.getAndSet(size)) {
-            return true;
-        }
-
-        FileTime modifiedTime = lastModified();
-        if (modifiedTime == null) {
-            // means file no longer exists
-            resetReloadAttributes();
-            return true;
-        }
-
-        long timestamp = modifiedTime.toMillis();
-        return timestamp != lastModified.getAndSet(timestamp);
-
-    }
-
-    /**
-     * Resets the state attributes used to detect changes to the initial
-     * construction values - i.e., file assumed not to exist and no known
-     * size of modify time
-     */
-    public void resetReloadAttributes() {
-        lastExisted.set(false);
-        lastSize.set(Long.MIN_VALUE);
-        lastModified.set(-1L);
-    }
-
-    /**
-     * May be called to refresh the state attributes used to detect changes
-     * e.g., file existence, size and last-modified time once re-loading is
-     * successfully completed. If the file does not exist then the attributes
-     * are reset to an &quot;unknown&quot; state.
-     *
-     * @throws IOException If failed to access the file (if exists)
-     * @see #resetReloadAttributes()
-     */
-    public void updateReloadAttributes() throws IOException {
-        if (exists()) {
-            long size = size();
-            FileTime modifiedTime = lastModified();
-
-            if ((size >= 0L) && (modifiedTime != null)) {
-                lastExisted.set(true);
-                lastSize.set(size);
-                lastModified.set(modifiedTime.toMillis());
-                return;
-            }
-        }
-
-        resetReloadAttributes();
-    }
-
-    @Override
-    public String toString() {
-        return Objects.toString(getPath());
-    }
-
-    /**
-     * <P>Checks if a path has strict permissions</P>
-     * <UL>
-     *
-     * <LI><P>
-     * (For {@code Unix}) The path may not have group or others write permissions
-     * </P></LI>
-     *
-     * <LI><P>
-     * The path must be owned by current user.
-     * </P></LI>
-     *
-     * <LI><P>
-     * (For {@code Unix}) The path may be owned by root.
-     * </P></LI>
-     *
-     * </UL>
-     *
-     * @param path    The {@link Path} to be checked - ignored if {@code null}
-     *                or does not exist
-     * @param options The {@link LinkOption}s to use to query the file's permissions
-     * @return The violated permission as {@link SimpleImmutableEntry} where key
-     * is a loggable message and value is the offending object
-     * - e.g., {@link PosixFilePermission} or {@link String} for owner. Return
-     * value is {@code null} if no violations detected
-     * @throws IOException If failed to retrieve the permissions
-     * @see #STRICTLY_PROHIBITED_FILE_PERMISSION
-     */
-    public static SimpleImmutableEntry<String, Object> validateStrictConfigFilePermissions(Path path, LinkOption... options) throws IOException {
-        if ((path == null) || (!Files.exists(path, options))) {
-            return null;
-        }
-
-        Collection<PosixFilePermission> perms = IoUtils.getPermissions(path, options);
-        if (GenericUtils.isEmpty(perms)) {
-            return null;
-        }
-
-        if (OsUtils.isUNIX()) {
-            PosixFilePermission p = IoUtils.validateExcludedPermissions(perms, STRICTLY_PROHIBITED_FILE_PERMISSION);
-            if (p != null) {
-                return new SimpleImmutableEntry<>(String.format("Permissions violation (%s)", p), p);
-            }
-        }
-
-        String owner = IoUtils.getFileOwner(path, options);
-        if (GenericUtils.isEmpty(owner)) {
-            // we cannot get owner
-            // general issue: jvm does not support permissions
-            // security issue: specific filesystem does not support permissions
-            return null;
-        }
-
-        String current = OsUtils.getCurrentUser();
-        Set<String> expected = new HashSet<>();
-        expected.add(current);
-        if (OsUtils.isUNIX()) {
-            // Windows "Administrator" was considered however in Windows most likely a group is used.
-            expected.add(OsUtils.ROOT_USER);
-        }
-
-        if (!expected.contains(owner)) {
-            return new SimpleImmutableEntry<>(String.format("Owner violation (%s)", owner), owner);
-        }
-
-        return null;
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/io/NoCloseInputStream.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/io/NoCloseInputStream.java b/sshd-core/src/main/java/org/apache/sshd/common/util/io/NoCloseInputStream.java
deleted file mode 100644
index b9aedee..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/io/NoCloseInputStream.java
+++ /dev/null
@@ -1,47 +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.io;
-
-import java.io.FilterInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * TODO Add javadoc
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class NoCloseInputStream extends FilterInputStream {
-    public NoCloseInputStream(InputStream in) {
-        super(in);
-    }
-
-    @Override
-    public void close() throws IOException {
-        // ignored
-    }
-
-    public static InputStream resolveInputStream(InputStream input, boolean okToClose) {
-        if ((input == null) || okToClose) {
-            return input;
-        } else {
-            return new NoCloseInputStream(input);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/io/NoCloseOutputStream.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/io/NoCloseOutputStream.java b/sshd-core/src/main/java/org/apache/sshd/common/util/io/NoCloseOutputStream.java
deleted file mode 100644
index 4ba16d3..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/io/NoCloseOutputStream.java
+++ /dev/null
@@ -1,47 +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.io;
-
-import java.io.FilterOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-
-/**
- * TODO Add javadoc
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class NoCloseOutputStream extends FilterOutputStream {
-    public NoCloseOutputStream(OutputStream out) {
-        super(out);
-    }
-
-    @Override
-    public void close() throws IOException {
-        // ignored
-    }
-
-    public static OutputStream resolveOutputStream(OutputStream output, boolean okToClose) {
-        if ((output == null) || okToClose) {
-            return output;
-        } else {
-            return new NoCloseOutputStream(output);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/io/NoCloseReader.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/io/NoCloseReader.java b/sshd-core/src/main/java/org/apache/sshd/common/util/io/NoCloseReader.java
deleted file mode 100644
index 9c8b218..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/io/NoCloseReader.java
+++ /dev/null
@@ -1,46 +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.io;
-
-import java.io.FilterReader;
-import java.io.IOException;
-import java.io.Reader;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class NoCloseReader extends FilterReader {
-    public NoCloseReader(Reader in) {
-        super(in);
-    }
-
-    @Override
-    public void close() throws IOException {
-        // ignored
-    }
-
-    public static Reader resolveReader(Reader r, boolean okToClose) {
-        if ((r == null) || okToClose) {
-            return r;
-        } else {
-            return new NoCloseReader(r);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/io/NoCloseWriter.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/io/NoCloseWriter.java b/sshd-core/src/main/java/org/apache/sshd/common/util/io/NoCloseWriter.java
deleted file mode 100644
index 0f92697..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/io/NoCloseWriter.java
+++ /dev/null
@@ -1,46 +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.io;
-
-import java.io.FilterWriter;
-import java.io.IOException;
-import java.io.Writer;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class NoCloseWriter extends FilterWriter {
-    public NoCloseWriter(Writer out) {
-        super(out);
-    }
-
-    @Override
-    public void close() throws IOException {
-        // ignored
-    }
-
-    public static Writer resolveWriter(Writer r, boolean okToClose) {
-        if ((r == null) || okToClose) {
-            return r;
-        } else {
-            return new NoCloseWriter(r);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/io/NullInputStream.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/io/NullInputStream.java b/sshd-core/src/main/java/org/apache/sshd/common/util/io/NullInputStream.java
deleted file mode 100644
index eb21383..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/io/NullInputStream.java
+++ /dev/null
@@ -1,90 +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.io;
-
-import java.io.EOFException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.channels.Channel;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-/**
- * A {@code /dev/null} input stream
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class NullInputStream extends InputStream implements Channel {
-    private final AtomicBoolean open = new AtomicBoolean(true);
-
-    public NullInputStream() {
-        super();
-    }
-
-    @Override
-    public boolean isOpen() {
-        return open.get();
-    }
-
-    @Override
-    public int read() throws IOException {
-        if (!isOpen()) {
-            throw new EOFException("Stream is closed for reading one value");
-        }
-        return -1;
-    }
-
-    @Override
-    public int read(byte[] b, int off, int len) throws IOException {
-        if (!isOpen()) {
-            throw new EOFException("Stream is closed for reading " + len + " bytes");
-        }
-        return -1;
-    }
-
-    @Override
-    public long skip(long n) throws IOException {
-        if (!isOpen()) {
-            throw new EOFException("Stream is closed for skipping " + n + " bytes");
-        }
-        return 0L;
-    }
-
-    @Override
-    public int available() throws IOException {
-        if (!isOpen()) {
-            throw new EOFException("Stream is closed for availability query");
-        }
-        return 0;
-    }
-
-    @Override
-    public synchronized void reset() throws IOException {
-        if (!isOpen()) {
-            throw new EOFException("Stream is closed for reset");
-        }
-    }
-
-    @Override
-    public void close() throws IOException {
-        if (open.getAndSet(false)) {
-            //noinspection UnnecessaryReturnStatement
-            return; // debug breakpoint
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/io/NullOutputStream.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/io/NullOutputStream.java b/sshd-core/src/main/java/org/apache/sshd/common/util/io/NullOutputStream.java
deleted file mode 100644
index 67fa2d0..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/io/NullOutputStream.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.common.util.io;
-
-import java.io.EOFException;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.channels.Channel;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-/**
- * A {code /dev/null} output stream
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class NullOutputStream extends OutputStream implements Channel {
-    private final AtomicBoolean open = new AtomicBoolean(true);
-
-    public NullOutputStream() {
-        super();
-    }
-
-    @Override
-    public boolean isOpen() {
-        return open.get();
-    }
-
-    @Override
-    public void write(int b) throws IOException {
-        if (!isOpen()) {
-            throw new EOFException("Stream is closed for writing one byte");
-        }
-    }
-
-    @Override
-    public void write(byte[] b, int off, int len) throws IOException {
-        if (!isOpen()) {
-            throw new EOFException("Stream is closed for writing " + len + " bytes");
-        }
-    }
-
-    @Override
-    public void flush() throws IOException {
-        if (!isOpen()) {
-            throw new EOFException("Stream is closed for flushing");
-        }
-    }
-
-    @Override
-    public void close() throws IOException {
-        if (open.getAndSet(false)) {
-            //noinspection UnnecessaryReturnStatement
-            return; // debug breakpoint
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/io/OutputStreamWithChannel.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/io/OutputStreamWithChannel.java b/sshd-core/src/main/java/org/apache/sshd/common/util/io/OutputStreamWithChannel.java
deleted file mode 100644
index 6f81872..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/io/OutputStreamWithChannel.java
+++ /dev/null
@@ -1,32 +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.io;
-
-import java.io.OutputStream;
-import java.nio.channels.Channel;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class OutputStreamWithChannel extends OutputStream implements Channel {
-    protected OutputStreamWithChannel() {
-        super();
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/io/der/ASN1Class.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/io/der/ASN1Class.java b/sshd-core/src/main/java/org/apache/sshd/common/util/io/der/ASN1Class.java
deleted file mode 100644
index b8351f1..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/io/der/ASN1Class.java
+++ /dev/null
@@ -1,93 +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.io.der;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-import org.apache.sshd.common.util.GenericUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public enum ASN1Class {
-    // NOTE: order is crucial, so DON'T change it
-    UNIVERSAL((byte) 0x00),
-    APPLICATION((byte) 0x01),
-    CONTEXT((byte) 0x02),
-    PRIVATE((byte) 0x03);
-
-    public static final List<ASN1Class>  VALUES =
-            Collections.unmodifiableList(Arrays.asList(values()));
-
-    private final byte  byteValue;
-
-    ASN1Class(byte classValue) {
-        byteValue = classValue;
-    }
-
-    public byte getClassValue() {
-        return byteValue;
-    }
-
-    public static ASN1Class fromName(String s) {
-        if (GenericUtils.isEmpty(s)) {
-            return null;
-        }
-
-        for (ASN1Class c : VALUES) {
-            if (s.equalsIgnoreCase(c.name())) {
-                return c;
-            }
-        }
-
-        return null;
-    }
-
-    /**
-     * <P>The first byte in DER encoding is made of following fields</P>
-     * <pre>
-     *-------------------------------------------------
-     *|Bit 8|Bit 7|Bit 6|Bit 5|Bit 4|Bit 3|Bit 2|Bit 1|
-     *-------------------------------------------------
-     *|  Class    | CF  |        Type                 |
-     *-------------------------------------------------
-     * </pre>
-     * @param value The original DER encoded byte
-     * @return The {@link ASN1Class} value - {@code null} if no match found
-     * @see #fromTypeValue(int)
-     */
-    public static ASN1Class fromDERValue(int value) {
-        return fromTypeValue((value >> 6) & 0x03);
-    }
-
-    /**
-     * @param value The &quot;pure&quot; value - unshifted and with no extras
-     * @return The {@link ASN1Class} value - {@code null} if no match found
-     */
-    public static ASN1Class fromTypeValue(int value) {
-        // all 4 values are defined
-        if ((value < 0) || (value >= VALUES.size())) {
-            return null;
-        }
-
-        return VALUES.get(value);
-    }
-}


[50/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common utilities code from sshd-core into sshd-common (new artifact)

Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/ConfigFileHostEntryResolver.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/ConfigFileHostEntryResolver.java b/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/ConfigFileHostEntryResolver.java
new file mode 100644
index 0000000..f1c9ea8
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/ConfigFileHostEntryResolver.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.client.config.hosts;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.LinkOption;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.common.util.io.ModifiableFileWatcher;
+
+/**
+ * Watches for changes in a configuration file and automatically reloads any changes
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class ConfigFileHostEntryResolver extends ModifiableFileWatcher implements HostConfigEntryResolver {
+    private final AtomicReference<HostConfigEntryResolver> delegateHolder = // assumes initially empty
+            new AtomicReference<>(HostConfigEntryResolver.EMPTY);
+
+    public ConfigFileHostEntryResolver(File file) {
+        this(Objects.requireNonNull(file, "No file to watch").toPath());
+    }
+
+    public ConfigFileHostEntryResolver(Path file) {
+        this(file, IoUtils.EMPTY_LINK_OPTIONS);
+    }
+
+    public ConfigFileHostEntryResolver(Path file, LinkOption... options) {
+        super(file, options);
+    }
+
+    @Override
+    public HostConfigEntry resolveEffectiveHost(String host, int port, String username) throws IOException {
+        try {
+            HostConfigEntryResolver delegate = Objects.requireNonNull(resolveEffectiveResolver(host, port, username), "No delegate");
+            HostConfigEntry entry = delegate.resolveEffectiveHost(host, port, username);
+            if (log.isDebugEnabled()) {
+                log.debug("resolveEffectiveHost({}@{}:{}) => {}", username, host, port, entry);
+            }
+
+            return entry;
+        } catch (Throwable e) {
+            if (log.isDebugEnabled()) {
+                log.debug("resolveEffectiveHost({}@{}:{}) failed ({}) to resolve: {}",
+                          username, host, port, e.getClass().getSimpleName(), e.getMessage());
+            }
+
+            if (log.isTraceEnabled()) {
+                log.trace("resolveEffectiveHost(" + username + "@" + host + ":" + port + ") resolution failure details", e);
+            }
+            if (e instanceof IOException) {
+                throw (IOException) e;
+            } else {
+                throw new IOException(e);
+            }
+        }
+    }
+
+    protected HostConfigEntryResolver resolveEffectiveResolver(String host, int port, String username) throws IOException {
+        if (checkReloadRequired()) {
+            delegateHolder.set(HostConfigEntryResolver.EMPTY);  // start fresh
+
+            Path path = getPath();
+            if (exists()) {
+                Collection<HostConfigEntry> entries = reloadHostConfigEntries(path, host, port, username);
+                if (GenericUtils.size(entries) > 0) {
+                    delegateHolder.set(HostConfigEntry.toHostConfigEntryResolver(entries));
+                }
+            } else {
+                log.info("resolveEffectiveResolver({}@{}:{}) no configuration file at {}", username, host, port, path);
+            }
+        }
+
+        return delegateHolder.get();
+    }
+
+    protected List<HostConfigEntry> reloadHostConfigEntries(Path path, String host, int port, String username) throws IOException {
+        List<HostConfigEntry> entries = HostConfigEntry.readHostConfigEntries(path);
+        log.info("resolveEffectiveResolver({}@{}:{}) loaded {} entries from {}", username, host, port, GenericUtils.size(entries), path);
+        updateReloadAttributes();
+        return entries;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/DefaultConfigFileHostEntryResolver.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/DefaultConfigFileHostEntryResolver.java b/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/DefaultConfigFileHostEntryResolver.java
new file mode 100644
index 0000000..29bc355
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/DefaultConfigFileHostEntryResolver.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.client.config.hosts;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.LinkOption;
+import java.nio.file.Path;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+import org.apache.sshd.common.util.io.IoUtils;
+
+/**
+ * Monitors the {@code ~/.ssh/config} file of the user currently running
+ * the client, re-loading it if necessary. It also (optionally) enforces
+ * the same permissions regime as {@code OpenSSH}
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class DefaultConfigFileHostEntryResolver extends ConfigFileHostEntryResolver {
+    /**
+     * The default instance that enforces the same permissions regime as {@code OpenSSH}
+     */
+    public static final DefaultConfigFileHostEntryResolver INSTANCE = new DefaultConfigFileHostEntryResolver(true);
+
+    private final boolean strict;
+
+    /**
+     * @param strict If {@code true} then makes sure that the containing folder
+     * has 0700 access and the file 0644. <B>Note:</B> for <I>Windows</I> it
+     * does not check these permissions
+     * @see #validateStrictConfigFilePermissions(Path, LinkOption...)
+     */
+    public DefaultConfigFileHostEntryResolver(boolean strict) {
+        this(HostConfigEntry.getDefaultHostConfigFile(), strict);
+    }
+
+    public DefaultConfigFileHostEntryResolver(File file, boolean strict) {
+        this(Objects.requireNonNull(file, "No file provided").toPath(), strict, IoUtils.getLinkOptions(true));
+    }
+
+    public DefaultConfigFileHostEntryResolver(Path path, boolean strict, LinkOption... options) {
+        super(path, options);
+        this.strict = strict;
+    }
+
+    /**
+     * @return If {@code true} then makes sure that the containing folder
+     * has 0700 access and the file 0644. <B>Note:</B> for <I>Windows</I> it
+     * does not check these permissions
+     * @see #validateStrictConfigFilePermissions(Path, LinkOption...)
+     */
+    public final boolean isStrict() {
+        return strict;
+    }
+
+    @Override
+    protected List<HostConfigEntry> reloadHostConfigEntries(Path path, String host, int port, String username) throws IOException {
+        if (isStrict()) {
+            if (log.isDebugEnabled()) {
+                log.debug("reloadHostConfigEntries({}@{}:{}) check permissions of {}", username, host, port, path);
+            }
+
+            Map.Entry<String, ?> violation = validateStrictConfigFilePermissions(path);
+            if (violation != null) {
+                log.warn("reloadHostConfigEntries({}@{}:{}) invalid file={} permissions: {}",
+                         username, host, port, path, violation.getKey());
+                updateReloadAttributes();
+                return Collections.emptyList();
+            }
+        }
+
+        return super.reloadHostConfigEntries(path, host, port, username);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntry.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntry.java b/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntry.java
new file mode 100644
index 0000000..8a05d75
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntry.java
@@ -0,0 +1,1169 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.client.config.hosts;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.StreamCorruptedException;
+import java.io.Writer;
+import java.net.InetAddress;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.OpenOption;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.TreeMap;
+
+import org.apache.sshd.common.auth.MutableUserHolder;
+import org.apache.sshd.common.config.ConfigFileReaderSupport;
+import org.apache.sshd.common.config.keys.IdentityUtils;
+import org.apache.sshd.common.config.keys.PublicKeyEntry;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.OsUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.common.util.io.NoCloseInputStream;
+import org.apache.sshd.common.util.io.NoCloseOutputStream;
+import org.apache.sshd.common.util.io.NoCloseReader;
+
+/**
+ * Represents an entry in the client's configuration file as defined by
+ * the <A HREF="http://www.gsp.com/cgi-bin/man.cgi?topic=ssh_config">configuration
+ * file format</A>
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class HostConfigEntry extends HostPatternsHolder implements MutableUserHolder {
+    /**
+     * Standard OpenSSH config file name
+     */
+    public static final String STD_CONFIG_FILENAME = "config";
+
+    public static final String HOST_CONFIG_PROP = "Host";
+    public static final String HOST_NAME_CONFIG_PROP = "HostName";
+    public static final String PORT_CONFIG_PROP = ConfigFileReaderSupport.PORT_CONFIG_PROP;
+    public static final String USER_CONFIG_PROP = "User";
+    public static final String IDENTITY_FILE_CONFIG_PROP = "IdentityFile";
+    /**
+     * Use only the identities specified in the host entry (if any)
+     */
+    public static final String EXCLUSIVE_IDENTITIES_CONFIG_PROP = "IdentitiesOnly";
+    public static final boolean DEFAULT_EXCLUSIVE_IDENTITIES = false;
+
+    /**
+     * A case <U>insensitive</U> {@link Set} of the properties that receive special handling
+     */
+    public static final Set<String> EXPLICIT_PROPERTIES =
+            Collections.unmodifiableSet(
+                    GenericUtils.asSortedSet(String.CASE_INSENSITIVE_ORDER,
+                            HOST_CONFIG_PROP, HOST_NAME_CONFIG_PROP, PORT_CONFIG_PROP,
+                            USER_CONFIG_PROP, IDENTITY_FILE_CONFIG_PROP, EXCLUSIVE_IDENTITIES_CONFIG_PROP
+                        ));
+
+    public static final String MULTI_VALUE_SEPARATORS = " ,";
+
+    public static final char HOME_TILDE_CHAR = '~';
+    public static final char PATH_MACRO_CHAR = '%';
+    public static final char LOCAL_HOME_MACRO = 'd';
+    public static final char LOCAL_USER_MACRO = 'u';
+    public static final char LOCAL_HOST_MACRO = 'l';
+    public static final char REMOTE_HOST_MACRO = 'h';
+    public static final char REMOTE_USER_MACRO = 'r';
+    // Extra - not part of the standard
+    public static final char REMOTE_PORT_MACRO = 'p';
+
+    private static final class LazyDefaultConfigFileHolder {
+        private static final Path CONFIG_FILE =
+            PublicKeyEntry.getDefaultKeysFolderPath().resolve(STD_CONFIG_FILENAME);
+
+        private LazyDefaultConfigFileHolder() {
+            throw new UnsupportedOperationException("No instance allowed");
+        }
+    }
+
+    private String host;
+    private String hostName;
+    private int port;
+    private String username;
+    private Boolean exclusiveIdentites;
+    private Collection<String> identities = Collections.emptyList();
+    private Map<String, String> properties = Collections.emptyMap();
+
+    public HostConfigEntry() {
+        super();
+    }
+
+    public HostConfigEntry(String pattern, String host, int port, String username) {
+        setHost(pattern);
+        setHostName(host);
+        setPort(port);
+        setUsername(username);
+    }
+
+    /**
+     * @return The <U>pattern(s)</U> represented by this entry
+     */
+    public String getHost() {
+        return host;
+    }
+
+    public void setHost(String host) {
+        this.host = host;
+        setPatterns(parsePatterns(parseConfigValue(host)));
+    }
+
+    public void setHost(Collection<String> patterns) {
+        this.host = GenericUtils.join(ValidateUtils.checkNotNullAndNotEmpty(patterns, "No patterns"), ',');
+        setPatterns(parsePatterns(patterns));
+    }
+
+    /**
+     * @return The effective host name to connect to if the pattern matches
+     */
+    public String getHostName() {
+        return hostName;
+    }
+
+    public void setHostName(String hostName) {
+        this.hostName = hostName;
+    }
+
+    public String resolveHostName(String originalHost) {
+        return resolveHostName(originalHost, getHostName());
+    }
+
+    /**
+     * @return A port override - if positive
+     */
+    public int getPort() {
+        return port;
+    }
+
+    public void setPort(int port) {
+        this.port = port;
+    }
+
+    /**
+     * Resolves the effective port to use
+     *
+     * @param originalPort The original requested port
+     * @return If the host entry port is positive, then it is used, otherwise
+     * the original requested port
+     * @see #resolvePort(int, int)
+     */
+    public int resolvePort(int originalPort) {
+        return resolvePort(originalPort, getPort());
+    }
+
+    /**
+     * @return A username override - if not {@code null}/empty
+     */
+    @Override
+    public String getUsername() {
+        return username;
+    }
+
+    @Override
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+    /**
+     * Resolves the effective username
+     *
+     * @param originalUser The original requested username
+     * @return If the configured host entry username is not {@code null}/empty
+     * then it is used, otherwise the original one.
+     * @see #resolveUsername(String)
+     */
+    public String resolveUsername(String originalUser) {
+        return resolveUsername(originalUser, getUsername());
+    }
+
+    /**
+     * @return The current identities file paths - may be {@code null}/empty
+     */
+    public Collection<String> getIdentities() {
+        return identities;
+    }
+
+    /**
+     * @param file A {@link File} that contains an identity key - never {@code null}
+     */
+    public void addIdentity(File file) {
+        addIdentity(Objects.requireNonNull(file, "No file").toPath());
+    }
+
+    /**
+     * @param path A {@link Path} to a file that contains an identity key
+     * - never {@code null}
+     */
+    public void addIdentity(Path path) {
+        addIdentity(Objects.requireNonNull(path, "No path").toAbsolutePath().normalize().toString());
+    }
+
+    /**
+     * Adds a path to an identity file
+     *
+     * @param id The identity path to add - never {@code null}
+     */
+    public void addIdentity(String id) {
+        String path = ValidateUtils.checkNotNullAndNotEmpty(id, "No identity provided");
+        if (GenericUtils.isEmpty(identities)) {
+            identities = new LinkedList<>();
+        }
+        identities.add(path);
+    }
+
+    public void setIdentities(Collection<String> identities) {
+        this.identities = (identities == null) ? Collections.emptyList() : identities;
+    }
+
+    /**
+     * @return {@code true} if must use only the identities in this entry
+     */
+    public boolean isIdentitiesOnly() {
+        return (exclusiveIdentites == null) ? DEFAULT_EXCLUSIVE_IDENTITIES : exclusiveIdentites;
+    }
+
+    public void setIdentitiesOnly(boolean identitiesOnly) {
+        exclusiveIdentites = identitiesOnly;
+    }
+
+    /**
+     * @return A {@link Map} of extra properties that have been read - may be
+     * {@code null}/empty, or even contain some values that have been parsed
+     * and set as members of the entry (e.g., host, port, etc.). <B>Note:</B>
+     * multi-valued keys use a comma-separated list of values
+     */
+    public Map<String, String> getProperties() {
+        return properties;
+    }
+
+    /**
+     * @param name Property name - never {@code null}/empty
+     * @return Property value or {@code null}  if no such property
+     * @see #getProperty(String, String)
+     */
+    public String getProperty(String name) {
+        return getProperty(name, null);
+    }
+
+    /**
+     * @param name Property name - never {@code null}/empty
+     * @param defaultValue Default value to return if no such property
+     * @return The property value or the default one if no such property
+     */
+    public String getProperty(String name, String defaultValue) {
+        String key = ValidateUtils.checkNotNullAndNotEmpty(name, "No property name");
+        Map<String, String> props = getProperties();
+        if (GenericUtils.isEmpty(props)) {
+            return null;
+        }
+
+        String value = props.get(key);
+        if (GenericUtils.isEmpty(value)) {
+            return defaultValue;
+        } else {
+            return value;
+        }
+    }
+
+    /**
+     * Updates the values that are <U>not</U> already configured with those
+     * from the global entry
+     *
+     * @param globalEntry The global entry - ignored if {@code null} or
+     * same reference as this entry
+     * @return {@code true} if anything updated
+     */
+    public boolean processGlobalValues(HostConfigEntry globalEntry) {
+        if ((globalEntry == null) || (this == globalEntry)) {
+            return false;
+        }
+
+        boolean modified = false;
+        /*
+         * NOTE !!! DO NOT TRY TO CHANGE THE ORDER OF THE OR-ing AS IT
+         * WOULD CAUSE INVALID CODE EXECUTION
+         */
+        modified = updateGlobalPort(globalEntry.getPort()) || modified;
+        modified = updateGlobalHostName(globalEntry.getHostName()) || modified;
+        modified = updateGlobalUserName(globalEntry.getUsername()) || modified;
+        modified = updateGlobalIdentities(globalEntry.getIdentities()) || modified;
+        modified = updateGlobalIdentityOnly(globalEntry.isIdentitiesOnly()) || modified;
+
+        Map<String, String> updated = updateGlobalProperties(globalEntry.getProperties());
+        modified = (GenericUtils.size(updated) > 0) || modified;
+
+        return modified;
+    }
+
+    /**
+     * Sets all the properties for which no current value exists in the entry
+     *
+     * @param props The global properties - ignored if {@code null}/empty
+     * @return A {@link Map} of the <U>updated</U> properties
+     */
+    public Map<String, String> updateGlobalProperties(Map<String, String> props) {
+        if (GenericUtils.isEmpty(props)) {
+            return Collections.emptyMap();
+        }
+
+        Map<String, String> updated = null;
+        // Cannot use forEach because of the modification of the updated map value (non-final)
+        for (Map.Entry<String, String> pe : props.entrySet()) {
+            String key = pe.getKey();
+            String curValue = getProperty(key);
+            if (GenericUtils.length(curValue) > 0) {
+                continue;
+            }
+
+            String newValue = pe.getValue();
+            setProperty(key, newValue);
+
+            if (updated == null) {
+                updated = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+            }
+
+            updated.put(key, newValue);
+        }
+
+        if (updated == null) {
+            return Collections.emptyMap();
+        } else {
+            return updated;
+        }
+    }
+
+    /**
+     * @param ids Global identities - ignored if {@code null}/empty or already
+     * have configured identities
+     * @return {@code true} if updated identities
+     */
+    public boolean updateGlobalIdentities(Collection<String> ids) {
+        if (GenericUtils.isEmpty(ids) || (GenericUtils.size(getIdentities()) > 0)) {
+            return false;
+        }
+
+        for (String id : ids) {
+            addIdentity(id);
+        }
+
+        return true;
+    }
+
+    /**
+     * @param user The global user name - ignored if {@code null}/empty or
+     * already have a configured user
+     * @return {@code true} if updated the username
+     */
+    public boolean updateGlobalUserName(String user) {
+        if (GenericUtils.isEmpty(user) || (GenericUtils.length(getUsername()) > 0)) {
+            return false;
+        }
+
+        setUsername(user);
+        return true;
+    }
+
+    /**
+     * @param name The global host name - ignored if {@code null}/empty or
+     * already have a configured target host
+     * @return {@code true} if updated the target host
+     */
+    public boolean updateGlobalHostName(String name) {
+        if (GenericUtils.isEmpty(name) || (GenericUtils.length(getHostName()) > 0)) {
+            return false;
+        }
+
+        setHostName(name);
+        return true;
+    }
+
+    /**
+     * @param portValue The global port value - ignored if not positive
+     * or already have a configured port
+     * @return {@code true} if updated the port value
+     */
+    public boolean updateGlobalPort(int portValue) {
+        if ((portValue <= 0) || (getPort() > 0)) {
+            return false;
+        }
+
+        setPort(portValue);
+        return true;
+    }
+
+    /**
+     * @param identitiesOnly Whether to use only the identities in this entry.
+     * Ignored if already set
+     * @return {@code true} if updated the option value
+     */
+    public boolean updateGlobalIdentityOnly(boolean identitiesOnly) {
+        if (exclusiveIdentites != null) {
+            return false;
+        }
+
+        setIdentitiesOnly(identitiesOnly);
+        return true;
+    }
+
+    /**
+     * @param name Property name - never {@code null}/empty
+     * @param valsList The available values for the property
+     * @param ignoreAlreadyInitialized If {@code false} and one of the &quot;known&quot;
+     * properties is encountered then throws an exception
+     * @throws IllegalArgumentException If an existing value is overwritten and
+     * <tt>ignoreAlreadyInitialized</tt> is {@code false} (except for {@link #IDENTITY_FILE_CONFIG_PROP}
+     * which is <U>cumulative</U>
+     * @see #HOST_NAME_CONFIG_PROP
+     * @see #PORT_CONFIG_PROP
+     * @see #USER_CONFIG_PROP
+     * @see #IDENTITY_FILE_CONFIG_PROP
+     */
+    public void processProperty(String name, Collection<String> valsList, boolean ignoreAlreadyInitialized) {
+        String key = ValidateUtils.checkNotNullAndNotEmpty(name, "No property name");
+        String joinedValue = GenericUtils.join(valsList, ',');
+        appendPropertyValue(key, joinedValue);
+
+        if (HOST_NAME_CONFIG_PROP.equalsIgnoreCase(key)) {
+            ValidateUtils.checkTrue(GenericUtils.size(valsList) == 1, "Multiple target hosts N/A: %s", joinedValue);
+
+            String curValue = getHostName();
+            ValidateUtils.checkTrue(GenericUtils.isEmpty(curValue) || ignoreAlreadyInitialized, "Already initialized %s: %s", key, curValue);
+            setHostName(joinedValue);
+        } else if (PORT_CONFIG_PROP.equalsIgnoreCase(key)) {
+            ValidateUtils.checkTrue(GenericUtils.size(valsList) == 1, "Multiple target ports N/A: %s", joinedValue);
+
+            int curValue = getPort();
+            ValidateUtils.checkTrue((curValue <= 0) || ignoreAlreadyInitialized, "Already initialized %s: %d", key, curValue);
+
+            int newValue = Integer.parseInt(joinedValue);
+            ValidateUtils.checkTrue(newValue > 0, "Bad new port value: %d", newValue);
+            setPort(newValue);
+        } else if (USER_CONFIG_PROP.equalsIgnoreCase(key)) {
+            ValidateUtils.checkTrue(GenericUtils.size(valsList) == 1, "Multiple target users N/A: %s", joinedValue);
+
+            String curValue = getUsername();
+            ValidateUtils.checkTrue(GenericUtils.isEmpty(curValue) || ignoreAlreadyInitialized, "Already initialized %s: %s", key, curValue);
+            setUsername(joinedValue);
+        } else if (IDENTITY_FILE_CONFIG_PROP.equalsIgnoreCase(key)) {
+            ValidateUtils.checkTrue(GenericUtils.size(valsList) > 0, "No identity files specified");
+            for (String id : valsList) {
+                addIdentity(id);
+            }
+        } else if (EXCLUSIVE_IDENTITIES_CONFIG_PROP.equalsIgnoreCase(key)) {
+            setIdentitiesOnly(
+                ConfigFileReaderSupport.parseBooleanValue(
+                    ValidateUtils.checkNotNullAndNotEmpty(joinedValue, "No identities option value")));
+        }
+    }
+
+    /**
+     * Appends a value using a <U>comma</U> to an existing one. If no previous
+     * value then same as calling {@link #setProperty(String, String)}.
+     *
+     * @param name Property name - never {@code null}/empty
+     * @param value The value to be appended - ignored if {@code null}/empty
+     * @return The value <U>before</U> appending - {@code null} if no previous value
+     */
+    public String appendPropertyValue(String name, String value) {
+        String key = ValidateUtils.checkNotNullAndNotEmpty(name, "No property name");
+        String curVal = getProperty(key);
+        if (GenericUtils.isEmpty(value)) {
+            return curVal;
+        }
+
+        if (GenericUtils.isEmpty(curVal)) {
+            return setProperty(key, value);
+        }
+
+        return setProperty(key, curVal + ',' + value);
+    }
+
+    /**
+     * Sets / Replaces the property value
+     *
+     * @param name Property name - never {@code null}/empty
+     * @param value Property value - if {@code null}/empty then
+     * {@link #removeProperty(String)} is called
+     * @return The previous property value - {@code null} if no such name
+     */
+    public String setProperty(String name, String value) {
+        if (GenericUtils.isEmpty(value)) {
+            return removeProperty(name);
+        }
+
+        String key = ValidateUtils.checkNotNullAndNotEmpty(name, "No property name");
+        if (GenericUtils.isEmpty(properties)) {
+            properties = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+        }
+
+        return properties.put(key, value);
+    }
+
+    /**
+     * @param name Property name - never {@code null}/empty
+     * @return The removed property value - {@code null} if no such property name
+     */
+    public String removeProperty(String name) {
+        String key = ValidateUtils.checkNotNullAndNotEmpty(name, "No property name");
+        Map<String, String> props = getProperties();
+        if (GenericUtils.isEmpty(props)) {
+            return null;
+        } else {
+            return props.remove(key);
+        }
+    }
+
+    /**
+     * @param properties The properties to set - if {@code null} then an empty
+     * map is effectively set. <B>Note:</B> it is highly recommended to use a
+     * <U>case insensitive</U> key mapper.
+     */
+    public void setProperties(Map<String, String> properties) {
+        this.properties = (properties == null) ? Collections.emptyMap() : properties;
+    }
+
+    public <A extends Appendable> A append(A sb) throws IOException {
+        sb.append(HOST_CONFIG_PROP).append(' ').append(ValidateUtils.checkNotNullAndNotEmpty(getHost(), "No host pattern")).append(IoUtils.EOL);
+        appendNonEmptyProperty(sb, HOST_NAME_CONFIG_PROP, getHostName());
+        appendNonEmptyPort(sb, PORT_CONFIG_PROP, getPort());
+        appendNonEmptyProperty(sb, USER_CONFIG_PROP, getUsername());
+        appendNonEmptyValues(sb, IDENTITY_FILE_CONFIG_PROP, getIdentities());
+        if (exclusiveIdentites != null) {
+            appendNonEmptyProperty(sb, EXCLUSIVE_IDENTITIES_CONFIG_PROP, ConfigFileReaderSupport.yesNoValueOf(exclusiveIdentites));
+        }
+        appendNonEmptyProperties(sb, getProperties());
+        return sb;
+    }
+
+    @Override
+    public String toString() {
+        return getHost() + ": " + getUsername() + "@" + getHostName() + ":" + getPort();
+    }
+
+    /**
+     * @param <A> The {@link Appendable} type
+     * @param sb The target appender
+     * @param name The property name - never {@code null}/empty
+     * @param port The port value - ignored if non-positive
+     * @return The target appender after having appended (or not) the value
+     * @throws IOException If failed to append the requested data
+     * @see #appendNonEmptyProperty(Appendable, String, Object)
+     */
+    public static <A extends Appendable> A appendNonEmptyPort(A sb, String name, int port) throws IOException {
+        return appendNonEmptyProperty(sb, name, (port > 0) ? Integer.toString(port) : null);
+    }
+
+    /**
+     * Appends the extra properties - while skipping the {@link #EXPLICIT_PROPERTIES} ones
+     *
+     * @param <A> The {@link Appendable} type
+     * @param sb The target appender
+     * @param props The {@link Map} of properties - ignored if {@code null}/empty
+     * @return The target appender after having appended (or not) the value
+     * @throws IOException If failed to append the requested data
+     * @see #appendNonEmptyProperty(Appendable, String, Object)
+     */
+    public static <A extends Appendable> A appendNonEmptyProperties(A sb, Map<String, ?> props) throws IOException {
+        if (GenericUtils.isEmpty(props)) {
+            return sb;
+        }
+
+        // Cannot use forEach because of the IOException being thrown by appendNonEmptyProperty
+        for (Map.Entry<String, ?> pe : props.entrySet()) {
+            String name = pe.getKey();
+            if (EXPLICIT_PROPERTIES.contains(name)) {
+                continue;
+            }
+
+            appendNonEmptyProperty(sb, name, pe.getValue());
+        }
+
+        return sb;
+    }
+
+    /**
+     * @param <A> The {@link Appendable} type
+     * @param sb The target appender
+     * @param name The property name - never {@code null}/empty
+     * @param value The property value - ignored if {@code null}. <B>Note:</B>
+     * if the string representation of the value contains any commas, they are
+     * assumed to indicate a multi-valued property which is broken down to
+     * <U>individual</U> lines - one per value.
+     * @return The target appender after having appended (or not) the value
+     * @throws IOException If failed to append the requested data
+     * @see #appendNonEmptyValues(Appendable, String, Object...)
+     */
+    public static <A extends Appendable> A appendNonEmptyProperty(A sb, String name, Object value) throws IOException {
+        String s = Objects.toString(value, null);
+        String[] vals = GenericUtils.split(s, ',');
+        return appendNonEmptyValues(sb, name, (Object[]) vals);
+    }
+
+    /**
+     * @param <A> The {@link Appendable} type
+     * @param sb The target appender
+     * @param name The property name - never {@code null}/empty
+     * @param values The values to be added - one per line - ignored if {@code null}/empty
+     * @return The target appender after having appended (or not) the value
+     * @throws IOException If failed to append the requested data
+     * @see #appendNonEmptyValues(Appendable, String, Collection)
+     */
+    public static <A extends Appendable> A appendNonEmptyValues(A sb, String name, Object... values) throws IOException {
+        return appendNonEmptyValues(sb, name, GenericUtils.isEmpty(values) ? Collections.emptyList() : Arrays.asList(values));
+    }
+
+    /**
+     * @param <A> The {@link Appendable} type
+     * @param sb The target appender
+     * @param name The property name - never {@code null}/empty
+     * @param values The values to be added - one per line - ignored if {@code null}/empty
+     * @return The target appender after having appended (or not) the value
+     * @throws IOException If failed to append the requested data
+     */
+    public static <A extends Appendable> A appendNonEmptyValues(A sb, String name, Collection<?> values) throws IOException {
+        String k = ValidateUtils.checkNotNullAndNotEmpty(name, "No property name");
+        if (GenericUtils.isEmpty(values)) {
+            return sb;
+        }
+
+        for (Object v : values) {
+            sb.append("    ").append(k).append(' ').append(Objects.toString(v)).append(IoUtils.EOL);
+        }
+
+        return sb;
+    }
+
+    /**
+     * @param entries The entries - ignored if {@code null}/empty
+     * @return A {@link HostConfigEntryResolver} wrapper using the entries
+     */
+    public static HostConfigEntryResolver toHostConfigEntryResolver(Collection<? extends HostConfigEntry> entries) {
+        if (GenericUtils.isEmpty(entries)) {
+            return HostConfigEntryResolver.EMPTY;
+        } else {
+            return (host1, port1, username1) -> {
+                List<HostConfigEntry> matches = findMatchingEntries(host1, entries);
+                int numMatches = GenericUtils.size(matches);
+                if (numMatches <= 0) {
+                    return null;
+                }
+
+                HostConfigEntry match = (numMatches == 1) ? matches.get(0) : findBestMatch(matches);
+                if (match == null) {
+                    ValidateUtils.throwIllegalArgumentException("No best match found for %s@%s:%d out of %d matches", username1, host1, port1, numMatches);
+                }
+
+                return normalizeEntry(match, host1, port1, username1);
+            };
+        }
+    }
+
+    /**
+     * @param entry The original entry - ignored if {@code null}
+     * @param host The original host name / address
+     * @param port The original port
+     * @param username The original user name
+     * @return A <U>cloned</U> entry whose values are resolved - including
+     * expanding macros in the identities files
+     * @throws IOException If failed to normalize the entry
+     * @see #resolveHostName(String)
+     * @see #resolvePort(int)
+     * @see #resolveUsername(String)
+     * @see #resolveIdentityFilePath(String, String, int, String)
+     */
+    public static HostConfigEntry normalizeEntry(HostConfigEntry entry, String host, int port, String username) throws IOException {
+        if (entry == null) {
+            return null;
+        }
+
+        HostConfigEntry normal = new HostConfigEntry();
+        normal.setHost(host);
+        normal.setHostName(entry.resolveHostName(host));
+        normal.setPort(entry.resolvePort(port));
+        normal.setUsername(entry.resolveUsername(username));
+
+        Map<String, String> props = entry.getProperties();
+        if (GenericUtils.size(props) > 0) {
+            normal.setProperties(new TreeMap<>(props));
+        }
+
+        Collection<String> ids = entry.getIdentities();
+        if (GenericUtils.isEmpty(ids)) {
+            return normal;
+        }
+
+        normal.setIdentities(Collections.emptyList());  // start fresh
+        for (String id : ids) {
+            String path = resolveIdentityFilePath(id, host, port, username);
+            normal.addIdentity(path);
+        }
+
+        return normal;
+    }
+
+    /**
+     * Resolves the effective target host
+     *
+     * @param originalName The original requested host
+     * @param entryName The configured host
+     * @return If the configured host entry is not {@code null}/empty
+     * then it is used, otherwise the original one.
+     */
+    public static String resolveHostName(String originalName, String entryName) {
+        if (GenericUtils.isEmpty(entryName)) {
+            return originalName;
+        } else {
+            return entryName;
+        }
+    }
+
+    /**
+     * Resolves the effective username
+     *
+     * @param originalUser The original requested username
+     * @param entryUser The configured host entry username
+     * @return If the configured host entry username is not {@code null}/empty
+     * then it is used, otherwise the original one.
+     */
+    public static String resolveUsername(String originalUser, String entryUser) {
+        if (GenericUtils.isEmpty(entryUser)) {
+            return originalUser;
+        } else {
+            return entryUser;
+        }
+    }
+
+    /**
+     * Resolves the effective port to use
+     *
+     * @param originalPort The original requested port
+     * @param entryPort The configured host entry port
+     * @return If the host entry port is positive, then it is used, otherwise
+     * the original requested port
+     */
+    public static int resolvePort(int originalPort, int entryPort) {
+        if (entryPort <= 0) {
+            return originalPort;
+        } else {
+            return entryPort;
+        }
+    }
+
+    public static List<HostConfigEntry> readHostConfigEntries(File file) throws IOException {
+        return readHostConfigEntries(file.toPath(), IoUtils.EMPTY_OPEN_OPTIONS);
+    }
+
+    public static List<HostConfigEntry> readHostConfigEntries(Path path, OpenOption... options) throws IOException {
+        try (InputStream input = Files.newInputStream(path, options)) {
+            return readHostConfigEntries(input, true);
+        }
+    }
+
+    public static List<HostConfigEntry> readHostConfigEntries(URL url) throws IOException {
+        try (InputStream input = url.openStream()) {
+            return readHostConfigEntries(input, true);
+        }
+    }
+
+    public static List<HostConfigEntry> readHostConfigEntries(String filePath) throws IOException {
+        try (InputStream inStream = new FileInputStream(filePath)) {
+            return readHostConfigEntries(inStream, true);
+        }
+    }
+
+    public static List<HostConfigEntry> readHostConfigEntries(InputStream inStream, boolean okToClose) throws IOException {
+        try (Reader reader = new InputStreamReader(NoCloseInputStream.resolveInputStream(inStream, okToClose), StandardCharsets.UTF_8)) {
+            return readHostConfigEntries(reader, true);
+        }
+    }
+
+    public static List<HostConfigEntry> readHostConfigEntries(Reader rdr, boolean okToClose) throws IOException {
+        try (BufferedReader buf = new BufferedReader(NoCloseReader.resolveReader(rdr, okToClose))) {
+            return readHostConfigEntries(buf);
+        }
+    }
+
+    /**
+     * Reads configuration entries
+     *
+     * @param rdr The {@link BufferedReader} to use
+     * @return The {@link List} of read {@link HostConfigEntry}-ies
+     * @throws IOException If failed to parse the read configuration
+     */
+    public static List<HostConfigEntry> readHostConfigEntries(BufferedReader rdr) throws IOException {
+        HostConfigEntry curEntry = null;
+        HostConfigEntry globalEntry = null;
+        List<HostConfigEntry> entries = null;
+
+        int lineNumber = 1;
+        for (String line = rdr.readLine(); line != null; line = rdr.readLine(), lineNumber++) {
+            line = GenericUtils.replaceWhitespaceAndTrim(line);
+            if (GenericUtils.isEmpty(line)) {
+                continue;
+            }
+
+            int pos = line.indexOf(ConfigFileReaderSupport.COMMENT_CHAR);
+            if (pos == 0) {
+                continue;
+            }
+
+            if (pos > 0) {
+                line = line.substring(0, pos);
+                line = line.trim();
+            }
+
+            /*
+             * Some options use '=', others use ' ' - try both
+             * NOTE: we do not validate the format for each option separately
+             */
+            pos = line.indexOf(' ');
+            if (pos < 0) {
+                pos = line.indexOf('=');
+            }
+
+            if (pos < 0) {
+                throw new StreamCorruptedException("No configuration value delimiter at line " + lineNumber + ": " + line);
+            }
+
+            String key = line.substring(0, pos);
+            String value = line.substring(pos + 1);
+            List<String> valsList = parseConfigValue(value);
+
+            if (HOST_CONFIG_PROP.equalsIgnoreCase(key)) {
+                if (GenericUtils.isEmpty(valsList)) {
+                    throw new StreamCorruptedException("Missing host pattern(s) at line " + lineNumber + ": " + line);
+                }
+
+                // If the all-hosts pattern is used, make sure no global section already active
+                for (String name : valsList) {
+                    if (ALL_HOSTS_PATTERN.equalsIgnoreCase(name) && (globalEntry != null)) {
+                        throw new StreamCorruptedException("Overriding the global section with a specific one at line " + lineNumber + ": " + line);
+                    }
+                }
+
+                if (curEntry != null) {
+                    curEntry.processGlobalValues(globalEntry);
+                }
+
+                entries = updateEntriesList(entries, curEntry);
+
+                curEntry = new HostConfigEntry();
+                curEntry.setHost(valsList);
+            } else if (curEntry == null) {
+                // if 1st encountered property is NOT for a specific host, then configuration applies to ALL
+                curEntry = new HostConfigEntry();
+                curEntry.setHost(Collections.singletonList(ALL_HOSTS_PATTERN));
+                globalEntry = curEntry;
+            }
+
+            try {
+                curEntry.processProperty(key, valsList, false);
+            } catch (RuntimeException e) {
+                throw new StreamCorruptedException("Failed (" + e.getClass().getSimpleName() + ")"
+                                                 + " to process line #" + lineNumber + " (" + line + ")"
+                                                 + ": " + e.getMessage());
+            }
+        }
+
+        if (curEntry != null) {
+            curEntry.processGlobalValues(globalEntry);
+        }
+
+        entries = updateEntriesList(entries, curEntry);
+        if (entries == null) {
+            return Collections.emptyList();
+        } else {
+            return entries;
+        }
+    }
+
+    /**
+     * Finds the best match out of the given ones.
+     *
+     * @param matches The available matches - ignored if {@code null}/empty
+     * @return The best match or {@code null} if no matches or no best match found
+     * @see #findBestMatch(Iterator)
+     */
+    public static HostConfigEntry findBestMatch(Collection<? extends HostConfigEntry> matches) {
+        if (GenericUtils.isEmpty(matches)) {
+            return null;
+        } else {
+            return findBestMatch(matches.iterator());
+        }
+    }
+
+    /**
+     * Finds the best match out of the given ones.
+     *
+     * @param matches The available matches - ignored if {@code null}/empty
+     * @return The best match or {@code null} if no matches or no best match found
+     * @see #findBestMatch(Iterator)
+     */
+    public static HostConfigEntry findBestMatch(Iterable<? extends HostConfigEntry> matches) {
+        if (matches == null) {
+            return null;
+        } else {
+            return findBestMatch(matches.iterator());
+        }
+    }
+
+    /**
+     * Finds the best match out of the given ones. The best match is defined as one whose
+     * pattern is as <U>specific</U> as possible (if more than one match is available).
+     * I.e., a non-global match is preferred over global one, and a match with no wildcards
+     * is preferred over one with such a pattern.
+     *
+     * @param matches The available matches - ignored if {@code null}/empty
+     * @return The best match or {@code null} if no matches or no best match found
+     * @see #isSpecificHostPattern(String)
+     */
+    public static HostConfigEntry findBestMatch(Iterator<? extends HostConfigEntry> matches) {
+        if ((matches == null) || (!matches.hasNext())) {
+            return null;
+        }
+
+        HostConfigEntry candidate = matches.next();
+        int wildcardMatches = 0;
+        while (matches.hasNext()) {
+            HostConfigEntry entry = matches.next();
+            String entryPattern = entry.getHost();
+            String candidatePattern = candidate.getHost();
+            // prefer non-global entry over global entry
+            if (ALL_HOSTS_PATTERN.equalsIgnoreCase(candidatePattern)) {
+                // unlikely, but handle it
+                if (ALL_HOSTS_PATTERN.equalsIgnoreCase(entryPattern)) {
+                    wildcardMatches++;
+                } else {
+                    candidate = entry;
+                    wildcardMatches = 0;
+                }
+                continue;
+            }
+
+            if (isSpecificHostPattern(entryPattern)) {
+                // if both are specific then no best match
+                if (isSpecificHostPattern(candidatePattern)) {
+                    return null;
+                }
+
+                candidate = entry;
+                wildcardMatches = 0;
+                continue;
+            }
+
+            wildcardMatches++;
+        }
+
+        String candidatePattern = candidate.getHost();
+        // best match either has specific host or no wildcard matches
+        if ((wildcardMatches <= 0) || (isSpecificHostPattern(candidatePattern))) {
+            return candidate;
+        }
+
+        return null;
+    }
+
+    public static List<HostConfigEntry> updateEntriesList(List<HostConfigEntry> entries, HostConfigEntry curEntry) {
+        if (curEntry == null) {
+            return entries;
+        }
+
+        if (entries == null) {
+            entries = new ArrayList<>();
+        }
+
+        entries.add(curEntry);
+        return entries;
+    }
+
+    public static void writeHostConfigEntries(File file, Collection<? extends HostConfigEntry> entries) throws IOException {
+        writeHostConfigEntries(Objects.requireNonNull(file, "No file").toPath(), entries, IoUtils.EMPTY_OPEN_OPTIONS);
+    }
+
+    public static void writeHostConfigEntries(Path path, Collection<? extends HostConfigEntry> entries, OpenOption... options) throws IOException {
+        try (OutputStream outputStream = Files.newOutputStream(path, options)) {
+            writeHostConfigEntries(outputStream, true, entries);
+        }
+    }
+
+    public static void writeHostConfigEntries(OutputStream outputStream, boolean okToClose, Collection<? extends HostConfigEntry> entries) throws IOException {
+        if (GenericUtils.isEmpty(entries)) {
+            return;
+        }
+
+        try (Writer w = new OutputStreamWriter(NoCloseOutputStream.resolveOutputStream(outputStream, okToClose), StandardCharsets.UTF_8)) {
+            appendHostConfigEntries(w, entries);
+        }
+    }
+
+    public static <A extends Appendable> A appendHostConfigEntries(A sb, Collection<? extends HostConfigEntry> entries) throws IOException {
+        if (GenericUtils.isEmpty(entries)) {
+            return sb;
+        }
+
+        for (HostConfigEntry entry : entries) {
+            entry.append(sb);
+        }
+
+        return sb;
+    }
+
+    /**
+     * Checks if this is a multi-value - allow space and comma
+     *
+     * @param value The value - ignored if {@code null}/empty (after trimming)
+     * @return A {@link List} of the encountered values
+     */
+    public static List<String> parseConfigValue(String value) {
+        String s = GenericUtils.replaceWhitespaceAndTrim(value);
+        if (GenericUtils.isEmpty(s)) {
+            return Collections.emptyList();
+        }
+
+        for (int index = 0; index < MULTI_VALUE_SEPARATORS.length(); index++) {
+            char sep = MULTI_VALUE_SEPARATORS.charAt(index);
+            int pos = s.indexOf(sep);
+            if (pos >= 0) {
+                String[] vals = GenericUtils.split(s, sep);
+                if (GenericUtils.isEmpty(vals)) {
+                    return Collections.emptyList();
+                } else {
+                    return Arrays.asList(vals);
+                }
+            }
+        }
+
+        // this point is reached if no separators found
+        return Collections.singletonList(s);
+    }
+
+    // The file name may use the tilde syntax to refer to a user’s home directory or one of the following escape characters:
+    // '%d' (local user's home directory), '%u' (local user name), '%l' (local host name), '%h' (remote host name) or '%r' (remote user name).
+    public static String resolveIdentityFilePath(String id, String host, int port, String username) throws IOException {
+        if (GenericUtils.isEmpty(id)) {
+            return id;
+        }
+
+        String path = id.replace('/', File.separatorChar);  // make sure all separators are local
+        String[] elements = GenericUtils.split(path, File.separatorChar);
+        StringBuilder sb = new StringBuilder(path.length() + Long.SIZE);
+        for (int index = 0; index < elements.length; index++) {
+            String elem = elements[index];
+            if (index > 0) {
+                sb.append(File.separatorChar);
+            }
+
+            for (int curPos = 0; curPos < elem.length(); curPos++) {
+                char ch = elem.charAt(curPos);
+                if (ch == HOME_TILDE_CHAR) {
+                    ValidateUtils.checkTrue((curPos == 0) && (index == 0), "Home tilde must be first: %s", id);
+                    appendUserHome(sb);
+                } else if (ch == PATH_MACRO_CHAR) {
+                    curPos++;
+                    ValidateUtils.checkTrue(curPos < elem.length(), "Missing macro modifier in %s", id);
+                    ch = elem.charAt(curPos);
+                    switch(ch) {
+                        case PATH_MACRO_CHAR:
+                            sb.append(ch);
+                            break;
+                        case LOCAL_HOME_MACRO:
+                            ValidateUtils.checkTrue((curPos == 1) && (index == 0), "Home macro must be first: %s", id);
+                            appendUserHome(sb);
+                            break;
+                        case LOCAL_USER_MACRO:
+                            sb.append(ValidateUtils.checkNotNullAndNotEmpty(OsUtils.getCurrentUser(), "No local user name value"));
+                            break;
+                        case LOCAL_HOST_MACRO: {
+                            InetAddress address = Objects.requireNonNull(InetAddress.getLocalHost(), "No local address");
+                            sb.append(ValidateUtils.checkNotNullAndNotEmpty(address.getHostName(), "No local name"));
+                            break;
+                        }
+                        case REMOTE_HOST_MACRO:
+                            sb.append(ValidateUtils.checkNotNullAndNotEmpty(host, "No remote host provided"));
+                            break;
+                        case REMOTE_USER_MACRO:
+                            sb.append(ValidateUtils.checkNotNullAndNotEmpty(username, "No remote user provided"));
+                            break;
+                        case REMOTE_PORT_MACRO:
+                            ValidateUtils.checkTrue(port > 0, "Bad remote port value: %d", port);
+                            sb.append(port);
+                            break;
+                        default:
+                            ValidateUtils.throwIllegalArgumentException("Bad modifier '%s' in %s", String.valueOf(ch), id);
+                    }
+                } else {
+                    sb.append(ch);
+                }
+            }
+        }
+
+        return sb.toString();
+    }
+
+    public static StringBuilder appendUserHome(StringBuilder sb) {
+        return appendUserHome(sb, IdentityUtils.getUserHomeFolder());
+    }
+
+    public static StringBuilder appendUserHome(StringBuilder sb, Path userHome) {
+        return appendUserHome(sb, Objects.requireNonNull(userHome, "No user home folder").toString());
+    }
+
+    public static StringBuilder appendUserHome(StringBuilder sb, String userHome) {
+        if (GenericUtils.isEmpty(userHome)) {
+            return sb;
+        }
+
+        sb.append(userHome);
+        // strip any ending separator since we add our own
+        int len = sb.length();
+        if (sb.charAt(len - 1) == File.separatorChar) {
+            sb.setLength(len - 1);
+        }
+
+        return sb;
+    }
+
+    /**
+     * @return The default {@link Path} location of the OpenSSH hosts entries configuration file
+     */
+    @SuppressWarnings("synthetic-access")
+    public static Path getDefaultHostConfigFile() {
+        return LazyDefaultConfigFileHolder.CONFIG_FILE;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntryResolver.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntryResolver.java b/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntryResolver.java
new file mode 100644
index 0000000..a07cfcf
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntryResolver.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.client.config.hosts;
+
+import java.io.IOException;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FunctionalInterface
+public interface HostConfigEntryResolver {
+
+    /**
+     * An &quot;empty&quot; implementation that does not resolve any entry
+     */
+    HostConfigEntryResolver EMPTY = new HostConfigEntryResolver() {
+        @Override
+        public HostConfigEntry resolveEffectiveHost(String host, int port, String username) throws IOException {
+            return null;
+        }
+
+        @Override
+        public String toString() {
+            return "EMPTY";
+        }
+    };
+
+    /**
+     * Invoked when creating a new client session in order to allow for overriding
+     * of the original parameters
+     *
+     * @param host The requested host - never {@code null}/empty
+     * @param port The requested port
+     * @param username The requested username
+     * @return A {@link HostConfigEntry} for the actual target - {@code null} if use
+     * original parameters. <B>Note:</B> if any identity files are attached to the
+     * configuration then they must point to <U>existing</U> locations. This means
+     * that any macros such as <code>~, %d, %h</code>, etc. must be resolved <U>prior</U>
+     * to returning the value
+     * @throws IOException If failed to resolve the configuration
+     */
+    HostConfigEntry resolveEffectiveHost(String host, int port, String username) throws IOException;
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/HostPatternValue.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/HostPatternValue.java b/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/HostPatternValue.java
new file mode 100644
index 0000000..20d682f
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/HostPatternValue.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.client.config.hosts;
+
+import java.util.regex.Pattern;
+
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * Represents a pattern definition in the <U>known_hosts</U> file
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ * @see <A HREF="https://en.wikibooks.org/wiki/OpenSSH/Client_Configuration_Files#About_the_Contents_of_the_known_hosts_Files">
+ * OpenSSH cookbook - About the Contents of the known hosts Files</A>
+ */
+public class HostPatternValue {
+    private Pattern pattern;
+    private int port;
+    private boolean negated;
+
+    public HostPatternValue() {
+        super();
+    }
+
+    public HostPatternValue(Pattern pattern, boolean negated) {
+        this(pattern, 0, negated);
+    }
+
+    public HostPatternValue(Pattern pattern, int port, boolean negated) {
+        this.pattern = pattern;
+        this.port = port;
+        this.negated = negated;
+    }
+
+    public Pattern getPattern() {
+        return pattern;
+    }
+
+    public void setPattern(Pattern pattern) {
+        this.pattern = pattern;
+    }
+
+    public int getPort() {
+        return port;
+    }
+
+    public void setPort(int port) {
+        this.port = port;
+    }
+
+    public boolean isNegated() {
+        return negated;
+    }
+
+    public void setNegated(boolean negated) {
+        this.negated = negated;
+    }
+
+    @Override
+    public String toString() {
+        Pattern p = getPattern();
+        String purePattern = (p == null) ? null : p.pattern();
+        StringBuilder sb = new StringBuilder(GenericUtils.length(purePattern) + Short.SIZE);
+        if (isNegated()) {
+            sb.append(HostPatternsHolder.NEGATION_CHAR_PATTERN);
+        }
+
+        int portValue = getPort();
+        if (portValue > 0) {
+            sb.append(HostPatternsHolder.NON_STANDARD_PORT_PATTERN_ENCLOSURE_START_DELIM);
+        }
+        sb.append(purePattern);
+        if (portValue > 0) {
+            sb.append(HostPatternsHolder.NON_STANDARD_PORT_PATTERN_ENCLOSURE_END_DELIM);
+            sb.append(HostPatternsHolder.PORT_VALUE_DELIMITER);
+            sb.append(portValue);
+        }
+
+        return sb.toString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/HostPatternsHolder.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/HostPatternsHolder.java b/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/HostPatternsHolder.java
new file mode 100644
index 0000000..9d90dac
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/HostPatternsHolder.java
@@ -0,0 +1,343 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.client.config.hosts;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Objects;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class HostPatternsHolder {
+
+    /**
+     * Used in a host pattern to denote zero or more consecutive characters
+     */
+    public static final char WILDCARD_PATTERN = '*';
+    public static final String ALL_HOSTS_PATTERN = String.valueOf(WILDCARD_PATTERN);
+
+    /**
+     * Used in a host pattern to denote any <U>one</U> character
+     */
+    public static final char SINGLE_CHAR_PATTERN = '?';
+
+    /**
+     * Used to negate a host pattern
+     */
+    public static final char NEGATION_CHAR_PATTERN = '!';
+
+    /**
+     * The available pattern characters
+     */
+    public static final String PATTERN_CHARS = new String(new char[]{WILDCARD_PATTERN, SINGLE_CHAR_PATTERN, NEGATION_CHAR_PATTERN});
+
+    /** Port value separator if non-standard port pattern used */
+    public static final char PORT_VALUE_DELIMITER = ':';
+
+    /** Non-standard port specification host pattern enclosure start delimiter */
+    public static final char NON_STANDARD_PORT_PATTERN_ENCLOSURE_START_DELIM = '[';
+
+    /** Non-standard port specification host pattern enclosure end delimiter */
+    public static final char NON_STANDARD_PORT_PATTERN_ENCLOSURE_END_DELIM = ']';
+
+    private Collection<HostPatternValue> patterns = new LinkedList<>();
+
+    protected HostPatternsHolder() {
+        super();
+    }
+
+    public Collection<HostPatternValue> getPatterns() {
+        return patterns;
+    }
+
+    public void setPatterns(Collection<HostPatternValue> patterns) {
+        this.patterns = patterns;
+    }
+
+    /**
+     * Checks if a given host name / address matches the entry's host pattern(s)
+     *
+     * @param host The host name / address - ignored if {@code null}/empty
+     * @param port The connection port
+     * @return {@code true} if the name / address matches the pattern(s)
+     * @see #isHostMatch(String, Pattern)
+     */
+    public boolean isHostMatch(String host, int port) {
+        return isHostMatch(host, port, getPatterns());
+    }
+
+    /**
+     * @param pattern The pattern to check - ignored if {@code null}/empty
+     * @return {@code true} if the pattern is not empty and contains no wildcard characters
+     * @see #WILDCARD_PATTERN
+     * @see #SINGLE_CHAR_PATTERN
+     * @see #SINGLE_CHAR_PATTERN
+     */
+    public static boolean isSpecificHostPattern(String pattern) {
+        if (GenericUtils.isEmpty(pattern)) {
+            return false;
+        }
+
+        for (int index = 0; index < PATTERN_CHARS.length(); index++) {
+            char ch = PATTERN_CHARS.charAt(index);
+            if (pattern.indexOf(ch) >= 0) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Locates all the matching entries for a give host name / address
+     *
+     * @param host The host name / address - ignored if {@code null}/empty
+     * @param entries The {@link HostConfigEntry}-ies to scan - ignored if {@code null}/empty
+     * @return A {@link List} of all the matching entries
+     * @see #isHostMatch(String, int)
+     */
+    public static List<HostConfigEntry> findMatchingEntries(String host, HostConfigEntry... entries) {
+        // TODO in Java-8 use Stream(s) + predicate
+        if (GenericUtils.isEmpty(host) || GenericUtils.isEmpty(entries)) {
+            return Collections.emptyList();
+        } else {
+            return findMatchingEntries(host, Arrays.asList(entries));
+        }
+    }
+
+    /**
+     * Locates all the matching entries for a give host name / address
+     *
+     * @param host The host name / address - ignored if {@code null}/empty
+     * @param entries The {@link HostConfigEntry}-ies to scan - ignored if {@code null}/empty
+     * @return A {@link List} of all the matching entries
+     * @see #isHostMatch(String, int)
+     */
+    public static List<HostConfigEntry> findMatchingEntries(String host, Collection<? extends HostConfigEntry> entries) {
+        // TODO in Java-8 use Stream(s) + predicate
+        if (GenericUtils.isEmpty(host) || GenericUtils.isEmpty(entries)) {
+            return Collections.emptyList();
+        }
+
+        List<HostConfigEntry> matches = null;
+        for (HostConfigEntry entry : entries) {
+            if (!entry.isHostMatch(host, 0 /* any port */)) {
+                continue;   // debug breakpoint
+            }
+
+            if (matches == null) {
+                matches = new ArrayList<>(entries.size());  // in case ALL of them match
+            }
+
+            matches.add(entry);
+        }
+
+        if (matches == null) {
+            return Collections.emptyList();
+        } else {
+            return matches;
+        }
+    }
+
+    public static boolean isHostMatch(String host, int port, Collection<HostPatternValue> patterns) {
+        if (GenericUtils.isEmpty(patterns)) {
+            return false;
+        }
+
+        boolean matchFound = false;
+        for (HostPatternValue pv : patterns) {
+            boolean negated = pv.isNegated();
+            /*
+             * If already found a match we are interested only in negations
+             */
+            if (matchFound && (!negated)) {
+                continue;
+            }
+
+            if (!isHostMatch(host, pv.getPattern())) {
+                continue;
+            }
+
+            /*
+             * According to https://www.freebsd.org/cgi/man.cgi?query=ssh_config&sektion=5:
+             *
+             *      If a negated entry is matched, then the Host entry is ignored,
+             *      regardless of whether any other patterns on the line match.
+             */
+            if (negated) {
+                return false;
+            }
+
+            int pvPort = pv.getPort();
+            if ((pvPort != 0) && (port != 0) && (pvPort != port)) {
+                continue;
+            }
+
+            matchFound = true;
+        }
+
+        return matchFound;
+    }
+
+    /**
+     * Checks if a given host name / address matches a host pattern
+     *
+     * @param host The host name / address - ignored if {@code null}/empty
+     * @param pattern The host {@link Pattern} - ignored if {@code null}
+     * @return {@code true} if the name / address matches the pattern
+     */
+    public static boolean isHostMatch(String host, Pattern pattern) {
+        if (GenericUtils.isEmpty(host) || (pattern == null)) {
+            return false;
+        }
+
+        Matcher m = pattern.matcher(host);
+        return m.matches();
+    }
+
+    public static List<HostPatternValue> parsePatterns(CharSequence... patterns) {
+        return parsePatterns(GenericUtils.isEmpty(patterns) ? Collections.emptyList() : Arrays.asList(patterns));
+    }
+
+    public static List<HostPatternValue> parsePatterns(Collection<? extends CharSequence> patterns) {
+        if (GenericUtils.isEmpty(patterns)) {
+            return Collections.emptyList();
+        }
+
+        List<HostPatternValue> result = new ArrayList<>(patterns.size());
+        for (CharSequence p : patterns) {
+            result.add(ValidateUtils.checkNotNull(toPattern(p), "No pattern for %s", p));
+        }
+
+        return result;
+    }
+
+    /**
+     * Converts a host pattern string to a regular expression matcher.
+     * <B>Note:</B> pattern matching is <U>case insensitive</U>
+     *
+     * @param patternString The original pattern string - ignored if {@code null}/empty
+     * @return The regular expression matcher {@link Pattern} and the indication
+     * whether it is a negating pattern or not - {@code null} if no original string
+     * @see #NON_STANDARD_PORT_PATTERN_ENCLOSURE_START_DELIM
+     * @see #NON_STANDARD_PORT_PATTERN_ENCLOSURE_END_DELIM
+     * @see #WILDCARD_PATTERN
+     * @see #SINGLE_CHAR_PATTERN
+     * @see #NEGATION_CHAR_PATTERN
+     */
+    public static HostPatternValue toPattern(CharSequence patternString) {
+        String pattern = GenericUtils.replaceWhitespaceAndTrim(Objects.toString(patternString, null));
+        if (GenericUtils.isEmpty(pattern)) {
+            return null;
+        }
+
+        int patternLen = pattern.length();
+        int port = 0;
+        // Check if non-standard port value used
+        StringBuilder sb = new StringBuilder(patternLen);
+        if (pattern.charAt(0) == HostPatternsHolder.NON_STANDARD_PORT_PATTERN_ENCLOSURE_START_DELIM) {
+            int pos = GenericUtils.lastIndexOf(pattern, HostPatternsHolder.PORT_VALUE_DELIMITER);
+            ValidateUtils.checkTrue(pos > 0, "Missing non-standard port value delimiter in %s", pattern);
+            ValidateUtils.checkTrue(pos < (patternLen - 1), "Missing non-standard port value number in %s", pattern);
+            ValidateUtils.checkTrue(pattern.charAt(pos - 1) == HostPatternsHolder.NON_STANDARD_PORT_PATTERN_ENCLOSURE_END_DELIM,
+                "Invalid non-standard port value host pattern enclosure delimiters in %s", pattern);
+
+            String csPort = pattern.substring(pos + 1, patternLen);
+            port = Integer.parseInt(csPort);
+            ValidateUtils.checkTrue((port > 0) && (port <= 0xFFFF), "Invalid non-start port value (%d) in %s", port, pattern);
+
+            pattern = pattern.substring(1, pos - 1);
+            patternLen = pattern.length();
+        }
+
+        boolean negated = false;
+        for (int curPos = 0; curPos < patternLen; curPos++) {
+            char ch = pattern.charAt(curPos);
+            ValidateUtils.checkTrue(isValidPatternChar(ch), "Invalid host pattern char in %s", pattern);
+
+            switch(ch) {
+                case '.':   // need to escape it
+                    sb.append('\\').append(ch);
+                    break;
+                case SINGLE_CHAR_PATTERN:
+                    sb.append('.');
+                    break;
+                case WILDCARD_PATTERN:
+                    sb.append(".*");
+                    break;
+                case NEGATION_CHAR_PATTERN:
+                    ValidateUtils.checkTrue(!negated, "Double negation in %s", pattern);
+                    ValidateUtils.checkTrue(curPos == 0, "Negation must be 1st char: %s", pattern);
+                    negated = true;
+                    break;
+                default:
+                    sb.append(ch);
+            }
+        }
+
+        return new HostPatternValue(Pattern.compile(sb.toString(), Pattern.CASE_INSENSITIVE), port, negated);
+    }
+
+    /**
+     * Checks if the given character is valid for a host pattern. Valid
+     * characters are:
+     * <UL>
+     *      <LI>A-Z</LI>
+     *      <LI>a-z</LI>
+     *      <LI>0-9</LI>
+     *      <LI>Underscore (_)</LI>
+     *      <LI>Hyphen (-)</LI>
+     *      <LI>Dot (.)</LI>
+     *      <LI>The {@link #WILDCARD_PATTERN}</LI>
+     *      <LI>The {@link #SINGLE_CHAR_PATTERN}</LI>
+     * </UL>
+     *
+     * @param ch The character to validate
+     * @return {@code true} if valid pattern character
+     */
+    public static boolean isValidPatternChar(char ch) {
+        if ((ch <= ' ') || (ch >= 0x7E)) {
+            return false;
+        }
+        if ((ch >= 'a') && (ch <= 'z')) {
+            return true;
+        }
+        if ((ch >= 'A') && (ch <= 'Z')) {
+            return true;
+        }
+        if ((ch >= '0') && (ch <= '9')) {
+            return true;
+        }
+        if ("-_.".indexOf(ch) >= 0) {
+            return true;
+        }
+        return PATTERN_CHARS.indexOf(ch) >= 0;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/KnownHostDigest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/KnownHostDigest.java b/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/KnownHostDigest.java
new file mode 100644
index 0000000..2d9a322
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/KnownHostDigest.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.client.config.hosts;
+
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.Objects;
+import java.util.Set;
+
+import org.apache.sshd.common.Factory;
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.mac.BuiltinMacs;
+import org.apache.sshd.common.mac.Mac;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * Available digesters for known hosts entries
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public enum KnownHostDigest implements NamedFactory<Mac> {
+    SHA1("1", BuiltinMacs.hmacsha1);
+
+    public static final Set<KnownHostDigest> VALUES =
+            Collections.unmodifiableSet(EnumSet.allOf(KnownHostDigest.class));
+
+    private final String name;
+    private final Factory<Mac> factory;
+
+    KnownHostDigest(String name, Factory<Mac> factory) {
+        this.name = ValidateUtils.checkNotNullAndNotEmpty(name, "No name");
+        this.factory = Objects.requireNonNull(factory, "No factory");
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public Mac create() {
+        return factory.create();
+    }
+
+    public static KnownHostDigest fromName(String name) {
+        return NamedResource.findByName(name, String.CASE_INSENSITIVE_ORDER, VALUES);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/KnownHostEntry.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/KnownHostEntry.java b/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/KnownHostEntry.java
new file mode 100644
index 0000000..bcaf965
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/KnownHostEntry.java
@@ -0,0 +1,276 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.client.config.hosts;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StreamCorruptedException;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.OpenOption;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.sshd.common.config.ConfigFileReaderSupport;
+import org.apache.sshd.common.config.keys.AuthorizedKeyEntry;
+import org.apache.sshd.common.config.keys.PublicKeyEntry;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.common.util.io.NoCloseInputStream;
+import org.apache.sshd.common.util.io.NoCloseReader;
+
+/**
+ * Contains a representation of an entry in the <code>known_hosts</code> file
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ * @see <A HREF="http://www.manpagez.com/man/8/sshd/">sshd(8) man page</A>
+ */
+public class KnownHostEntry extends HostPatternsHolder {
+    /**
+     * Character that denotes that start of a marker
+     */
+    public static final char MARKER_INDICATOR = '@';
+
+    /**
+     * Standard OpenSSH config file name
+     */
+    public static final String STD_HOSTS_FILENAME = "known_hosts";
+
+    private static final class LazyDefaultConfigFileHolder {
+        private static final Path HOSTS_FILE =
+            PublicKeyEntry.getDefaultKeysFolderPath().resolve(STD_HOSTS_FILENAME);
+
+        private LazyDefaultConfigFileHolder() {
+            throw new UnsupportedOperationException("No instance allowed");
+        }
+    }
+
+    private String line;
+    private String marker;
+    private AuthorizedKeyEntry keyEntry;
+    private KnownHostHashValue hashedEntry;
+
+    public KnownHostEntry() {
+        super();
+    }
+
+    /**
+     * @param line The original line from which this entry was created
+     */
+    public KnownHostEntry(String line) {
+        this.line = line;
+    }
+
+    /**
+     * @return The original line from which this entry was created
+     */
+    public String getConfigLine() {
+        return line;
+    }
+
+    public void setConfigLine(String line) {
+        this.line = line;
+    }
+
+    public String getMarker() {
+        return marker;
+    }
+
+    public void setMarker(String marker) {
+        this.marker = marker;
+    }
+
+    public AuthorizedKeyEntry getKeyEntry() {
+        return keyEntry;
+    }
+
+    public void setKeyEntry(AuthorizedKeyEntry keyEntry) {
+        this.keyEntry = keyEntry;
+    }
+
+    public KnownHostHashValue getHashedEntry() {
+        return hashedEntry;
+    }
+
+    public void setHashedEntry(KnownHostHashValue hashedEntry) {
+        this.hashedEntry = hashedEntry;
+    }
+
+    @Override
+    public boolean isHostMatch(String host, int port) {
+        if (super.isHostMatch(host, port)) {
+            return true;
+        }
+
+        KnownHostHashValue hash = getHashedEntry();
+        return (hash != null) && hash.isHostMatch(host);
+    }
+
+    @Override
+    public String toString() {
+        return getConfigLine();
+    }
+
+    /**
+     * @return The default {@link Path} location of the OpenSSH known hosts file
+     */
+    @SuppressWarnings("synthetic-access")
+    public static Path getDefaultKnownHostsFile() {
+        return LazyDefaultConfigFileHolder.HOSTS_FILE;
+    }
+
+    public static List<KnownHostEntry> readKnownHostEntries(File file) throws IOException {
+        return readKnownHostEntries(file.toPath(), IoUtils.EMPTY_OPEN_OPTIONS);
+    }
+
+    public static List<KnownHostEntry> readKnownHostEntries(Path path, OpenOption... options) throws IOException {
+        try (InputStream input = Files.newInputStream(path, options)) {
+            return readKnownHostEntries(input, true);
+        }
+    }
+
+    public static List<KnownHostEntry> readKnownHostEntries(URL url) throws IOException {
+        try (InputStream input = url.openStream()) {
+            return readKnownHostEntries(input, true);
+        }
+    }
+
+    public static List<KnownHostEntry> readKnownHostEntries(String filePath) throws IOException {
+        try (InputStream inStream = new FileInputStream(filePath)) {
+            return readKnownHostEntries(inStream, true);
+        }
+    }
+
+    public static List<KnownHostEntry> readKnownHostEntries(InputStream inStream, boolean okToClose) throws IOException {
+        try (Reader reader = new InputStreamReader(NoCloseInputStream.resolveInputStream(inStream, okToClose), StandardCharsets.UTF_8)) {
+            return readKnownHostEntries(reader, true);
+        }
+    }
+
+    public static List<KnownHostEntry> readKnownHostEntries(Reader rdr, boolean okToClose) throws IOException {
+        try (BufferedReader buf = new BufferedReader(NoCloseReader.resolveReader(rdr, okToClose))) {
+            return readKnownHostEntries(buf);
+        }
+    }
+
+    /**
+     * Reads configuration entries
+     *
+     * @param rdr The {@link BufferedReader} to use
+     * @return The {@link List} of read {@link KnownHostEntry}-ies
+     * @throws IOException If failed to parse the read configuration
+     */
+    public static List<KnownHostEntry> readKnownHostEntries(BufferedReader rdr) throws IOException {
+        List<KnownHostEntry> entries = null;
+
+        int lineNumber = 1;
+        for (String line = rdr.readLine(); line != null; line = rdr.readLine(), lineNumber++) {
+            line = GenericUtils.trimToEmpty(line);
+            if (GenericUtils.isEmpty(line)) {
+                continue;
+            }
+
+            int pos = line.indexOf(ConfigFileReaderSupport.COMMENT_CHAR);
+            if (pos == 0) {
+                continue;
+            }
+
+            if (pos > 0) {
+                line = line.substring(0, pos);
+                line = line.trim();
+            }
+
+            try {
+                KnownHostEntry entry = parseKnownHostEntry(line);
+                if (entry == null) {
+                    continue;
+                }
+
+                if (entries == null) {
+                    entries = new ArrayList<>();
+                }
+                entries.add(entry);
+            } catch (RuntimeException | Error e) {   // TODO consider consulting a user callback
+                throw new StreamCorruptedException("Failed (" + e.getClass().getSimpleName() + ")"
+                        + " to parse line #" + lineNumber + " '" + line + "': " + e.getMessage());
+            }
+        }
+
+        if (entries == null) {
+            return Collections.emptyList();
+        } else {
+            return entries;
+        }
+    }
+
+    public static KnownHostEntry parseKnownHostEntry(String line) {
+        return parseKnownHostEntry(GenericUtils.isEmpty(line) ? null : new KnownHostEntry(), line);
+    }
+
+    public static <E extends KnownHostEntry> E parseKnownHostEntry(E entry, String data) {
+        String line = GenericUtils.replaceWhitespaceAndTrim(data);
+        if (GenericUtils.isEmpty(line) || (line.charAt(0) == PublicKeyEntry.COMMENT_CHAR)) {
+            return entry;
+        }
+
+        entry.setConfigLine(line);
+
+        if (line.charAt(0) == MARKER_INDICATOR) {
+            int pos = line.indexOf(' ');
+            ValidateUtils.checkTrue(pos > 0, "Missing marker name end delimiter in line=%s", data);
+            ValidateUtils.checkTrue(pos > 1, "No marker name after indicator in line=%s", data);
+            entry.setMarker(line.substring(1, pos));
+            line = line.substring(pos + 1).trim();
+        } else {
+            entry.setMarker(null);
+        }
+
+        int pos = line.indexOf(' ');
+        ValidateUtils.checkTrue(pos > 0, "Missing host patterns end delimiter in line=%s", data);
+        String hostPattern = line.substring(0, pos);
+        line = line.substring(pos + 1).trim();
+
+        if (hostPattern.charAt(0) == KnownHostHashValue.HASHED_HOST_DELIMITER) {
+            KnownHostHashValue hash =
+                ValidateUtils.checkNotNull(KnownHostHashValue.parse(hostPattern),
+                    "Failed to extract host hash value from line=%s", data);
+            entry.setHashedEntry(hash);
+            entry.setPatterns(null);
+        } else {
+            entry.setHashedEntry(null);
+            entry.setPatterns(parsePatterns(GenericUtils.split(hostPattern, ',')));
+        }
+
+        AuthorizedKeyEntry key =
+            ValidateUtils.checkNotNull(AuthorizedKeyEntry.parseAuthorizedKeyEntry(line),
+                "No valid key entry recovered from line=%s", data);
+        entry.setKeyEntry(key);
+        return entry;
+    }
+}


[18/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common utilities code from sshd-core into sshd-common (new artifact)

Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProvider.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProvider.java b/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProvider.java
deleted file mode 100644
index a816304..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProvider.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sshd.common.keyprovider;
-
-import java.security.KeyPair;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.LinkedHashSet;
-import java.util.Objects;
-import java.util.stream.Collectors;
-
-import org.apache.sshd.common.cipher.ECCurves;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-
-/**
- * Provider for key pairs.  This provider is used on the server side to provide
- * the host key, or on the client side to provide the user key.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface KeyPairProvider extends KeyIdentityProvider {
-
-    /**
-     * SSH identifier for RSA keys
-     */
-    String SSH_RSA = "ssh-rsa";
-
-    /**
-     * SSH identifier for DSA keys
-     */
-    String SSH_DSS = "ssh-dss";
-
-    /**
-     * SSH identifier for ED25519 elliptic curve keys
-     */
-    String SSH_ED25519 = "ssh-ed25519";
-
-    /**
-     * SSH identifier for EC keys in NIST curve P-256
-     */
-    String ECDSA_SHA2_NISTP256 = ECCurves.nistp256.getKeyType();
-
-    /**
-     * SSH identifier for EC keys in NIST curve P-384
-     */
-    String ECDSA_SHA2_NISTP384 = ECCurves.nistp384.getKeyType();
-
-    /**
-     * SSH identifier for EC keys in NIST curve P-521
-     */
-    String ECDSA_SHA2_NISTP521 = ECCurves.nistp521.getKeyType();
-
-    /**
-     * A {@link KeyPairProvider} that has no keys
-     */
-    KeyPairProvider EMPTY_KEYPAIR_PROVIDER =
-        new KeyPairProvider() {
-            @Override
-            public KeyPair loadKey(String type) {
-                return null;
-            }
-
-            @Override
-            public Iterable<String> getKeyTypes() {
-                return Collections.emptyList();
-            }
-
-            @Override
-            public Iterable<KeyPair> loadKeys() {
-                return Collections.emptyList();
-            }
-
-            @Override
-            public String toString() {
-                return "EMPTY_KEYPAIR_PROVIDER";
-            }
-        };
-
-    /**
-     * Load a key of the specified type which can be &quot;ssh-rsa&quot;, &quot;ssh-dss&quot;,
-     * or &quot;ecdsa-sha2-nistp{256,384,521}&quot;. If there is no key of this type, return
-     * {@code null}
-     *
-     * @param type the type of key to load
-     * @return a valid key pair or {@code null} if this type of key is not available
-     */
-    default KeyPair loadKey(String type) {
-        ValidateUtils.checkNotNullAndNotEmpty(type, "No key type to load");
-        return GenericUtils.stream(loadKeys())
-                .filter(key -> type.equals(KeyUtils.getKeyType(key)))
-                .findFirst()
-                .orElse(null);
-    }
-
-    /**
-     * @return The available {@link Iterable} key types in preferred order - never {@code null}
-     */
-    default Iterable<String> getKeyTypes() {
-        return GenericUtils.stream(loadKeys())
-                .map(KeyUtils::getKeyType)
-                .filter(GenericUtils::isNotEmpty)
-                .collect(Collectors.toSet());
-    }
-
-    /**
-     * Wrap the provided {@link KeyPair}s into a {@link KeyPairProvider}
-     *
-     * @param pairs The available pairs - ignored if {@code null}/empty (i.e.,
-     * returns {@link #EMPTY_KEYPAIR_PROVIDER})
-     * @return The provider wrapper
-     * @see #wrap(Iterable)
-     */
-    static KeyPairProvider wrap(KeyPair... pairs) {
-        return GenericUtils.isEmpty(pairs) ? EMPTY_KEYPAIR_PROVIDER : wrap(Arrays.asList(pairs));
-    }
-
-    /**
-     * Wrap the provided {@link KeyPair}s into a {@link KeyPairProvider}
-     *
-     * @param pairs The available pairs {@link Iterable} - ignored if {@code null} (i.e.,
-     * returns {@link #EMPTY_KEYPAIR_PROVIDER})
-     * @return The provider wrapper
-     */
-    static KeyPairProvider wrap(Iterable<KeyPair> pairs) {
-        return (pairs == null) ? EMPTY_KEYPAIR_PROVIDER : new KeyPairProvider() {
-            @Override
-            public Iterable<KeyPair> loadKeys() {
-                return pairs;
-            }
-
-            @Override
-            public KeyPair loadKey(String type) {
-                for (KeyPair kp : pairs) {
-                    String t = KeyUtils.getKeyType(kp);
-                    if (Objects.equals(type, t)) {
-                        return kp;
-                    }
-                }
-
-                return null;
-            }
-
-            @Override
-            public Iterable<String> getKeyTypes() {
-                // use a LinkedHashSet so as to preserve the order but avoid duplicates
-                Collection<String> types = new LinkedHashSet<>();
-                for (KeyPair kp : pairs) {
-                    String t = KeyUtils.getKeyType(kp);
-                    if (GenericUtils.isEmpty(t)) {
-                        continue;   // avoid unknown key types
-                    }
-
-                    if (!types.add(t)) {
-                        continue;   // debug breakpoint
-                    }
-                }
-
-                return types;
-            }
-        };
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProviderHolder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProviderHolder.java b/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProviderHolder.java
deleted file mode 100644
index 553d553..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProviderHolder.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.common.keyprovider;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface KeyPairProviderHolder {
-    /**
-     * Retrieve the <code>KeyPairProvider</code> that will be used to find
-     * the host key to use on the server side or the user key on the client side.
-     *
-     * @return the <code>KeyPairProvider</code>, never {@code null}
-     */
-    KeyPairProvider getKeyPairProvider();
-
-    void setKeyPairProvider(KeyPairProvider keyPairProvider);
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/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
deleted file mode 100644
index bf3ec8b..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/MappedKeyPairProvider.java
+++ /dev/null
@@ -1,97 +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.keyprovider;
-
-import java.security.KeyPair;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Map;
-import java.util.TreeMap;
-import java.util.function.Function;
-
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-
-/**
- * Holds a {@link Map} of {@link String}-&gt;{@link KeyPair} where the map key
- * is the type and value is the associated {@link KeyPair}
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class MappedKeyPairProvider implements KeyPairProvider {
-    /**
-     * Transforms a {@link Map} of {@link String}-&gt;{@link KeyPair} to a
-     * {@link KeyPairProvider} where map key is the type and value is the
-     * associated {@link KeyPair}
-     */
-    public static final Function<Map<String, KeyPair>, KeyPairProvider> MAP_TO_KEY_PAIR_PROVIDER =
-            MappedKeyPairProvider::new;
-
-    private final Map<String, KeyPair> pairsMap;
-
-    public MappedKeyPairProvider(KeyPair... pairs) {
-        this(GenericUtils.isEmpty(pairs) ? Collections.emptyList() : Arrays.asList(pairs));
-    }
-
-    public MappedKeyPairProvider(Collection<? extends KeyPair> pairs) {
-        this(mapUniquePairs(pairs));
-    }
-
-    public MappedKeyPairProvider(Map<String, KeyPair> pairsMap) {
-        this.pairsMap = ValidateUtils.checkNotNullAndNotEmpty(pairsMap, "No pairs map provided");
-    }
-
-    @Override
-    public Iterable<KeyPair> loadKeys() {
-        return pairsMap.values();
-    }
-
-    @Override
-    public KeyPair loadKey(String type) {
-        return pairsMap.get(type);
-    }
-
-    @Override
-    public Iterable<String> getKeyTypes() {
-        return pairsMap.keySet();
-    }
-
-    @Override
-    public String toString() {
-        return String.valueOf(getKeyTypes());
-    }
-
-    public static Map<String, KeyPair> mapUniquePairs(Collection<? extends KeyPair> pairs) {
-        if (GenericUtils.isEmpty(pairs)) {
-            return Collections.emptyMap();
-        }
-
-        Map<String, KeyPair> pairsMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
-        for (KeyPair kp : pairs) {
-            String keyType = ValidateUtils.checkNotNullAndNotEmpty(KeyUtils.getKeyType(kp), "Cannot determine key type");
-            KeyPair prev = pairsMap.put(keyType, kp);
-            ValidateUtils.checkTrue(prev == null, "Multiple keys of type=%s", keyType);
-        }
-
-        return pairsMap;
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/mac/BaseMac.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/mac/BaseMac.java b/sshd-core/src/main/java/org/apache/sshd/common/mac/BaseMac.java
deleted file mode 100644
index e1681d4..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/mac/BaseMac.java
+++ /dev/null
@@ -1,111 +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.mac;
-
-import javax.crypto.spec.SecretKeySpec;
-
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * Base class for <code>Mac</code> implementations based on the JCE provider.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class BaseMac implements Mac {
-
-    private final String algorithm;
-    private final int defbsize;
-    private final int bsize;
-    private final byte[] tmp;
-    private javax.crypto.Mac mac;
-    private String s;
-
-    public BaseMac(String algorithm, int bsize, int defbsize) {
-        this.algorithm = algorithm;
-        this.bsize = bsize;
-        this.defbsize = defbsize;
-        this.tmp = new byte[defbsize];
-    }
-
-    @Override
-    public final String getAlgorithm() {
-        return algorithm;
-    }
-
-    @Override
-    public final int getBlockSize() {
-        return bsize;
-    }
-
-    @Override
-    public final int getDefaultBlockSize() {
-        return defbsize;
-    }
-
-    @Override
-    public void init(byte[] key) throws Exception {
-        if (key.length > defbsize) {
-            byte[] tmp = new byte[defbsize];
-            System.arraycopy(key, 0, tmp, 0, defbsize);
-            key = tmp;
-        }
-
-        SecretKeySpec skey = new SecretKeySpec(key, algorithm);
-        mac = SecurityUtils.getMac(algorithm);
-        mac.init(skey);
-    }
-
-    @Override
-    public void updateUInt(long i) {
-        tmp[0] = (byte) (i >>> 24);
-        tmp[1] = (byte) (i >>> 16);
-        tmp[2] = (byte) (i >>> 8);
-        tmp[3] = (byte) i;
-        update(tmp, 0, 4);
-    }
-
-    @Override
-    public void update(byte buf[], int offset, int len) {
-        mac.update(buf, offset, len);
-    }
-
-    @Override
-    public void doFinal(byte[] buf, int offset) throws Exception {
-        int blockSize = getBlockSize();
-        int defaultSize = getDefaultBlockSize();
-        if (blockSize != defaultSize) {
-            mac.doFinal(tmp, 0);
-            System.arraycopy(tmp, 0, buf, offset, blockSize);
-        } else {
-            mac.doFinal(buf, offset);
-        }
-    }
-
-    @Override
-    public String toString() {
-        synchronized (this) {
-            if (s == null) {
-                s = getClass().getSimpleName() + "[" + getAlgorithm() + "] - "
-                    + " block=" + getBlockSize() + "/" + getDefaultBlockSize() + " bytes";
-            }
-        }
-
-        return s;
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/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
deleted file mode 100644
index 5a4528d..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/mac/BuiltinMacs.java
+++ /dev/null
@@ -1,273 +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.mac;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.List;
-import java.util.Map;
-import java.util.NavigableSet;
-import java.util.Objects;
-import java.util.Set;
-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.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-
-/**
- * Provides easy access to the currently implemented macs
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public enum BuiltinMacs implements MacFactory {
-    hmacmd5(Constants.HMAC_MD5, "HmacMD5", 16, 16),
-    hmacmd596(Constants.HMAC_MD5_96, "HmacMD5", 12, 16),
-    hmacsha1(Constants.HMAC_SHA1, "HmacSHA1", 20, 20),
-    hmacsha196(Constants.HMAC_SHA1_96, "HmacSHA1", 12, 20),
-    /** See <A HREF="https://tools.ietf.org/html/rfc6668">RFC 6668</A> */
-    hmacsha256(Constants.HMAC_SHA2_256, "HmacSHA256", 32, 32),
-    /** See <A HREF="https://tools.ietf.org/html/rfc6668">RFC 6668</A> */
-    hmacsha512(Constants.HMAC_SHA2_512, "HmacSHA512", 64, 64);
-
-    public static final Set<BuiltinMacs> VALUES =
-            Collections.unmodifiableSet(EnumSet.allOf(BuiltinMacs.class));
-
-    private static final Map<String, MacFactory> EXTENSIONS =
-            new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
-
-    private final String factoryName;
-    private final String algorithm;
-    private final int defbsize;
-    private final int bsize;
-
-    BuiltinMacs(String factoryName, String algorithm, int bsize, int defbsize) {
-        this.factoryName = factoryName;
-        this.algorithm = algorithm;
-        this.bsize = bsize;
-        this.defbsize = defbsize;
-    }
-
-    @Override
-    public Mac create() {
-        return new BaseMac(getAlgorithm(), getBlockSize(), getDefaultBlockSize());
-    }
-
-    @Override
-    public final String getName() {
-        return factoryName;
-    }
-
-    @Override
-    public final String getAlgorithm() {
-        return algorithm;
-    }
-
-    @Override
-    public final int getBlockSize() {
-        return bsize;
-    }
-
-    @Override
-    public final int getDefaultBlockSize() {
-        return defbsize;
-    }
-
-    @Override
-    public final boolean isSupported() {
-        return true;
-    }
-
-    @Override
-    public final String toString() {
-        return getName();
-    }
-
-    /**
-     * Registered a {@link NamedFactory} to be available besides the built-in
-     * ones when parsing configuration
-     *
-     * @param extension The factory to register
-     * @throws IllegalArgumentException if factory instance is {@code null},
-     * or overrides a built-in one or overrides another registered factory
-     * with the same name (case <U>insensitive</U>).
-     */
-    public static void registerExtension(MacFactory extension) {
-        String name = Objects.requireNonNull(extension, "No extension provided").getName();
-        ValidateUtils.checkTrue(fromFactoryName(name) == null, "Extension overrides built-in: %s", name);
-
-        synchronized (EXTENSIONS) {
-            ValidateUtils.checkTrue(!EXTENSIONS.containsKey(name), "Extension overrides existing: %s", name);
-            EXTENSIONS.put(name, extension);
-        }
-    }
-
-    /**
-     * @return A {@link NavigableSet} of the currently registered extensions, sorted
-     * according to the factory name (case <U>insensitive</U>)
-     */
-    public static NavigableSet<MacFactory> getRegisteredExtensions() {
-        synchronized (EXTENSIONS) {
-            return GenericUtils.asSortedSet(NamedResource.BY_NAME_COMPARATOR, EXTENSIONS.values());
-        }
-    }
-
-    /**
-     * Unregisters specified extension
-     *
-     * @param name The factory name - ignored if {@code null}/empty
-     * @return The registered extension - {@code null} if not found
-     */
-    public static MacFactory unregisterExtension(String name) {
-        if (GenericUtils.isEmpty(name)) {
-            return null;
-        }
-
-        synchronized (EXTENSIONS) {
-            return EXTENSIONS.remove(name);
-        }
-    }
-
-    /**
-     * @param s The {@link Enum}'s name - ignored if {@code null}/empty
-     * @return The matching {@link org.apache.sshd.common.mac.BuiltinMacs} whose {@link Enum#name()} matches
-     * (case <U>insensitive</U>) the provided argument - {@code null} if no match
-     */
-    public static BuiltinMacs fromString(String s) {
-        if (GenericUtils.isEmpty(s)) {
-            return null;
-        }
-
-        for (BuiltinMacs c : VALUES) {
-            if (s.equalsIgnoreCase(c.name())) {
-                return c;
-            }
-        }
-
-        return 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<Mac> factory) {
-        if (factory == null) {
-            return null;
-        } else {
-            return fromFactoryName(factory.getName());
-        }
-    }
-
-    /**
-     * @param name The factory name - ignored if {@code null}/empty
-     * @return The matching {@link BuiltinMacs} whose factory name matches
-     * (case <U>insensitive</U>) the provided name - {@code null} if no match
-     */
-    public static BuiltinMacs fromFactoryName(String name) {
-        return NamedResource.findByName(name, String.CASE_INSENSITIVE_ORDER, VALUES);
-    }
-
-    /**
-     * @param macs A comma-separated list of MACs' names - ignored
-     *             if {@code null}/empty
-     * @return A {@link ParseResult} containing the successfully parsed
-     * factories and the unknown ones. <B>Note:</B> it is up to caller to
-     * ensure that the lists do not contain duplicates
-     */
-    public static ParseResult parseMacsList(String macs) {
-        return parseMacsList(GenericUtils.split(macs, ','));
-    }
-
-    public static ParseResult parseMacsList(String... macs) {
-        return parseMacsList(GenericUtils.isEmpty((Object[]) macs) ? Collections.emptyList() : Arrays.asList(macs));
-    }
-
-    public static ParseResult parseMacsList(Collection<String> macs) {
-        if (GenericUtils.isEmpty(macs)) {
-            return ParseResult.EMPTY;
-        }
-
-        List<MacFactory> factories = new ArrayList<>(macs.size());
-        List<String> unknown = Collections.emptyList();
-        for (String name : macs) {
-            MacFactory m = resolveFactory(name);
-            if (m != null) {
-                factories.add(m);
-            } else {
-                // replace the (unmodifiable) empty list with a real one
-                if (unknown.isEmpty()) {
-                    unknown = new ArrayList<>();
-                }
-                unknown.add(name);
-            }
-        }
-
-        return new ParseResult(factories, unknown);
-    }
-
-    /**
-     * @param name The factory name
-     * @return The factory or {@code null} if it is neither a built-in one
-     * or a registered extension
-     */
-    public static MacFactory resolveFactory(String name) {
-        if (GenericUtils.isEmpty(name)) {
-            return null;
-        }
-
-        MacFactory m = fromFactoryName(name);
-        if (m != null) {
-            return m;
-        }
-
-        synchronized (EXTENSIONS) {
-            return EXTENSIONS.get(name);
-        }
-    }
-
-    public static final class ParseResult extends NamedFactoriesListParseResult<Mac, MacFactory> {
-        public static final ParseResult EMPTY = new ParseResult(Collections.emptyList(), Collections.emptyList());
-
-        public ParseResult(List<MacFactory> parsed, List<String> unsupported) {
-            super(parsed, unsupported);
-        }
-    }
-
-    public static final class Constants {
-        public static final String HMAC_MD5 = "hmac-md5";
-        public static final String HMAC_MD5_96 = "hmac-md5-96";
-        public static final String HMAC_SHA1 = "hmac-sha1";
-        public static final String HMAC_SHA1_96 = "hmac-sha1-96";
-        public static final String HMAC_SHA2_256 = "hmac-sha2-256";
-        public static final String HMAC_SHA2_512 = "hmac-sha2-512";
-
-        private Constants() {
-            throw new UnsupportedOperationException("No instance allowed");
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/mac/Mac.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/mac/Mac.java b/sshd-core/src/main/java/org/apache/sshd/common/mac/Mac.java
deleted file mode 100644
index 4b80447..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/mac/Mac.java
+++ /dev/null
@@ -1,52 +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.mac;
-
-import org.apache.sshd.common.util.NumberUtils;
-
-/**
- * Message Authentication Code for use in SSH.
- * It usually wraps a javax.crypto.Mac class.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface Mac extends MacInformation {
-    void init(byte[] key) throws Exception;
-
-    default void update(byte[] buf) {
-        update(buf, 0, NumberUtils.length(buf));
-    }
-
-    void update(byte[] buf, int start, int len);
-
-    void updateUInt(long foo);
-
-    default byte[] doFinal() throws Exception {
-        int blockSize = getBlockSize();
-        byte[] buf = new byte[blockSize];
-        doFinal(buf);
-        return buf;
-    }
-
-    default void doFinal(byte[] buf) throws Exception {
-        doFinal(buf, 0);
-    }
-
-    void doFinal(byte[] buf, int offset) throws Exception;
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/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
deleted file mode 100644
index 4463600..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/mac/MacFactory.java
+++ /dev/null
@@ -1,31 +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.mac;
-
-import org.apache.sshd.common.BuiltinFactory;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-// CHECKSTYLE:OFF
-public interface MacFactory extends MacInformation, BuiltinFactory<Mac> {
-    // nothing extra
-}
-//CHECKSTYLE:ON

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/mac/MacInformation.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/mac/MacInformation.java b/sshd-core/src/main/java/org/apache/sshd/common/mac/MacInformation.java
deleted file mode 100644
index 583165f..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/mac/MacInformation.java
+++ /dev/null
@@ -1,41 +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.mac;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface MacInformation {
-    /**
-     * @return MAC algorithm name
-     */
-    String getAlgorithm();
-
-    /**
-     * @return MAC output block size in bytes - may be less than the default
-     * - e.g., MD5-96
-     */
-    int getBlockSize();
-
-    /**
-     * @return The &quot;natural&quot; MAC block size in bytes
-     */
-    int getDefaultBlockSize();
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/mac/package.html
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/mac/package.html b/sshd-core/src/main/java/org/apache/sshd/common/mac/package.html
deleted file mode 100644
index 52a90ca..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/mac/package.html
+++ /dev/null
@@ -1,25 +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.
--->
-<html>
-<head>
-</head>
-<body>
-
-<a href="{@docRoot}/org/apache/sshd/common/mac/Mac.html"><code>Mac</code></a> implementations.
-
-</body>
-</html>

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/random/AbstractRandom.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/random/AbstractRandom.java b/sshd-core/src/main/java/org/apache/sshd/common/random/AbstractRandom.java
deleted file mode 100644
index 2a1f825..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/random/AbstractRandom.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.common.random;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class AbstractRandom implements Random {
-    protected AbstractRandom() {
-        super();
-    }
-
-    @Override
-    public String toString() {
-        return getName();
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/random/AbstractRandomFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/random/AbstractRandomFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/random/AbstractRandomFactory.java
deleted file mode 100644
index c1d7893..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/random/AbstractRandomFactory.java
+++ /dev/null
@@ -1,43 +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.random;
-
-import org.apache.sshd.common.util.ValidateUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class AbstractRandomFactory implements RandomFactory {
-    private final String name;
-
-    protected AbstractRandomFactory(String name) {
-        this.name = ValidateUtils.checkNotNullAndNotEmpty(name, "No name");
-    }
-
-    @Override
-    public final String getName() {
-        return name;
-    }
-
-    @Override
-    public String toString() {
-        return getName();
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/random/JceRandom.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/random/JceRandom.java b/sshd-core/src/main/java/org/apache/sshd/common/random/JceRandom.java
deleted file mode 100644
index ba050e6..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/random/JceRandom.java
+++ /dev/null
@@ -1,60 +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.random;
-
-import java.security.SecureRandom;
-
-/**
- * A <code>Random</code> implementation using the built-in {@link SecureRandom} PRNG.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class JceRandom extends AbstractRandom {
-    public static final String NAME = "JCE";
-
-    private byte[] tmp = new byte[16];
-    private final SecureRandom random = new SecureRandom();
-
-    public JceRandom() {
-        super();
-    }
-
-    @Override
-    public String getName() {
-        return NAME;
-    }
-
-    @Override
-    public synchronized void fill(byte[] foo, int start, int len) {
-        if ((start == 0) && (len == foo.length)) {
-            random.nextBytes(foo);
-        } else {
-            if (len > tmp.length) {
-                tmp = new byte[len];
-            }
-            random.nextBytes(tmp);
-            System.arraycopy(tmp, 0, foo, start, len);
-        }
-    }
-
-    @Override
-    public synchronized int random(int n) {
-        return random.nextInt(n);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/random/JceRandomFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/random/JceRandomFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/random/JceRandomFactory.java
deleted file mode 100644
index 450a85e..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/random/JceRandomFactory.java
+++ /dev/null
@@ -1,42 +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.random;
-
-/**
- * Named factory for the JCE <code>Random</code>
- */
-public class JceRandomFactory extends AbstractRandomFactory {
-    public static final String NAME = "default";
-    public static final JceRandomFactory INSTANCE = new JceRandomFactory();
-
-    public JceRandomFactory() {
-        super(NAME);
-    }
-
-    @Override
-    public boolean isSupported() {
-        return true;
-    }
-
-    @Override
-    public Random create() {
-        return new JceRandom();
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/random/Random.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/random/Random.java b/sshd-core/src/main/java/org/apache/sshd/common/random/Random.java
deleted file mode 100644
index 6e597ef..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/random/Random.java
+++ /dev/null
@@ -1,56 +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.random;
-
-import org.apache.sshd.common.NamedResource;
-
-/**
- * A pseudo random number generator.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface Random extends NamedResource {
-    /**
-     * Fill the buffer with random values
-     *
-     * @param bytes The bytes to fill
-     * @see #fill(byte[], int, int)
-     */
-    default void fill(byte[] bytes) {
-        fill(bytes, 0, bytes.length);
-    }
-
-    /**
-     * Fill part of bytes with random values.
-     *
-     * @param bytes byte array to be filled.
-     * @param start index to start filling at.
-     * @param len   length of segment to fill.
-     */
-    void fill(byte[] bytes, int start, int len);
-
-    /**
-     * Returns a pseudo-random uniformly distributed {@code int}
-     * in the half-open range [0, n).
-     *
-     * @param n The range upper limit
-     * @return The randomly selected value in the range
-     */
-    int random(int n);
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/random/RandomFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/random/RandomFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/random/RandomFactory.java
deleted file mode 100644
index dded06f..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/random/RandomFactory.java
+++ /dev/null
@@ -1,31 +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.random;
-
-import org.apache.sshd.common.BuiltinFactory;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-// CHECKSTYLE:OFF
-public interface RandomFactory extends BuiltinFactory<Random> {
-    // nothing extra
-}
-//CHECKSTYLE:ON

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/random/SingletonRandomFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/random/SingletonRandomFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/random/SingletonRandomFactory.java
deleted file mode 100644
index ce24112..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/random/SingletonRandomFactory.java
+++ /dev/null
@@ -1,70 +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.random;
-
-import java.util.Objects;
-
-import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.OptionalFeature;
-
-/**
- * A random factory wrapper that uses a single random instance.
- * The underlying random instance has to be thread safe.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class SingletonRandomFactory extends AbstractRandom implements RandomFactory {
-
-    private final NamedFactory<Random> factory;
-    private final Random random;
-
-    public SingletonRandomFactory(NamedFactory<Random> factory) {
-        this.factory = Objects.requireNonNull(factory, "No factory");
-        this.random = Objects.requireNonNull(factory.create(), "No random instance created");
-    }
-
-    @Override
-    public boolean isSupported() {
-        if (factory instanceof OptionalFeature) {
-            return ((OptionalFeature) factory).isSupported();
-        } else {
-            return true;
-        }
-    }
-
-    @Override
-    public void fill(byte[] bytes, int start, int len) {
-        random.fill(bytes, start, len);
-    }
-
-    @Override
-    public int random(int max) {
-        return random.random(max);
-    }
-
-    @Override
-    public String getName() {
-        return factory.getName();
-    }
-
-    @Override
-    public Random create() {
-        return this;
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/random/package.html
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/random/package.html b/sshd-core/src/main/java/org/apache/sshd/common/random/package.html
deleted file mode 100644
index 0e94d7f..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/random/package.html
+++ /dev/null
@@ -1,25 +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.
--->
-<html>
-<head>
-</head>
-<body>
-
-<a href="{@docRoot}/org/apache/sshd/common/random/Random.html"><code>Random</code></a> implementations.
-
-</body>
-</html>

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/session/Session.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/Session.java b/sshd-core/src/main/java/org/apache/sshd/common/session/Session.java
index def98a3..f8e80c5 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/session/Session.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/session/Session.java
@@ -19,10 +19,12 @@
 package org.apache.sshd.common.session;
 
 import java.io.IOException;
+import java.util.Objects;
 import java.util.concurrent.TimeUnit;
 
 import org.apache.sshd.common.AttributeStore;
 import org.apache.sshd.common.Closeable;
+import org.apache.sshd.common.FactoryManager;
 import org.apache.sshd.common.FactoryManagerHolder;
 import org.apache.sshd.common.PropertyResolver;
 import org.apache.sshd.common.Service;
@@ -318,6 +320,11 @@ public interface Session
      */
     void startService(String name) throws Exception;
 
+    @Override
+    default <T> T resolveAttribute(AttributeKey<T> key) {
+        return resolveAttribute(this, key);
+    }
+
     /**
      * @param version The reported client/server version
      * @return {@code true} if version not empty and starts with either
@@ -327,4 +334,24 @@ public interface Session
         return GenericUtils.isNotEmpty(version)
             && (version.startsWith(DEFAULT_SSH_VERSION_PREFIX) || version.startsWith(FALLBACK_SSH_VERSION_PREFIX));
     }
+
+    /**
+     * Attempts to use the session's attribute, if not found then tries the factory manager
+     *
+     * @param <T> The generic attribute type
+     * @param session The {@link Session} - ignored if {@code null}
+     * @param key The attribute key - never {@code null}
+     * @return Associated value - {@code null} if not found
+     * @see Session#getFactoryManager()
+     * @see #resolveAttribute(FactoryManager, AttributeKey)
+     */
+    static <T> T resolveAttribute(Session session, AttributeKey<T> key) {
+        Objects.requireNonNull(key, "No key");
+        if (session == null) {
+            return null;
+        }
+
+        T value = session.getAttribute(key);
+        return (value != null) ? value : FactoryManager.resolveAttribute(session.getFactoryManager(), key);
+    }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java b/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java
index 076de1e..a739c33 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java
@@ -44,7 +44,6 @@ import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.concurrent.atomic.AtomicReference;
 
-import org.apache.sshd.common.AttributeStore;
 import org.apache.sshd.common.Closeable;
 import org.apache.sshd.common.Factory;
 import org.apache.sshd.common.FactoryManager;
@@ -2253,11 +2252,6 @@ public abstract class AbstractSession extends AbstractKexFactoryManager implemen
     }
 
     @Override
-    public <T> T resolveAttribute(AttributeKey<T> key) {
-        return AttributeStore.resolveAttribute(this, key);
-    }
-
-    @Override
     public String getUsername() {
         return username;
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/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
deleted file mode 100644
index ef06d15..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/signature/AbstractSignature.java
+++ /dev/null
@@ -1,149 +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.signature;
-
-import java.nio.charset.StandardCharsets;
-import java.security.GeneralSecurityException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.SignatureException;
-import java.util.AbstractMap.SimpleImmutableEntry;
-import java.util.Objects;
-
-import org.apache.sshd.common.util.NumberUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.buffer.BufferUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * Useful base class for {@link Signature} implementation
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class AbstractSignature implements Signature {
-    private java.security.Signature signatureInstance;
-    private final String algorithm;
-
-    protected AbstractSignature(String algorithm) {
-        this.algorithm = ValidateUtils.checkNotNullAndNotEmpty(algorithm, "No signature algorithm specified");
-    }
-
-    @Override
-    public final String getAlgorithm() {
-        return algorithm;
-    }
-
-    /**
-     * Initializes the internal signature instance
-     *
-     * @param algo The signature's algorithm
-     * @param forSigning If {@code true} then it is being initialized for signing,
-     * otherwise for verifying a signature
-     * @return The {@link java.security.Signature} instance
-     * @throws GeneralSecurityException if failed to initialize
-     */
-    protected java.security.Signature doInitSignature(String algo, boolean forSigning) throws GeneralSecurityException {
-        return SecurityUtils.getSignature(algo);
-    }
-
-    /**
-     * @return The current {@link java.security.Signature} instance
-     * - {@code null} if not initialized
-     * @see #doInitSignature(String, boolean)
-     */
-    protected java.security.Signature getSignature() {
-        return signatureInstance;
-    }
-
-    @Override
-    public byte[] sign() throws Exception {
-        java.security.Signature signature = Objects.requireNonNull(getSignature(), "Signature not initialized");
-        return signature.sign();
-    }
-
-    @Override
-    public void initVerifier(PublicKey key) throws Exception {
-        String algo = getAlgorithm();
-        signatureInstance = Objects.requireNonNull(doInitSignature(algo, false), "No signature instance create");
-        signatureInstance.initVerify(Objects.requireNonNull(key, "No public key provided"));
-    }
-
-    @Override
-    public void initSigner(PrivateKey key) throws Exception {
-        String algo = getAlgorithm();
-        signatureInstance = Objects.requireNonNull(doInitSignature(algo, true), "No signature instance create");
-        signatureInstance.initSign(Objects.requireNonNull(key, "No private key provided"));
-    }
-
-    @Override
-    public void update(byte[] hash, int off, int len) throws Exception {
-        java.security.Signature signature = Objects.requireNonNull(getSignature(), "Signature not initialized");
-        signature.update(hash, off, len);
-    }
-
-    /**
-     * Makes an attempt to detect if the signature is encoded or pure data
-     *
-     * @param sig The original signature
-     * @return A {@link SimpleImmutableEntry} where first value is the key type and second
-     * value is the data - {@code null} if not encoded
-     */
-    protected SimpleImmutableEntry<String, byte[]> extractEncodedSignature(byte[] sig) {
-        final int dataLen = NumberUtils.length(sig);
-        // if it is encoded then we must have at least 2 UINT32 values
-        if (dataLen < (2 * Integer.BYTES)) {
-            return null;
-        }
-
-        long keyTypeLen = BufferUtils.getUInt(sig, 0, dataLen);
-        // after the key type we MUST have data bytes
-        if (keyTypeLen >= (dataLen - Integer.BYTES)) {
-            return null;
-        }
-
-        int keyTypeStartPos = Integer.BYTES;
-        int keyTypeEndPos = keyTypeStartPos + (int) keyTypeLen;
-        int remainLen = dataLen - keyTypeEndPos;
-        // must have UINT32 with the data bytes length
-        if (remainLen < Integer.BYTES) {
-            return null;
-        }
-
-        long dataBytesLen = BufferUtils.getUInt(sig, keyTypeEndPos, remainLen);
-        // make sure reported number of bytes does not exceed available
-        if (dataBytesLen > (remainLen - Integer.BYTES)) {
-            return null;
-        }
-
-        String keyType = new String(sig, keyTypeStartPos, (int) keyTypeLen, StandardCharsets.UTF_8);
-        byte[] data = new byte[(int) dataBytesLen];
-        System.arraycopy(sig, keyTypeEndPos + Integer.BYTES, data, 0, (int) dataBytesLen);
-        return new SimpleImmutableEntry<>(keyType, data);
-    }
-
-    protected boolean doVerify(byte[] data) throws SignatureException {
-        java.security.Signature signature = Objects.requireNonNull(getSignature(), "Signature not initialized");
-        return signature.verify(data);
-    }
-
-    @Override
-    public String toString() {
-        return getClass().getSimpleName() + "[" + getAlgorithm() + "]";
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/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
deleted file mode 100644
index 7d4e1e2..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/signature/BuiltinSignatures.java
+++ /dev/null
@@ -1,305 +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.signature;
-
-import java.security.spec.ECParameterSpec;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.List;
-import java.util.Map;
-import java.util.NavigableSet;
-import java.util.Objects;
-import java.util.Set;
-import java.util.TreeMap;
-
-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.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * Provides easy access to the currently implemented signatures
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public enum BuiltinSignatures implements SignatureFactory {
-    dsa(KeyPairProvider.SSH_DSS) {
-        @Override
-        public Signature create() {
-            return new SignatureDSA();
-        }
-    },
-    rsa(KeyPairProvider.SSH_RSA) {
-        @Override
-        public Signature create() {
-            return new SignatureRSA();
-        }
-    },
-    nistp256(KeyPairProvider.ECDSA_SHA2_NISTP256) {
-        @Override
-        public Signature create() {
-            return new SignatureECDSA.SignatureECDSA256();
-        }
-
-        @Override
-        public boolean isSupported() {
-            return SecurityUtils.isECCSupported();
-        }
-    },
-    nistp384(KeyPairProvider.ECDSA_SHA2_NISTP384) {
-        @Override
-        public Signature create() {
-            return new SignatureECDSA.SignatureECDSA384();
-        }
-
-        @Override
-        public boolean isSupported() {
-            return SecurityUtils.isECCSupported();
-        }
-    },
-    nistp521(KeyPairProvider.ECDSA_SHA2_NISTP521) {
-        @Override
-        public Signature create() {
-            return new SignatureECDSA.SignatureECDSA521();
-        }
-
-        @Override
-        public boolean isSupported() {
-            return SecurityUtils.isECCSupported();
-        }
-    },
-    ed25519(KeyPairProvider.SSH_ED25519) {
-        @Override
-        public Signature create() {
-            return SecurityUtils.getEDDSASigner();
-        }
-
-        @Override
-        public boolean isSupported() {
-            return SecurityUtils.isEDDSACurveSupported();
-        }
-    };
-
-    public static final Set<BuiltinSignatures> VALUES =
-            Collections.unmodifiableSet(EnumSet.allOf(BuiltinSignatures.class));
-
-    private static final Map<String, SignatureFactory> EXTENSIONS =
-            new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
-
-    private final String factoryName;
-
-    BuiltinSignatures(String facName) {
-        factoryName = facName;
-    }
-
-    public static Signature getByCurveSize(ECParameterSpec params) {
-        int curveSize = ECCurves.getCurveSize(params);
-        if (curveSize <= 256) {
-            return nistp256.create();
-        } else if (curveSize <= 384) {
-            return nistp384.create();
-        } else {
-            return nistp521.create();
-        }
-    }
-
-    @Override
-    public final String getName() {
-        return factoryName;
-    }
-
-    @Override
-    public final String toString() {
-        return getName();
-    }
-
-    @Override
-    public boolean isSupported() {
-        return true;
-    }
-
-    /**
-     * Registered a {@link NamedFactory} to be available besides the built-in
-     * ones when parsing configuration
-     *
-     * @param extension The factory to register
-     * @throws IllegalArgumentException if factory instance is {@code null},
-     * or overrides a built-in one or overrides another registered factory
-     * with the same name (case <U>insensitive</U>).
-     */
-    public static void registerExtension(SignatureFactory extension) {
-        String name = Objects.requireNonNull(extension, "No extension provided").getName();
-        ValidateUtils.checkTrue(fromFactoryName(name) == null, "Extension overrides built-in: %s", name);
-
-        synchronized (EXTENSIONS) {
-            ValidateUtils.checkTrue(!EXTENSIONS.containsKey(name), "Extension overrides existing: %s", name);
-            EXTENSIONS.put(name, extension);
-        }
-    }
-
-    /**
-     * @return A {@link NavigableSet} of the currently registered extensions, sorted
-     * according to the factory name (case <U>insensitive</U>)
-     */
-    public static NavigableSet<SignatureFactory> getRegisteredExtensions() {
-        synchronized (EXTENSIONS) {
-            return GenericUtils.asSortedSet(NamedResource.BY_NAME_COMPARATOR, EXTENSIONS.values());
-        }
-    }
-
-    /**
-     * Unregisters specified extension
-     *
-     * @param name The factory name - ignored if {@code null}/empty
-     * @return The registered extension - {@code null} if not found
-     */
-    public static SignatureFactory unregisterExtension(String name) {
-        if (GenericUtils.isEmpty(name)) {
-            return null;
-        }
-
-        synchronized (EXTENSIONS) {
-            return EXTENSIONS.remove(name);
-        }
-    }
-
-    /**
-     * @param s The {@link Enum}'s name - ignored if {@code null}/empty
-     * @return The matching {@link org.apache.sshd.common.signature.BuiltinSignatures} whose {@link Enum#name()} matches
-     * (case <U>insensitive</U>) the provided argument - {@code null} if no match
-     */
-    public static BuiltinSignatures fromString(String s) {
-        if (GenericUtils.isEmpty(s)) {
-            return null;
-        }
-
-        for (BuiltinSignatures c : VALUES) {
-            if (s.equalsIgnoreCase(c.name())) {
-                return c;
-            }
-        }
-
-        return 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<Signature> factory) {
-        if (factory == null) {
-            return null;
-        } else {
-            return fromFactoryName(factory.getName());
-        }
-    }
-
-    /**
-     * @param name The factory name - ignored if {@code null}/empty
-     * @return The matching {@link BuiltinSignatures} whose factory name matches
-     * (case <U>insensitive</U>) the provided name - {@code null} if no match
-     */
-    public static BuiltinSignatures fromFactoryName(String name) {
-        return NamedResource.findByName(name, String.CASE_INSENSITIVE_ORDER, VALUES);
-    }
-
-    /**
-     * @param sigs A comma-separated list of signatures' names - ignored
-     *             if {@code null}/empty
-     * @return A {@link ParseResult} of all the {@link NamedFactory} whose
-     * name appears in the string and represent a built-in signature. Any
-     * unknown name is <U>ignored</U>. The order of the returned result
-     * is the same as the original order - bar the unknown signatures.
-     * <B>Note:</B> it is up to caller to ensure that the list does not
-     * contain duplicates
-     */
-    public static ParseResult parseSignatureList(String sigs) {
-        return parseSignatureList(GenericUtils.split(sigs, ','));
-    }
-
-    public static ParseResult parseSignatureList(String... sigs) {
-        return parseSignatureList(GenericUtils.isEmpty((Object[]) sigs) ? Collections.emptyList() : Arrays.asList(sigs));
-    }
-
-    public static ParseResult parseSignatureList(Collection<String> sigs) {
-        if (GenericUtils.isEmpty(sigs)) {
-            return ParseResult.EMPTY;
-        }
-
-        List<SignatureFactory> factories = new ArrayList<>(sigs.size());
-        List<String> unknown = Collections.emptyList();
-        for (String name : sigs) {
-            SignatureFactory s = resolveFactory(name);
-            if (s != null) {
-                factories.add(s);
-            } else {
-                // replace the (unmodifiable) empty list with a real one
-                if (unknown.isEmpty()) {
-                    unknown = new ArrayList<>();
-                }
-                unknown.add(name);
-            }
-        }
-
-        return new ParseResult(factories, unknown);
-    }
-
-    /**
-     * @param name The factory name
-     * @return The factory or {@code null} if it is neither a built-in one
-     * or a registered extension
-     */
-    public static SignatureFactory resolveFactory(String name) {
-        if (GenericUtils.isEmpty(name)) {
-            return null;
-        }
-
-        SignatureFactory s = fromFactoryName(name);
-        if (s != null) {
-            return s;
-        }
-
-        synchronized (EXTENSIONS) {
-            return EXTENSIONS.get(name);
-        }
-    }
-
-    /**
-     * Holds the result of the {@link BuiltinSignatures#parseSignatureList(String)}
-     *
-     * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
-     */
-    public static final class ParseResult extends NamedFactoriesListParseResult<Signature, SignatureFactory> {
-        public static final ParseResult EMPTY = new ParseResult(Collections.emptyList(), Collections.emptyList());
-
-        public ParseResult(List<SignatureFactory> parsed, List<String> unsupported) {
-            super(parsed, unsupported);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/signature/Signature.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/signature/Signature.java b/sshd-core/src/main/java/org/apache/sshd/common/signature/Signature.java
deleted file mode 100644
index fd88a1d..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/signature/Signature.java
+++ /dev/null
@@ -1,88 +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.signature;
-
-import java.security.PrivateKey;
-import java.security.PublicKey;
-
-import org.apache.sshd.common.util.NumberUtils;
-
-/**
- * Signature interface for SSH used to sign or verify packets
- * Usually wraps a javax.crypto.Signature object
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface Signature {
-    /**
-     * @return The signature algorithm name
-     */
-    String getAlgorithm();
-
-    /**
-     * @param key The {@link PublicKey} to be used for verifying signatures
-     * @throws Exception If failed to initialize
-     */
-    void initVerifier(PublicKey key) throws Exception;
-
-    /**
-     * @param key The {@link PrivateKey} to be used for signing
-     * @throws Exception If failed to initialize
-     */
-    void initSigner(PrivateKey key) throws Exception;
-
-    /**
-     * Update the computed signature with the given data
-     *
-     * @param hash The hash data buffer
-     * @throws Exception If failed to update
-     * @see #update(byte[], int, int)
-     */
-    default void update(byte[] hash) throws Exception {
-        update(hash, 0, NumberUtils.length(hash));
-    }
-
-    /**
-     * Update the computed signature with the given data
-     *
-     * @param hash The hash data buffer
-     * @param off  Offset of hash data in buffer
-     * @param len  Length of hash data
-     * @throws Exception If failed to update
-     */
-    void update(byte[] hash, int off, int len) throws Exception;
-
-    /**
-     * Verify against the given signature
-     *
-     * @param sig The signed data
-     * @return {@code true} if signature is valid
-     * @throws Exception If failed to extract signed data for validation
-     */
-    boolean verify(byte[] sig) throws Exception;
-
-    /**
-     * Compute the signature
-     *
-     * @return The signature value
-     * @throws Exception If failed to calculate the signature
-     */
-    byte[] sign() throws Exception;
-
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/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
deleted file mode 100644
index 1f552bd..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureDSA.java
+++ /dev/null
@@ -1,142 +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.signature;
-
-import java.io.StreamCorruptedException;
-import java.math.BigInteger;
-import java.security.SignatureException;
-import java.util.Map;
-
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.NumberUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.buffer.BufferUtils;
-import org.apache.sshd.common.util.io.der.DERParser;
-import org.apache.sshd.common.util.io.der.DERWriter;
-
-
-/**
- * DSA <code>Signature</code>
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- * @see <A HREF="https://tools.ietf.org/html/rfc4253#section-6.6">RFC4253 section 6.6</A>
- */
-public class SignatureDSA extends AbstractSignature {
-    public static final String DEFAULT_ALGORITHM = "SHA1withDSA";
-
-    public static final int DSA_SIGNATURE_LENGTH = 40;
-    // result must be 40 bytes, but length of r and s may not exceed 20 bytes
-    public static final int MAX_SIGNATURE_VALUE_LENGTH = DSA_SIGNATURE_LENGTH / 2;
-
-    public SignatureDSA() {
-        this(DEFAULT_ALGORITHM);
-    }
-
-    protected SignatureDSA(String algorithm) {
-        super(algorithm);
-    }
-
-    @Override
-    public byte[] sign() throws Exception {
-        byte[] sig = super.sign();
-
-        try (DERParser parser = new DERParser(sig)) {
-            int type = parser.read();
-            if (type != 0x30) {
-                throw new StreamCorruptedException("Invalid signature format - not a DER SEQUENCE: 0x" + Integer.toHexString(type));
-            }
-
-            // length of remaining encoding of the 2 integers
-            int remainLen = parser.readLength();
-            /*
-             * There are supposed to be 2 INTEGERs, each encoded with:
-             *
-             *  - one byte representing the fact that it is an INTEGER
-             *  - one byte of the integer encoding length
-             *  - at least one byte of integer data (zero length is not an option)
-             */
-            if (remainLen < (2 * 3)) {
-                throw new StreamCorruptedException("Invalid signature format - not enough encoded data length: " + remainLen);
-            }
-
-            BigInteger r = parser.readBigInteger();
-            BigInteger s = parser.readBigInteger();
-
-            byte[] result = new byte[DSA_SIGNATURE_LENGTH];
-            putBigInteger(r, result, 0);
-            putBigInteger(s, result, MAX_SIGNATURE_VALUE_LENGTH);
-            return result;
-        }
-    }
-
-    public static void putBigInteger(BigInteger value, byte[] result, int offset) {
-        byte[] data = value.toByteArray();
-        boolean maxExceeded = data.length > MAX_SIGNATURE_VALUE_LENGTH;
-        int dstOffset = maxExceeded ? 0 : (MAX_SIGNATURE_VALUE_LENGTH - data.length);
-        System.arraycopy(data, maxExceeded ? 1 : 0,
-                result, offset + dstOffset,
-                Math.min(MAX_SIGNATURE_VALUE_LENGTH, data.length));
-    }
-
-    @Override
-    public boolean verify(byte[] sig) throws Exception {
-        int sigLen = NumberUtils.length(sig);
-        byte[] data = sig;
-
-        if (sigLen != DSA_SIGNATURE_LENGTH) {
-            // probably some encoded data
-            Map.Entry<String, byte[]> encoding = extractEncodedSignature(sig);
-            if (encoding != null) {
-                String keyType = encoding.getKey();
-                ValidateUtils.checkTrue(KeyPairProvider.SSH_DSS.equals(keyType), "Mismatched key type: %s", keyType);
-                data = encoding.getValue();
-                sigLen = NumberUtils.length(data);
-            }
-        }
-
-        if (sigLen != DSA_SIGNATURE_LENGTH) {
-            throw new SignatureException("Bad signature length (" + sigLen + " instead of " + DSA_SIGNATURE_LENGTH + ")"
-                    + " for " + BufferUtils.toHex(':', data));
-        }
-
-        byte[] rEncoding;
-        try (DERWriter w = new DERWriter(MAX_SIGNATURE_VALUE_LENGTH + 4)) {     // in case length > 0x7F
-            w.writeBigInteger(data, 0, MAX_SIGNATURE_VALUE_LENGTH);
-            rEncoding = w.toByteArray();
-        }
-
-        byte[] sEncoding;
-        try (DERWriter w = new DERWriter(MAX_SIGNATURE_VALUE_LENGTH + 4)) {     // in case length > 0x7F
-            w.writeBigInteger(data, MAX_SIGNATURE_VALUE_LENGTH, MAX_SIGNATURE_VALUE_LENGTH);
-            sEncoding = w.toByteArray();
-        }
-
-        int length = rEncoding.length + sEncoding.length;
-        byte[] encoded;
-        try (DERWriter w = new DERWriter(1 + length + 4)) {  // in case length > 0x7F
-            w.write(0x30); // SEQUENCE
-            w.writeLength(length);
-            w.write(rEncoding);
-            w.write(sEncoding);
-            encoded = w.toByteArray();
-        }
-
-        return doVerify(encoded);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureECDSA.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureECDSA.java b/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureECDSA.java
deleted file mode 100644
index 56964d3..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureECDSA.java
+++ /dev/null
@@ -1,144 +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.signature;
-
-import java.io.StreamCorruptedException;
-import java.math.BigInteger;
-import java.util.Map;
-
-import org.apache.sshd.common.cipher.ECCurves;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.buffer.Buffer;
-import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
-import org.apache.sshd.common.util.io.der.DERParser;
-import org.apache.sshd.common.util.io.der.DERWriter;
-
-/**
- * Signature algorithm for EC keys using ECDSA.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- * @see <A HREF="http://tools.ietf.org/html/rfc3278#section-8.2">RFC3278 section 8.2</A>
- */
-public class SignatureECDSA extends AbstractSignature {
-    public static class SignatureECDSA256 extends SignatureECDSA {
-        public static final String DEFAULT_ALGORITHM = "SHA256withECDSA";
-
-        public SignatureECDSA256() {
-            super(DEFAULT_ALGORITHM);
-        }
-    }
-
-    public static class SignatureECDSA384 extends SignatureECDSA {
-        public static final String DEFAULT_ALGORITHM = "SHA384withECDSA";
-
-        public SignatureECDSA384() {
-            super(DEFAULT_ALGORITHM);
-        }
-    }
-
-    public static class SignatureECDSA521 extends SignatureECDSA {
-        public static final String DEFAULT_ALGORITHM = "SHA512withECDSA";
-
-        public SignatureECDSA521() {
-            super(DEFAULT_ALGORITHM);
-        }
-    }
-
-    protected SignatureECDSA(String algo) {
-        super(algo);
-    }
-
-    @Override
-    public byte[] sign() throws Exception {
-        byte[] sig = super.sign();
-
-        try (DERParser parser = new DERParser(sig)) {
-            int type = parser.read();
-            if (type != 0x30) {
-                throw new StreamCorruptedException("Invalid signature format - not a DER SEQUENCE: 0x" + Integer.toHexString(type));
-            }
-
-            // length of remaining encoding of the 2 integers
-            int remainLen = parser.readLength();
-            /*
-             * There are supposed to be 2 INTEGERs, each encoded with:
-             *
-             *  - one byte representing the fact that it is an INTEGER
-             *  - one byte of the integer encoding length
-             *  - at least one byte of integer data (zero length is not an option)
-             */
-            if (remainLen < (2 * 3)) {
-                throw new StreamCorruptedException("Invalid signature format - not enough encoded data length: " + remainLen);
-            }
-
-            BigInteger r = parser.readBigInteger();
-            BigInteger s = parser.readBigInteger();
-            // Write the <r,s> to its own types writer.
-            Buffer rsBuf = new ByteArrayBuffer();
-            rsBuf.putMPInt(r);
-            rsBuf.putMPInt(s);
-
-            return rsBuf.getCompactData();
-        }
-    }
-
-    @Override
-    public boolean verify(byte[] sig) throws Exception {
-        byte[] data = sig;
-        Map.Entry<String, byte[]> encoding = extractEncodedSignature(data);
-        if (encoding != null) {
-            String keyType = encoding.getKey();
-            ECCurves curve = ECCurves.fromKeyType(keyType);
-            ValidateUtils.checkNotNull(curve, "Unknown curve type: %s", keyType);
-            data = encoding.getValue();
-        }
-
-        Buffer rsBuf = new ByteArrayBuffer(data);
-        byte[] rArray = rsBuf.getMPIntAsBytes();
-        byte[] rEncoding;
-        try (DERWriter w = new DERWriter(rArray.length + 4)) {     // in case length > 0x7F
-            w.writeBigInteger(rArray);
-            rEncoding = w.toByteArray();
-        }
-
-        byte[] sArray = rsBuf.getMPIntAsBytes();
-        byte[] sEncoding;
-        try (DERWriter w = new DERWriter(sArray.length + 4)) {     // in case length > 0x7F
-            w.writeBigInteger(sArray);
-            sEncoding = w.toByteArray();
-        }
-
-        int remaining = rsBuf.available();
-        if (remaining != 0) {
-            throw new StreamCorruptedException("Signature had padding - remaining=" + remaining);
-        }
-
-        int length = rEncoding.length + sEncoding.length;
-        byte[] encoded;
-        try (DERWriter w = new DERWriter(1 + length + 4)) {  // in case length > 0x7F
-            w.write(0x30); // SEQUENCE
-            w.writeLength(length);
-            w.write(rEncoding);
-            w.write(sEncoding);
-            encoded = w.toByteArray();
-        }
-
-        return doVerify(encoded);
-    }
-}


[36/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common utilities code from sshd-core into sshd-common (new artifact)

Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/io/der/ASN1Object.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/der/ASN1Object.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/der/ASN1Object.java
new file mode 100644
index 0000000..1d32a40
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/der/ASN1Object.java
@@ -0,0 +1,338 @@
+/*
+ * 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.der;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.Serializable;
+import java.io.StreamCorruptedException;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.NumberUtils;
+import org.apache.sshd.common.util.buffer.BufferUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class ASN1Object implements Serializable, Cloneable {
+    // Constructed Flag
+    public static final byte CONSTRUCTED = 0x20;
+
+    private static final long serialVersionUID = 4687581744706127265L;
+
+    private ASN1Class objClass;
+    private ASN1Type objType;
+    private boolean constructed;
+    private int length;
+    private byte[] value;
+
+    public ASN1Object() {
+        super();
+    }
+
+    /*
+     * <P>The first byte in DER encoding is made of following fields</P>
+     * <pre>
+     *-------------------------------------------------
+     *|Bit 8|Bit 7|Bit 6|Bit 5|Bit 4|Bit 3|Bit 2|Bit 1|
+     *-------------------------------------------------
+     *|  Class    | CF  |        Type                 |
+     *-------------------------------------------------
+     * </pre>
+     */
+    public ASN1Object(byte tag, int len, byte... data) {
+        this(ASN1Class.fromDERValue(tag), ASN1Type.fromDERValue(tag), (tag & CONSTRUCTED) == CONSTRUCTED, len, data);
+    }
+
+    public ASN1Object(ASN1Class c, ASN1Type t, boolean ctored, int len, byte... data) {
+        objClass = c;
+        objType = t;
+        constructed = ctored;
+        length = len;
+        value = data;
+    }
+
+    public ASN1Class getObjClass() {
+        return objClass;
+    }
+
+    public void setObjClass(ASN1Class c) {
+        objClass = c;
+    }
+
+    public ASN1Type getObjType() {
+        return objType;
+    }
+
+    public void setObjType(ASN1Type y) {
+        objType = y;
+    }
+
+    public boolean isConstructed() {
+        return constructed;
+    }
+
+    public void setConstructed(boolean c) {
+        constructed = c;
+    }
+
+    public int getLength() {
+        return length;
+    }
+
+    public void setLength(int l) {
+        length = l;
+    }
+
+    public byte[] getValue() {
+        return value;
+    }
+
+    // if length is less than value.length then returns copy of it
+    public byte[] getPureValueBytes() {
+        byte[] bytes = getValue();
+        int available = getLength();
+        int numBytes = NumberUtils.length(bytes);
+        if (numBytes == available) {
+            return bytes;
+        }
+
+        if (available == 0) {
+            return GenericUtils.EMPTY_BYTE_ARRAY;
+        }
+
+        byte[] pure = new byte[available];
+        System.arraycopy(bytes, 0, pure, 0, available);
+        return pure;
+    }
+
+    public void setValue(byte[] v) {
+        value = v;
+    }
+
+    public DERParser createParser() {
+        return new DERParser(getValue(), 0, getLength());
+    }
+
+    public Object asObject() throws IOException {
+        ASN1Type type = getObjType();
+        if (type == null) {
+            throw new IOException("No type set");
+        }
+
+        switch (type) {
+            case INTEGER:
+                return asInteger();
+
+            case NUMERIC_STRING:
+            case PRINTABLE_STRING:
+            case VIDEOTEX_STRING:
+            case IA5_STRING:
+            case GRAPHIC_STRING:
+            case ISO646_STRING:
+            case GENERAL_STRING:
+            case BMP_STRING:
+            case UTF8_STRING:
+                return asString();
+
+            case OBJECT_IDENTIFIER:
+                return asOID();
+
+            case SEQUENCE   :
+                return getValue();
+
+            default:
+                throw new IOException("Invalid DER: unsupported type: " + type);
+        }
+    }
+
+    /**
+     * Get the value as {@link BigInteger}
+     * @return BigInteger
+     * @throws IOException if type not an {@link ASN1Type#INTEGER}
+     */
+    public BigInteger asInteger() throws IOException {
+        ASN1Type typeValue = getObjType();
+        if (ASN1Type.INTEGER.equals(typeValue)) {
+            return toInteger();
+        } else {
+            throw new IOException("Invalid DER: object is not integer: " + typeValue);
+        }
+    }
+
+    // does not check if this is an integer
+    public BigInteger toInteger() {
+        return new BigInteger(getPureValueBytes());
+    }
+
+    /**
+     * Get value as string. Most strings are treated as Latin-1.
+     * @return Java string
+     * @throws IOException if
+     */
+    public String asString() throws IOException {
+        ASN1Type type = getObjType();
+        if (type == null) {
+            throw new IOException("No type set");
+        }
+
+        final String encoding;
+        switch (type) {
+            // Not all are Latin-1 but it's the closest thing
+            case NUMERIC_STRING:
+            case PRINTABLE_STRING:
+            case VIDEOTEX_STRING:
+            case IA5_STRING:
+            case GRAPHIC_STRING:
+            case ISO646_STRING:
+            case GENERAL_STRING:
+                encoding = "ISO-8859-1";
+                break;
+
+            case BMP_STRING:
+                encoding = "UTF-16BE";
+                break;
+
+            case UTF8_STRING:
+                encoding = "UTF-8";
+                break;
+
+            case UNIVERSAL_STRING:
+                throw new IOException("Invalid DER: can't handle UCS-4 string");
+
+            default:
+                throw new IOException("Invalid DER: object is not a string: " + type);
+        }
+
+        return new String(getValue(), 0, getLength(), encoding);
+    }
+
+    public List<Integer> asOID() throws IOException {
+        ASN1Type typeValue = getObjType();
+        if (ASN1Type.OBJECT_IDENTIFIER.equals(typeValue)) {
+            return toOID();
+        } else {
+            throw new StreamCorruptedException("Invalid DER: object is not an OID: " + typeValue);
+        }
+    }
+
+    // Does not check that type is OID
+    public List<Integer> toOID() throws IOException {
+        int vLen = getLength();
+        if (vLen <= 0) {
+            throw new EOFException("Not enough data for an OID");
+        }
+
+        List<Integer> oid = new ArrayList<>(vLen + 1);
+        byte[] bytes = getValue();
+        int val1 = bytes[0] & 0xFF;
+        oid.add(Integer.valueOf(val1 / 40));
+        oid.add(Integer.valueOf(val1 % 40));
+
+        for (int curPos = 1; curPos < vLen; curPos++) {
+            int v = bytes[curPos] & 0xFF;
+            if (v <= 0x7F) {    // short form
+                oid.add(Integer.valueOf(v));
+                continue;
+            }
+
+            long curVal = v & 0x7F;
+            curPos++;
+
+            for (int subLen = 1;; subLen++, curPos++) {
+                if (curPos >= vLen) {
+                    throw new EOFException("Incomplete OID value");
+                }
+
+                if (subLen > 5) {   // 32 bit values can span at most 5 octets
+                    throw new StreamCorruptedException("OID component encoding beyond 5 bytes");
+                }
+
+                v = bytes[curPos] & 0xFF;
+                curVal = ((curVal << 7) & 0xFFFFFFFF80L) | (v & 0x7FL);
+                if (curVal  > Integer.MAX_VALUE) {
+                    throw new StreamCorruptedException("OID value exceeds 32 bits: " + curVal);
+                }
+
+                if (v <= 0x7F) {    // found last octet ?
+                    break;
+                }
+            }
+
+            oid.add(Integer.valueOf((int) (curVal & 0x7FFFFFFFL)));
+        }
+
+        return oid;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getObjClass(), getObjType())
+             + Boolean.hashCode(isConstructed())
+             + getLength()
+             + NumberUtils.hashCode(getValue(), 0, getLength());
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (this == obj) {
+            return true;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+
+        ASN1Object other = (ASN1Object) obj;
+        return Objects.equals(this.getObjClass(), other.getObjClass())
+            && Objects.equals(this.getObjType(), other.getObjType())
+            && (this.isConstructed() == other.isConstructed())
+            && (this.getLength() == other.getLength())
+            && (NumberUtils.diffOffset(this.getValue(), 0, other.getValue(), 0, this.getLength()) < 0);
+    }
+
+    @Override
+    public ASN1Object clone() {
+        try {
+            ASN1Object cpy = getClass().cast(super.clone());
+            byte[] data = cpy.getValue();
+            if (data != null) {
+                cpy.setValue(data.clone());
+            }
+            return cpy;
+        } catch (CloneNotSupportedException e) {
+            throw new IllegalStateException("Unexpected clone failure: " + e.getMessage(), e);
+        }
+    }
+
+    @Override
+    public String toString() {
+        return Objects.toString(getObjClass())
+             + "/" + getObjType()
+             + "/" + isConstructed()
+             + "[" + getLength() + "]"
+             + ": " + BufferUtils.toHex(getValue(), 0, getLength(), ':');
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/io/der/ASN1Type.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/der/ASN1Type.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/der/ASN1Type.java
new file mode 100644
index 0000000..3dd13ca
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/der/ASN1Type.java
@@ -0,0 +1,118 @@
+/*
+ * 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.der;
+
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.Set;
+
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public enum ASN1Type {
+    ANY((byte) 0x00),
+    BOOLEAN((byte) 0x01),
+    INTEGER((byte) 0x02),
+    BIT_STRING((byte) 0x03),
+    OCTET_STRING((byte) 0x04),
+    NULL((byte) 0x05),
+    OBJECT_IDENTIFIER((byte) 0x06),
+    REAL((byte) 0x09),
+    ENUMERATED((byte) 0x0a),
+    RELATIVE_OID((byte) 0x0d),
+    SEQUENCE((byte) 0x10),
+    SET((byte) 0x11),
+    NUMERIC_STRING((byte) 0x12),
+    PRINTABLE_STRING((byte) 0x13),
+    T61_STRING((byte) 0x14),
+    VIDEOTEX_STRING((byte) 0x15),
+    IA5_STRING((byte) 0x16),
+    GRAPHIC_STRING((byte) 0x19),
+    ISO646_STRING((byte) 0x1A),
+    GENERAL_STRING((byte) 0x1B),
+    UTF8_STRING((byte) 0x0C),
+    UNIVERSAL_STRING((byte) 0x1C),
+    BMP_STRING((byte) 0x1E),
+    UTC_TIME((byte) 0x17),
+    GENERALIZED_TIME((byte) 0x18);
+
+    public static final Set<ASN1Type> VALUES =
+            Collections.unmodifiableSet(EnumSet.allOf(ASN1Type.class));
+
+    private final byte typeValue;
+
+    ASN1Type(byte typeVal) {
+        typeValue = typeVal;
+    }
+
+    public byte getTypeValue() {
+        return typeValue;
+    }
+
+    public static ASN1Type fromName(String s) {
+        if (GenericUtils.isEmpty(s)) {
+            return null;
+        }
+
+        for (ASN1Type t : VALUES) {
+            if (s.equalsIgnoreCase(t.name())) {
+                return t;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * <P>The first byte in DER encoding is made of following fields</P>
+     * <pre>
+     *-------------------------------------------------
+     *|Bit 8|Bit 7|Bit 6|Bit 5|Bit 4|Bit 3|Bit 2|Bit 1|
+     *-------------------------------------------------
+     *|  Class    | CF  |        Type                 |
+     *-------------------------------------------------
+     * </pre>
+     * @param value The original DER encoded byte
+     * @return The {@link ASN1Type} value - {@code null} if no match found
+     * @see #fromTypeValue(int)
+     */
+    public static ASN1Type fromDERValue(int value) {
+        return fromTypeValue(value & 0x1F);
+    }
+
+    /**
+     * @param value The &quot;pure&quot; type value - with no extra bits set
+     * @return The {@link ASN1Type} value - {@code null} if no match found
+     */
+    public static ASN1Type fromTypeValue(int value) {
+        if ((value < 0) || (value > 0x1F)) {    // only 5 bits are used
+            return null;
+        }
+
+        for (ASN1Type t : VALUES) {
+            if (t.getTypeValue() == value) {
+                return t;
+            }
+        }
+
+        return null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/io/der/DERParser.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/der/DERParser.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/der/DERParser.java
new file mode 100644
index 0000000..5f37bd7
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/der/DERParser.java
@@ -0,0 +1,151 @@
+/*
+ * 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.der;
+
+import java.io.ByteArrayInputStream;
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StreamCorruptedException;
+import java.math.BigInteger;
+import java.util.Arrays;
+
+import org.apache.sshd.common.util.NumberUtils;
+import org.apache.sshd.common.util.buffer.BufferUtils;
+
+/**
+ * A bare minimum DER parser - just enough to be able to decode
+ * signatures and private keys
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class DERParser extends FilterInputStream {
+    /**
+     * Maximum size of data allowed by {@link #readLength()} - it is a bit
+     * arbitrary since one can encode 32-bit length data, but it is good
+     * enough for the keys
+     */
+    public static final int MAX_DER_VALUE_LENGTH = 2 * Short.MAX_VALUE;
+
+    private final byte[] lenBytes = new byte[Integer.BYTES];
+
+    public DERParser(byte... bytes) {
+        this(bytes, 0, NumberUtils.length(bytes));
+    }
+
+    public DERParser(byte[] bytes, int offset, int len) {
+        this(new ByteArrayInputStream(bytes, offset, len));
+    }
+
+    public DERParser(InputStream s) {
+        super(s);
+    }
+
+    /**
+     * Decode the length of the field. Can only support length
+     * encoding up to 4 octets. In BER/DER encoding, length can
+     * be encoded in 2 forms:
+     * <ul>
+     * <li><p>
+     * Short form - One octet. Bit 8 has value "0" and bits 7-1
+     * give the length.
+     * </p></li>
+     *
+     * <li><p>
+     * Long form - Two to 127 octets (only 4 is supported here).
+     * Bit 8 of first octet has value "1" and bits 7-1 give the
+     * number of additional length octets. Second and following
+     * octets give the length, base 256, most significant digit
+     * first.
+     * </p></li>
+     * </ul>
+     *
+     * @return The length as integer
+     * @throws IOException If invalid format found
+     */
+    public int readLength() throws IOException {
+        int i = read();
+        if (i == -1) {
+            throw new StreamCorruptedException("Invalid DER: length missing");
+        }
+
+        // A single byte short length
+        if ((i & ~0x7F) == 0) {
+            return i;
+        }
+
+        int num = i & 0x7F;
+        // TODO We can't handle length longer than 4 bytes
+        if ((i >= 0xFF) || (num > lenBytes.length)) {
+            throw new StreamCorruptedException("Invalid DER: length field too big: " + i);
+        }
+
+        // place the read bytes last so that the 1st ones are zeroes as big endian
+        Arrays.fill(lenBytes, (byte) 0);
+        int n = read(lenBytes, 4 - num, num);
+        if (n < num) {
+            throw new StreamCorruptedException("Invalid DER: length data too short: expected=" + num + ", actual=" + n);
+        }
+
+        long len = BufferUtils.getUInt(lenBytes);
+        if (len < 0x7FL) {   // according to standard: "the shortest possible length encoding must be used"
+            throw new StreamCorruptedException("Invalid DER: length not in shortest form: " + len);
+        }
+
+        if (len > MAX_DER_VALUE_LENGTH) {
+            throw new StreamCorruptedException("Invalid DER: data length too big: " + len + " (max=" + MAX_DER_VALUE_LENGTH + ")");
+        }
+
+        // we know the cast is safe since it is less than MAX_DER_VALUE_LENGTH which is ~64K
+        return (int) len;
+    }
+
+    public ASN1Object readObject() throws IOException {
+        int tag = read();
+        if (tag == -1) {
+            return null;
+        }
+
+        int length = readLength();
+        byte[] value = new byte[length];
+        int n = read(value);
+        if (n < length) {
+            throw new StreamCorruptedException("Invalid DER: stream too short, missing value: read " + n + " out of required " + length);
+        }
+
+        return new ASN1Object((byte) tag, length, value);
+    }
+
+    public BigInteger readBigInteger() throws IOException {
+        int type = read();
+        if (type != 0x02) {
+            throw new StreamCorruptedException("Invalid DER: data type is not an INTEGER: 0x" + Integer.toHexString(type));
+        }
+
+        int len = readLength();
+        byte[] value = new byte[len];
+        int n = read(value);
+        if (n < len) {
+            throw new StreamCorruptedException("Invalid DER: stream too short, missing value: read " + n + " out of required " + len);
+        }
+
+        return new BigInteger(value);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/io/der/DERWriter.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/der/DERWriter.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/der/DERWriter.java
new file mode 100644
index 0000000..bc603ee
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/der/DERWriter.java
@@ -0,0 +1,172 @@
+/*
+ * 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.der;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.StreamCorruptedException;
+import java.math.BigInteger;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.sshd.common.util.NumberUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.buffer.BufferUtils;
+import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
+
+/**
+ * A bare-minimum DER encoder - just enough so we can encoder signatures
+ * and keys data
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class DERWriter extends FilterOutputStream {
+    private final byte[] lenBytes = new byte[Integer.BYTES];
+
+    public DERWriter() {
+        this(ByteArrayBuffer.DEFAULT_SIZE);
+    }
+
+    public DERWriter(int initialSize) {
+        this(new ByteArrayOutputStream(initialSize));
+    }
+
+    public DERWriter(OutputStream stream) {
+        super(Objects.requireNonNull(stream, "No output stream"));
+    }
+
+    public DERWriter startSequence() {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        AtomicBoolean dataWritten = new AtomicBoolean(false);
+        @SuppressWarnings("resource")
+        DERWriter encloser = this;
+        return new DERWriter(baos) {
+            @Override
+            public void close() throws IOException {
+                baos.close();
+
+                if (!dataWritten.getAndSet(true)) { // detect repeated calls and write this only once
+                    encloser.writeObject(new ASN1Object(ASN1Class.UNIVERSAL, ASN1Type.SEQUENCE, false, baos.size(), baos.toByteArray()));
+                }
+            }
+        };
+    }
+
+    public void writeBigInteger(BigInteger value) throws IOException {
+        writeBigInteger(Objects.requireNonNull(value, "No value").toByteArray());
+    }
+
+    /**
+     * The integer is always considered to be positive, so if the first byte is < 0,
+     * we pad with a zero to make it positive
+     *
+     * @param bytes {@link BigInteger} bytes
+     * @throws IOException If failed to write the bytes
+     */
+    public void writeBigInteger(byte... bytes) throws IOException {
+        writeBigInteger(bytes, 0, NumberUtils.length(bytes));
+    }
+
+    /**
+     * The integer is always considered to be positive, so if the first byte is < 0,
+     * we pad with a zero to make it positive
+     *
+     * @param bytes {@link BigInteger} bytes
+     * @param off Offset in bytes data
+     * @param len Number of bytes to write
+     * @throws IOException If failed to write the bytes
+     */
+    public void writeBigInteger(byte[] bytes, int off, int len) throws IOException {
+        // Strip leading zeroes
+        while (len > 1 && bytes[off] == 0 && isPositive(bytes[off + 1])) {
+            off++;
+            len--;
+        }
+        // indicate it is an INTEGER
+        write(0x02);
+        // Pad with a zero if needed
+        if (isPositive(bytes[off])) {
+            writeLength(len);
+        } else {
+            writeLength(len + 1);
+            write(0);
+        }
+        // Write data
+        write(bytes, off, len);
+    }
+
+    private boolean isPositive(byte b) {
+        return (b & 0x80) == 0;
+    }
+
+    public void writeObject(ASN1Object obj) throws IOException {
+        Objects.requireNonNull(obj, "No ASN.1 object");
+
+        ASN1Type type = obj.getObjType();
+        byte typeValue = type.getTypeValue();
+        ASN1Class clazz = obj.getObjClass();
+        byte classValue = clazz.getClassValue();
+        byte tagValue = (byte) (((classValue << 6) & 0xC0) | (typeValue & 0x1F));
+        writeObject(tagValue, obj.getLength(), obj.getValue());
+    }
+
+    public void writeObject(byte tag, int len, byte... data) throws IOException {
+        write(tag & 0xFF);
+        writeLength(len);
+        write(data, 0, len);
+    }
+
+    public void writeLength(int len) throws IOException {
+        ValidateUtils.checkTrue(len >= 0, "Invalid length: %d", len);
+
+        // short form - MSBit is zero
+        if (len <= 127) {
+            write(len);
+            return;
+        }
+
+        BufferUtils.putUInt(len, lenBytes);
+
+        int nonZeroPos = 0;
+        for (; nonZeroPos < lenBytes.length; nonZeroPos++) {
+            if (lenBytes[nonZeroPos] != 0) {
+                break;
+            }
+        }
+
+        if (nonZeroPos >= lenBytes.length) {
+            throw new StreamCorruptedException("All zeroes length representation for len=" + len);
+        }
+
+        int bytesLen = lenBytes.length - nonZeroPos;
+        write(0x80 | bytesLen); // indicate number of octets
+        write(lenBytes, nonZeroPos, bytesLen);
+    }
+
+    public byte[] toByteArray() throws IOException {
+        if (this.out instanceof ByteArrayOutputStream) {
+            return ((ByteArrayOutputStream) this.out).toByteArray();
+        } else {
+            throw new IOException("The underlying stream is not a byte[] stream");
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/io/functors/IOFunction.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/functors/IOFunction.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/functors/IOFunction.java
new file mode 100644
index 0000000..a55ac2e
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/functors/IOFunction.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.io.functors;
+
+import java.io.IOException;
+import java.util.Objects;
+
+/**
+ * Invokes some I/O function on the input returning some output
+ * and potentially throwing an {@link IOException} in the process
+ *
+ * @param <T> Type of input
+ * @param <R> Type of output
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FunctionalInterface
+public interface IOFunction<T, R> {
+    R apply(T t) throws IOException;
+
+    /**
+     * Returns a composed function that first applies the {@code before}
+     * function to its input, and then applies this function to the result.
+     * If evaluation of either function throws an exception, it is relayed to
+     * the caller of the composed function.
+     *
+     * @param <V> the type of input to the {@code before} function, and to the
+     *           composed function
+     * @param before the function to apply before this function is applied
+     * @return a composed function that first applies the {@code before}
+     * function and then applies this function
+     * @throws NullPointerException if before is null
+     *
+     * @see #andThen(IOFunction)
+     */
+    default <V> IOFunction<V, R> compose(IOFunction<? super V, ? extends T> before) {
+        Objects.requireNonNull(before, "No composing function provided");
+        return (V v) -> apply(before.apply(v));
+    }
+
+    /**
+     * Returns a composed function that first applies this function to
+     * its input, and then applies the {@code after} function to the result.
+     * If evaluation of either function throws an exception, it is relayed to
+     * the caller of the composed function.
+     *
+     * @param <V> the type of output of the {@code after} function, and of the
+     *           composed function
+     * @param after the function to apply after this function is applied
+     * @return a composed function that first applies this function and then
+     * applies the {@code after} function
+     * @throws NullPointerException if after is null
+     *
+     * @see #compose(IOFunction)
+     */
+    default <V> IOFunction<T, V> andThen(IOFunction<? super R, ? extends V> after) {
+        Objects.requireNonNull(after, "No composing function provided");
+        return (T t) -> after.apply(apply(t));
+    }
+
+    /**
+     * Returns a function that always returns its input argument.
+     *
+     * @param <T> the type of the input and output objects to the function
+     * @return a function that always returns its input argument
+     */
+    static <T> IOFunction<T, T> identity() {
+        return t -> t;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/logging/AbstractLoggingBean.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/logging/AbstractLoggingBean.java b/sshd-common/src/main/java/org/apache/sshd/common/util/logging/AbstractLoggingBean.java
new file mode 100644
index 0000000..27fef2f
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/logging/AbstractLoggingBean.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.logging;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.sshd.common.util.GenericUtils;
+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;
+    private final AtomicReference<SimplifiedLog> simplifiedLog = new AtomicReference<>();
+
+    /**
+     * Default constructor - creates a logger using the full class name
+     */
+    protected AbstractLoggingBean() {
+        this("");
+    }
+
+    /**
+     * 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 - ignored if {@code null}
+     * or empty
+     */
+    protected AbstractLoggingBean(String discriminator) {
+        String name = getClass().getName();
+        if (GenericUtils.length(discriminator) > 0) {
+            name += "[" + discriminator + "]";
+        }
+        log = LoggerFactory.getLogger(name);
+    }
+
+    protected SimplifiedLog getSimplifiedLogger() {
+        SimplifiedLog logger;
+        synchronized (simplifiedLog) {
+            logger = simplifiedLog.get();
+            if (logger == null) {
+                logger = LoggingUtils.wrap(log);
+            }
+        }
+
+        return logger;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/logging/LoggingUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/logging/LoggingUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/util/logging/LoggingUtils.java
new file mode 100644
index 0000000..674ed1a
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/logging/LoggingUtils.java
@@ -0,0 +1,549 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.logging;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.NavigableMap;
+import java.util.Objects;
+import java.util.TreeMap;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+import java.util.logging.Level;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.NumberUtils;
+import org.apache.sshd.common.util.ReflectionUtils;
+import org.slf4j.Logger;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class LoggingUtils {
+
+    private LoggingUtils() {
+        throw new UnsupportedOperationException("No instance");
+    }
+
+    /**
+     * Scans using reflection API for all fields that are {@code public static final}
+     * that start with the given common prefix (case <U>sensitive</U>) and are of type
+     * {@link Number}.
+     *
+     * @param clazz The {@link Class} to query
+     * @param commonPrefix The expected common prefix
+     * @return A {@link NavigableMap} of all the matching fields, where key=the field's {@link Integer}
+     * value and mapping=the field's name
+     * @see #generateMnemonicMap(Class, Predicate)
+     */
+    public static NavigableMap<Integer, String> generateMnemonicMap(Class<?> clazz, final String commonPrefix) {
+        return generateMnemonicMap(clazz, f -> {
+            String name = f.getName();
+            return name.startsWith(commonPrefix);
+        });
+    }
+
+    /**
+     * Scans using reflection API for all <U>numeric {@code public static final}</U> fields
+     * that are also accepted by the predicate. Any field that is not such or fail to retrieve
+     * its value, or has a duplicate value is <U>silently</U> skipped.
+     *
+     * @param clazz The {@link Class} to query
+     * @param acceptor The {@link Predicate} used to decide whether to process the {@link Field}
+     * (besides being a {@link Number} and {@code public static final}).
+     * @return A {@link NavigableMap} of all the matching fields, where key=the field's {@link Integer}
+     * value and mapping=the field's name
+     * @see #getMnemonicFields(Class, Predicate)
+     */
+    public static NavigableMap<Integer, String> generateMnemonicMap(Class<?> clazz, Predicate<? super Field> acceptor) {
+        Collection<Field> fields = getMnemonicFields(clazz, acceptor);
+        if (GenericUtils.isEmpty(fields)) {
+            return Collections.emptyNavigableMap();
+        }
+
+        NavigableMap<Integer, String> result = new TreeMap<>(Comparator.naturalOrder());
+        for (Field f : fields) {
+            String name = f.getName();
+            try {
+                Number value = (Number) f.get(null);
+                String prev = result.put(NumberUtils.toInteger(value), name);
+                if (prev != null) {
+                    //noinspection UnnecessaryContinue
+                    continue;   // debug breakpoint
+                }
+            } catch (Exception e) {
+                //noinspection UnnecessaryContinue
+                continue;   // debug breakpoint
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * Scans using reflection API for all <U>numeric {@code public static final}</U> fields
+     * that have a common prefix and whose value is used by several of the other
+     * matching fields
+     *
+     * @param clazz The {@link Class} to query
+     * @param commonPrefix The expected common prefix
+     * @return A {@link Map} of all the mnemonic fields names whose value is the same as other
+     * fields in this map. The key is the field's name and value is its associated opcode.
+     * @see #getAmbiguousMenmonics(Class, Predicate)
+     */
+    public static Map<String, Integer> getAmbiguousMenmonics(Class<?> clazz, String commonPrefix) {
+        return getAmbiguousMenmonics(clazz, f -> {
+            String name = f.getName();
+            return name.startsWith(commonPrefix);
+        });
+    }
+
+    /**
+     * Scans using reflection API for all <U>numeric {@code public static final}</U> fields
+     * that are also accepted by the predicate and whose value is used by several of the other
+     * matching fields
+     *
+     * @param clazz The {@link Class} to query
+     * @param acceptor The {@link Predicate} used to decide whether to process the {@link Field}
+     * (besides being a {@link Number} and {@code public static final}).
+     * @return A {@link Map} of all the mnemonic fields names whose value is the same as other
+     * fields in this map. The key is the field's name and value is its associated opcode.
+     * @see #getMnemonicFields(Class, Predicate)
+     */
+    public static Map<String, Integer> getAmbiguousMenmonics(Class<?> clazz, Predicate<? super Field> acceptor) {
+        Collection<Field> fields = getMnemonicFields(clazz, acceptor);
+        if (GenericUtils.isEmpty(fields)) {
+            return Collections.emptyMap();
+        }
+
+        Map<String, Integer> result = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+        Map<Integer, List<String>> opcodesMap = new TreeMap<>(Comparator.naturalOrder());
+        for (Field f : fields) {
+            String name = f.getName();
+            try {
+                Number value = (Number) f.get(null);
+                Integer key = NumberUtils.toInteger(value);
+                List<String> nameList = opcodesMap.get(key);
+                if (nameList == null) {
+                    nameList = new ArrayList<>();
+                    opcodesMap.put(key, nameList);
+                }
+                nameList.add(name);
+
+                int numOpcodes = nameList.size();
+                if (numOpcodes > 1) {
+                    result.put(name, key);
+                    if (numOpcodes == 2) {  // add the 1st name as well
+                        result.put(nameList.get(0), key);
+                    }
+                }
+            } catch (Exception e) {
+                continue;   // debug breakpoint
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * Scans using reflection API for all <U>numeric {@code public static final}</U> fields
+     * that are also accepted by the predicate.
+     *
+     * @param clazz The {@link Class} to query
+     * @param acceptor The {@link Predicate} used to decide whether to process the {@link Field}
+     * (besides being a {@link Number} and {@code public static final}).
+     * @return A {@link Collection} of all the fields that have satisfied all conditions
+     */
+    public static Collection<Field> getMnemonicFields(Class<?> clazz, Predicate<? super Field> acceptor) {
+        return ReflectionUtils.getMatchingFields(clazz, f -> {
+            int mods = f.getModifiers();
+            if ((!Modifier.isPublic(mods)) || (!Modifier.isStatic(mods)) || (!Modifier.isFinal(mods))) {
+                return false;
+            }
+
+            Class<?> type = f.getType();
+            if (!NumberUtils.isNumericClass(type)) {
+                return false;
+            }
+
+            return acceptor.test(f);
+        });
+    }
+
+    /**
+     * Verifies if the given level is above the required threshold for logging.
+     *
+     * @param level     The {@link Level} to evaluate
+     * @param threshold The threshold {@link Level}
+     * @return {@code true} if the evaluated level is above the required
+     * threshold.
+     * <P>
+     * <B>Note(s):</B>
+     * </P>
+     * <UL>
+     * <LI><P>
+     * If either argument is {@code null} then result is {@code false}.
+     * </P></LI>
+     *
+     * <LI><P>
+     * If the evaluated level is {@link Level#OFF} then result is {@code false}
+     * regardless of the threshold.
+     * </P></LI>
+     *
+     * <LI><P>
+     * If the threshold is {@link Level#ALL} and the evaluated level is
+     * <U>not</U> {@link Level#OFF} the result is {@code true}.
+     * </P></LI>
+     *
+     * <LI><P>
+     * Otherwise, the evaluated level {@link Level#intValue()} must be
+     * greater or equal to the threshold.
+     * </P></LI>
+     * </UL>
+     */
+    public static boolean isLoggable(Level level, Level threshold) {
+        if ((level == null) || (threshold == null)) {
+            return false;
+        } else if (Level.OFF.equals(level) || Level.OFF.equals(threshold)) {
+            return false;
+        } else if (Level.ALL.equals(threshold)) {
+            return true;
+        } else {
+            return level.intValue() >= threshold.intValue();
+        }
+    }
+
+    public static SimplifiedLog wrap(final Logger logger) {
+        if (logger == null) {
+            return SimplifiedLog.EMPTY;
+        } else {
+            return new SimplifiedLog() {
+                @Override
+                public void log(Level level, Object message, Throwable t) {
+                    if (isEnabled(level)) {
+                        logMessage(logger, level, message, t);
+                    }
+
+                }
+
+                @Override
+                public boolean isEnabled(Level level) {
+                    return isLoggable(logger, level);
+                }
+            };
+        }
+    }
+
+    // NOTE: assume that level enabled has been checked !!!
+    public static void logMessage(Logger logger, Level level, Object message, Throwable t) {
+        if ((logger == null) || (level == null) || Level.OFF.equals(level)) {
+            return;
+        } else if (Level.SEVERE.equals(level)) {
+            logger.error(Objects.toString(message), t);
+        } else if (Level.WARNING.equals(level)) {
+            logger.warn(Objects.toString(message), t);
+        } else if (Level.INFO.equals(level) || Level.ALL.equals(level)) {
+            logger.info(Objects.toString(message), t);
+        } else if (Level.CONFIG.equals(level) || Level.FINE.equals(level)) {
+            logger.debug(Objects.toString(message), t);
+        } else {
+            logger.trace(Objects.toString(message), t);
+        }
+    }
+
+    /**
+     * @param logger The {@link Logger} instance - ignored if {@code null}
+     * @param level  The validate log {@link Level} - ignored if {@code null}
+     * @return <P>{@code true} if the level is enabled for the logger. The
+     * mapping of the level to the logger is as follows:</P>
+     * <UL>
+     * <LI>{@link Level#OFF} always returns {@code false}</LI>
+     * <LI>{@link Level#SEVERE} returns {@link Logger#isErrorEnabled()}</LI>
+     * <LI>{@link Level#WARNING} returns {@link Logger#isWarnEnabled()}</LI>
+     * <LI>{@link Level#INFO} and {@link Level#ALL} returns {@link Logger#isInfoEnabled()}</LI>
+     * <LI>{@link Level#CONFIG} and {@link Level#FINE} returns {@link Logger#isDebugEnabled()}</LI>
+     * <LI>All other levels return {@link Logger#isTraceEnabled()}</LI>
+     * </UL>
+     */
+    public static boolean isLoggable(Logger logger, Level level) {
+        if ((logger == null) || (level == null) || Level.OFF.equals(level)) {
+            return false;
+        } else if (Level.SEVERE.equals(level)) {
+            return logger.isErrorEnabled();
+        } else if (Level.WARNING.equals(level)) {
+            return logger.isWarnEnabled();
+        } else if (Level.INFO.equals(level) || Level.ALL.equals(level)) {
+            return logger.isInfoEnabled();
+        } else if (Level.CONFIG.equals(level) || Level.FINE.equals(level)) {
+            return logger.isDebugEnabled();
+        } else {
+            return logger.isTraceEnabled();
+        }
+    }
+
+    /**
+     * @param <T> Generic message type consumer
+     * @param logger The {@link Logger} instance to use
+     * @param level The log {@link Level} mapped as follows:</BR>
+     *
+     * <UL>
+     *     <LI>{@link Level#OFF} - {@link #nologClosure(Logger)}</LI>
+     *     <LI>{@link Level#SEVERE} - {@link #errorClosure(Logger)}</LI>
+     *     <LI>{@link Level#WARNING} - {@link #warnClosure(Logger)}</LI>
+     *     <LI>{@link Level#INFO}/{@link Level#ALL} - {@link #infoClosure(Logger)}</LI>
+     *     <LI>{@link Level#CONFIG}/{@link Level#FINE} - {@link #debugClosure(Logger)}</LI>
+     *     <LI>All others - {@link #traceClosure(Logger)}</LI>
+     * </UL>
+     * @return A consumer whose {@link Consumer#accept(Object)} method logs the
+     * {@link String#valueOf(Object)} value of its argument if the specific level is enabled
+     */
+    public static <T> Consumer<T> loggingClosure(Logger logger, Level level) {
+        return loggingClosure(logger, level, null);
+    }
+
+    public static <T> Consumer<T> loggingClosure(Logger logger, Level level, Throwable t) {
+        Objects.requireNonNull(level, "No level provided");
+
+        if (Level.OFF.equals(level)) {
+            return nologClosure(logger);
+        } else if (Level.SEVERE.equals(level)) {
+            return errorClosure(logger, t);
+        } else if (Level.WARNING.equals(level)) {
+            return warnClosure(logger, t);
+        } else if (Level.INFO.equals(level) || Level.ALL.equals(level)) {
+            return infoClosure(logger, t);
+        } else if (Level.CONFIG.equals(level) || Level.FINE.equals(level)) {
+            return debugClosure(logger, t);
+        } else {
+            return traceClosure(logger, t);
+        }
+    }
+
+    /**
+     * @param <T> Generic message type consumer
+     * @param logger The {@link Logger} instance to use
+     * @return A consumer whose {@link Consumer#accept(Object)} method logs nothing when invoked
+     */
+    public static <T> Consumer<T> nologClosure(Logger logger) {
+        Objects.requireNonNull(logger, "No logger provided");
+        return t -> { /* do nothing */ };
+    }
+
+    /**
+     * @param <T> Generic message type consumer
+     * @param logger The {@link Logger} instance to use
+     * @return A consumer whose {@link Consumer#accept(Object)} method logs the
+     * {@link String#valueOf(Object)} value of its argument if {@link Logger#isErrorEnabled()}
+     */
+    public static <T> Consumer<T> errorClosure(Logger logger) {
+        return errorClosure(logger, null);
+    }
+
+    /**
+     * @param <T> Generic message type consumer
+     * @param logger The {@link Logger} instance to use
+     * @param thrown A {@link Throwable} to attach to the message - ignored if {@code null}
+     * @return A consumer whose {@link Consumer#accept(Object)} method logs the
+     * {@link String#valueOf(Object)} value of its argument if {@link Logger#isErrorEnabled()}
+     */
+    public static <T> Consumer<T> errorClosure(Logger logger, Throwable thrown) {
+        Objects.requireNonNull(logger, "No logger provided");
+        return new Consumer<T>() {
+            @Override
+            public void accept(T input) {
+                if (logger.isErrorEnabled()) {
+                    String msg = String.valueOf(input);
+                    if (thrown == null) {
+                        logger.error(msg);
+                    } else {
+                        logger.error(msg, thrown);
+                    }
+                }
+            }
+
+            @Override
+            public String toString() {
+                return "ERROR";
+            }
+        };
+    }
+
+    /**
+     * @param <T> Generic message type consumer
+     * @param logger The {@link Logger} instance to use
+     * @return A consumer whose {@link Consumer#accept(Object)} method logs the
+     * {@link String#valueOf(Object)} value of its argument if {@link Logger#isWarnEnabled()}
+     */
+    public static <T> Consumer<T> warnClosure(Logger logger) {
+        return warnClosure(logger, null);
+    }
+
+    /**
+     * @param <T> Generic message type consumer
+     * @param logger The {@link Logger} instance to use
+     * @param thrown A {@link Throwable} to attach to the message - ignored if {@code null}
+     * @return A consumer whose {@link Consumer#accept(Object)} method logs the {@link String#valueOf(Object)}
+     * value of its argument if {@link Logger#isWarnEnabled()}
+     */
+    public static <T> Consumer<T> warnClosure(Logger logger, Throwable thrown) {
+        Objects.requireNonNull(logger, "No logger provided");
+        return new Consumer<T>() {
+            @Override
+            public void accept(T input) {
+                if (logger.isWarnEnabled()) {
+                    String msg = String.valueOf(input);
+                    if (thrown == null) {
+                        logger.warn(msg);
+                    } else {
+                        logger.warn(msg, thrown);
+                    }
+                }
+            }
+
+            @Override
+            public String toString() {
+                return "WARN";
+            }
+        };
+    }
+
+    /**
+     * @param <T> Generic message type consumer
+     * @param logger The {@link Logger} instance to use
+     * @return A consumer whose {@link Consumer#accept(Object)} method logs the {@link String#valueOf(Object)}
+     * value of its argument if {@link Logger#isInfoEnabled()}
+     */
+    public static <T> Consumer<T> infoClosure(Logger logger) {
+        return infoClosure(logger, null);
+    }
+
+    /**
+     * @param <T> Generic message type consumer
+     * @param logger The {@link Logger} instance to use
+     * @param thrown A {@link Throwable} to attach to the message - ignored if {@code null}
+     * @return A consumer whose {@link Consumer#accept(Object)} method logs the
+     * {@link String#valueOf(Object)} value of its argument if {@link Logger#isInfoEnabled()}
+     */
+    public static <T> Consumer<T> infoClosure(Logger logger, Throwable thrown) {
+        Objects.requireNonNull(logger, "No logger provided");
+        return new Consumer<T>() {
+            @Override
+            public void accept(T input) {
+                if (logger.isInfoEnabled()) {
+                    String msg = String.valueOf(input);
+                    if (thrown == null) {
+                        logger.info(msg);
+                    } else {
+                        logger.info(msg, thrown);
+                    }
+                }
+            }
+
+            @Override
+            public String toString() {
+                return "INFO";
+            }
+        };
+    }
+
+    /**
+     * @param <T> Generic message type consumer
+     * @param logger The {@link Logger} instance to use
+     * @return A consumer whose {@link Consumer#accept(Object)} method logs the
+     * {@link String#valueOf(Object)} value of its argument if {@link Logger#isDebugEnabled()}
+     */
+    public static <T> Consumer<T> debugClosure(Logger logger) {
+        return debugClosure(logger, null);
+    }
+
+    /**
+     * @param <T> Generic message type consumer
+     * @param logger The {@link Logger} instance to use
+     * @param thrown A {@link Throwable} to attach to the message - ignored if {@code null}
+     * @return A consumer whose {@link Consumer#accept(Object)} method logs the
+     * {@link String#valueOf(Object)} value of its argument if {@link Logger#isDebugEnabled()}
+     */
+    public static <T> Consumer<T> debugClosure(Logger logger, Throwable thrown) {
+        Objects.requireNonNull(logger, "No logger provided");
+        return new Consumer<T>() {
+            @Override
+            public void accept(T input) {
+                if (logger.isDebugEnabled()) {
+                    String msg = String.valueOf(input);
+                    if (thrown == null) {
+                        logger.debug(msg);
+                    } else {
+                        logger.debug(msg, thrown);
+                    }
+                }
+            }
+
+            @Override
+            public String toString() {
+                return "DEBUG";
+            }
+        };
+    }
+
+    /**
+     * @param <T> Generic message type consumer
+     * @param logger The {@link Logger} instance to use
+     * @return A consumer whose {@link Consumer#accept(Object)} method logs the
+     * {@link String#valueOf(Object)} value of its argument if {@link Logger#isTraceEnabled()}
+     */
+    public static <T> Consumer<T> traceClosure(Logger logger) {
+        return traceClosure(logger, null);
+    }
+
+    /**
+     * @param <T> Generic message type consumer
+     * @param logger The {@link Logger} instance to use
+     * @param thrown A {@link Throwable} to attach to the message - ignored if {@code null}
+     * @return A consumer whose {@link Consumer#accept(Object)} method logs the
+     * {@link String#valueOf(Object)} value of its argument if {@link Logger#isTraceEnabled()}
+     */
+    public static <T> Consumer<T> traceClosure(Logger logger, Throwable thrown) {
+        Objects.requireNonNull(logger, "No logger provided");
+        return new Consumer<T>() {
+            @Override
+            public void accept(T input) {
+                if (logger.isTraceEnabled()) {
+                    String msg = String.valueOf(input);
+                    if (thrown == null) {
+                        logger.trace(msg);
+                    } else {
+                        logger.trace(msg, thrown);
+                    }
+                }
+            }
+
+            @Override
+            public String toString() {
+                return "TRACE";
+            }
+        };
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/logging/SimplifiedLog.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/logging/SimplifiedLog.java b/sshd-common/src/main/java/org/apache/sshd/common/util/logging/SimplifiedLog.java
new file mode 100644
index 0000000..58d58ee
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/logging/SimplifiedLog.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util.logging;
+
+import java.util.logging.Level;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface SimplifiedLog {
+
+    /**
+     * An &quot;empty&quot; {@link SimplifiedLog} that does nothing
+     */
+    SimplifiedLog EMPTY = new SimplifiedLog() {
+        @Override
+        public boolean isEnabled(Level level) {
+            return false;
+        }
+
+        @Override
+        public void log(Level level, Object message, Throwable t) {
+            // ignored
+        }
+
+        @Override
+        public String toString() {
+            return "EMPTY";
+        }
+    };
+
+    boolean isEnabled(Level level);
+
+    default void log(Level level, Object message) {
+        log(level, message, null);
+    }
+
+    void log(Level level, Object message, Throwable t);
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/net/NetworkConnector.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/net/NetworkConnector.java b/sshd-common/src/main/java/org/apache/sshd/common/util/net/NetworkConnector.java
new file mode 100644
index 0000000..df7683a
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/net/NetworkConnector.java
@@ -0,0 +1,90 @@
+/*
+ * 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.net;
+
+import java.util.concurrent.TimeUnit;
+
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class NetworkConnector extends AbstractLoggingBean {
+    public static final String DEFAULT_HOST = SshdSocketAddress.LOCALHOST_IPV4;
+    public static final long DEFAULT_CONNECT_TIMEOUT = TimeUnit.SECONDS.toMillis(5L);
+    public static final long DEFAULT_READ_TIMEOUT = TimeUnit.SECONDS.toMillis(15L);
+
+    private String protocol;
+    private String host = DEFAULT_HOST;
+    private int port;
+    private long connectTimeout = DEFAULT_CONNECT_TIMEOUT;
+    private long readTimeout = DEFAULT_READ_TIMEOUT;
+
+    public NetworkConnector() {
+        super();
+    }
+
+    public String getProtocol() {
+        return protocol;
+    }
+
+    public void setProtocol(String protocol) {
+        this.protocol = protocol;
+    }
+
+    public String getHost() {
+        return host;
+    }
+
+    public void setHost(String host) {
+        this.host = host;
+    }
+
+    public int getPort() {
+        return port;
+    }
+
+    public void setPort(int port) {
+        this.port = port;
+    }
+
+    public long getConnectTimeout() {
+        return connectTimeout;
+    }
+
+    public void setConnectTimeout(long connectTimeout) {
+        this.connectTimeout = connectTimeout;
+    }
+
+    public long getReadTimeout() {
+        return readTimeout;
+    }
+
+    public void setReadTimeout(long readTimeout) {
+        this.readTimeout = readTimeout;
+    }
+
+    @Override
+    public String toString() {
+        return getProtocol() + "://" + getHost() + ":" + getPort()
+             + ";connect=" + getConnectTimeout()
+             + ";read=" + getReadTimeout();
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/net/SshdSocketAddress.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/net/SshdSocketAddress.java b/sshd-common/src/main/java/org/apache/sshd/common/util/net/SshdSocketAddress.java
new file mode 100644
index 0000000..b465d14
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/net/SshdSocketAddress.java
@@ -0,0 +1,639 @@
+/*
+ * 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.net;
+
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.NetworkInterface;
+import java.net.SocketAddress;
+import java.net.SocketException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Enumeration;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.NumberUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * <P>A simple socket address holding the host name and port number. The reason
+ * it does not extend {@link InetSocketAddress} is twofold:</P>
+ * <OL>
+ * <LI><P>
+ * The {@link InetSocketAddress} performs a DNS resolution on the
+ * provided host name - which we don't want do use until we want to
+ * create a connection using this address (thus the {@link #toInetSocketAddress()}
+ * call which executes this query
+ * </P></LI>
+ *
+ * <LI><P>
+ * If empty host name is provided we replace it with the <I>any</I>
+ * address of 0.0.0.0
+ * </P></LI>
+ * </OL>
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class SshdSocketAddress extends SocketAddress {
+    public static final String LOCALHOST_NAME = "localhost";
+    public static final String LOCALHOST_IPV4 = "127.0.0.1";
+    public static final String IPV4_ANYADDR = "0.0.0.0";
+
+    public static final Set<String> WELL_KNOWN_IPV4_ADDRESSES =
+        Collections.unmodifiableSet(new LinkedHashSet<>(Arrays.asList(LOCALHOST_IPV4, IPV4_ANYADDR)));
+
+    // 10.0.0.0 - 10.255.255.255
+    public static final String PRIVATE_CLASS_A_PREFIX = "10.";
+    // 172.16.0.0 - 172.31.255.255
+    public static final String PRIVATE_CLASS_B_PREFIX = "172.";
+    // 192.168.0.0 - 192.168.255.255
+    public static final String PRIVATE_CLASS_C_PREFIX = "192.168.";
+    // 100.64.0.0 - 100.127.255.255
+    public static final String CARRIER_GRADE_NAT_PREFIX = "100.";
+    // The IPv4 broadcast address
+    public static final String BROADCAST_ADDRESS = "255.255.255.255";
+
+    /** Max. number of hex groups (separated by &quot;:&quot;) in an IPV6 address */
+    public static final int IPV6_MAX_HEX_GROUPS = 8;
+
+    /** Max. hex digits in each IPv6 group */
+    public static final int IPV6_MAX_HEX_DIGITS_PER_GROUP = 4;
+
+    public static final String IPV6_LONG_ANY_ADDRESS = "0:0:0:0:0:0:0:0";
+    public static final String IPV6_SHORT_ANY_ADDRESS = "::";
+
+    public static final String IPV6_LONG_LOCALHOST = "0:0:0:0:0:0:0:1";
+    public static final String IPV6_SHORT_LOCALHOST = "::1";
+
+    public static final Set<String> WELL_KNOWN_IPV6_ADDRESSES =
+        Collections.unmodifiableSet(new LinkedHashSet<>(Arrays.asList(
+                IPV6_LONG_LOCALHOST, IPV6_SHORT_LOCALHOST,
+                IPV6_LONG_ANY_ADDRESS, IPV6_SHORT_ANY_ADDRESS)));
+
+    /**
+     * A dummy placeholder that can be used instead of {@code null}s
+     */
+    public static final SshdSocketAddress LOCALHOST_ADDRESS = new SshdSocketAddress(LOCALHOST_IPV4, 0);
+
+    /**
+     * Compares {@link InetAddress}-es according to their {@link InetAddress#getHostAddress()}
+     * value case <U>insensitive</U>
+     *
+     * @see #toAddressString(InetAddress)
+     */
+    public static final Comparator<InetAddress> BY_HOST_ADDRESS = (a1, a2) -> {
+        String n1 = GenericUtils.trimToEmpty(toAddressString(a1));
+        String n2 = GenericUtils.trimToEmpty(toAddressString(a2));
+        return String.CASE_INSENSITIVE_ORDER.compare(n1, n2);
+    };
+
+    /**
+     * Compares {@link SocketAddress}-es according to their host case <U>insensitive</U>
+     * and if equals, then according to their port value (if any)
+     *
+     * @see #toAddressString(SocketAddress)
+     * @see #toAddressPort(SocketAddress)
+     */
+    public static final Comparator<SocketAddress> BY_HOST_AND_PORT = (a1, a2) -> {
+        String n1 = GenericUtils.trimToEmpty(toAddressString(a1));
+        String n2 = GenericUtils.trimToEmpty(toAddressString(a2));
+        int nRes = String.CASE_INSENSITIVE_ORDER.compare(n1, n2);
+        if (nRes != 0) {
+            return nRes;
+        }
+
+        int p1 = toAddressPort(a1);
+        int p2 = toAddressPort(a2);
+        nRes = Integer.compare(p1, p2);
+        if (nRes != 0) {
+            return nRes;
+        }
+
+        return 0;
+    };
+
+    private static final long serialVersionUID = 6461645947151952729L;
+
+    private final String hostName;
+    private final int port;
+
+    public SshdSocketAddress(int port) {
+        this(IPV4_ANYADDR, port);
+    }
+
+    public SshdSocketAddress(InetSocketAddress addr) {
+        Objects.requireNonNull(addr, "No address provided");
+
+        String host = addr.getHostString();
+        hostName = GenericUtils.isEmpty(host) ? IPV4_ANYADDR : host;
+        port = addr.getPort();
+        ValidateUtils.checkTrue(port >= 0, "Port must be >= 0: %d", port);
+    }
+
+    public SshdSocketAddress(String hostName, int port) {
+        Objects.requireNonNull(hostName, "Host name may not be null");
+        this.hostName = GenericUtils.isEmpty(hostName) ? IPV4_ANYADDR : hostName;
+
+        ValidateUtils.checkTrue(port >= 0, "Port must be >= 0: %d", port);
+        this.port = port;
+    }
+
+    public String getHostName() {
+        return hostName;
+    }
+
+    public int getPort() {
+        return port;
+    }
+
+    public InetSocketAddress toInetSocketAddress() {
+        return new InetSocketAddress(getHostName(), getPort());
+    }
+
+    @Override
+    public String toString() {
+        return getHostName() + ":" + getPort();
+    }
+
+    protected boolean isEquivalent(SshdSocketAddress that) {
+        if (that == null) {
+            return false;
+        } else if (that == this) {
+            return true;
+        } else {
+            return (this.getPort() == that.getPort())
+                && (GenericUtils.safeCompare(this.getHostName(), that.getHostName(), false) == 0);
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == null) {
+            return false;
+        }
+        if (getClass() != o.getClass()) {
+            return false;
+        }
+        return isEquivalent((SshdSocketAddress) o);
+    }
+
+    @Override
+    public int hashCode() {
+        return GenericUtils.hashCode(getHostName(), Boolean.FALSE) + getPort();
+    }
+
+
+    /**
+     * Returns the first external network address assigned to this
+     * machine or null if one is not found.
+     * @return Inet4Address associated with an external interface
+     * DevNote:  We actually return InetAddress here, as Inet4Addresses are final and cannot be mocked.
+     */
+    public static InetAddress getFirstExternalNetwork4Address() {
+        List<? extends InetAddress> addresses = getExternalNetwork4Addresses();
+        return (GenericUtils.size(addresses) > 0) ? addresses.get(0) : null;
+    }
+
+    /**
+     * @return a {@link List} of local network addresses which are not multicast
+     * or localhost sorted according to {@link #BY_HOST_ADDRESS}
+     */
+    public static List<InetAddress> getExternalNetwork4Addresses() {
+        List<InetAddress> addresses = new ArrayList<>();
+        try {
+            for (Enumeration<NetworkInterface> nets = NetworkInterface.getNetworkInterfaces(); (nets != null) && nets.hasMoreElements();) {
+                NetworkInterface netint = nets.nextElement();
+                /* TODO - uncomment when 1.5 compatibility no longer required
+                if (!netint.isUp()) {
+                    continue;    // ignore non-running interfaces
+                }
+                */
+
+                for (Enumeration<InetAddress> inetAddresses = netint.getInetAddresses(); (inetAddresses != null) && inetAddresses.hasMoreElements();) {
+                    InetAddress inetAddress = inetAddresses.nextElement();
+                    if (isValidHostAddress(inetAddress)) {
+                        addresses.add(inetAddress);
+                    }
+                }
+            }
+        } catch (SocketException e) {
+            // swallow
+        }
+
+        if (GenericUtils.size(addresses) > 1) {
+            Collections.sort(addresses, BY_HOST_ADDRESS);
+        }
+
+        return addresses;
+    }
+
+    /**
+     * @param addr The {@link InetAddress} to be verified
+     * @return <P><code>true</code> if the address is:</P></BR>
+     * <UL>
+     *         <LI>Not {@code null}</LI>
+     *         <LI>An {@link Inet4Address}</LI>
+     *         <LI>Not link local</LI>
+     *         <LI>Not a multicast</LI>
+     *         <LI>Not a loopback</LI>
+     * </UL>
+     * @see InetAddress#isLinkLocalAddress()
+     * @see InetAddress#isMulticastAddress()
+     * @see InetAddress#isMulticastAddress()
+     */
+    public static boolean isValidHostAddress(InetAddress addr) {
+        if (addr == null) {
+            return false;
+        }
+
+        if (addr.isLinkLocalAddress()) {
+            return false;
+        }
+
+        if (addr.isMulticastAddress()) {
+            return false;
+        }
+
+        if (!(addr instanceof Inet4Address)) {
+            return false;   // TODO add support for IPv6 - see SSHD-746
+        }
+
+        return !isLoopback(addr);
+
+    }
+
+    /**
+     * @param addr The {@link InetAddress} to be considered
+     * @return <code>true</code> if the address is a loopback one.
+     * <B>Note:</B> if {@link InetAddress#isLoopbackAddress()}
+     * returns <code>false</code> the address <U>string</U> is checked
+     * @see #toAddressString(InetAddress)
+     * @see #isLoopback(String)
+     */
+    public static boolean isLoopback(InetAddress addr) {
+        if (addr == null) {
+            return false;
+        }
+
+        if (addr.isLoopbackAddress()) {
+            return true;
+        }
+
+        String ip = toAddressString(addr);
+        return isLoopback(ip);
+    }
+
+    /**
+     * @param ip IP value to be tested
+     * @return <code>true</code> if the IP is &quot;localhost&quot; or
+     * &quot;127.x.x.x&quot;.
+     */
+    public static boolean isLoopback(String ip) {
+        if (GenericUtils.isEmpty(ip)) {
+            return false;
+        }
+
+        if (LOCALHOST_NAME.equals(ip) || LOCALHOST_IPV4.equals(ip)) {
+            return true;
+        }
+
+        // TODO add support for IPv6 - see SSHD-746
+        String[] values = GenericUtils.split(ip, '.');
+        if (GenericUtils.length(values) != 4) {
+            return false;
+        }
+
+        for (int index = 0; index < values.length; index++) {
+            String val = values[index];
+            if (!isValidIPv4AddressComponent(val)) {
+                return false;
+            }
+
+            if (index == 0) {
+                int number = Integer.parseInt(val);
+                if (number != 127) {
+                    return false;
+                }
+            }
+        }
+
+        return true;
+    }
+
+    public static SshdSocketAddress toSshdSocketAddress(SocketAddress addr) {
+        if (addr == null) {
+            return null;
+        } else if (addr instanceof SshdSocketAddress) {
+            return (SshdSocketAddress) addr;
+        } else if (addr instanceof InetSocketAddress) {
+            InetSocketAddress isockAddress = (InetSocketAddress) addr;
+            return new SshdSocketAddress(isockAddress.getHostName(), isockAddress.getPort());
+        } else {
+            throw new UnsupportedOperationException("Cannot convert " + addr.getClass().getSimpleName()
+                    + "=" + addr + " to " + SshdSocketAddress.class.getSimpleName());
+        }
+    }
+
+    public static String toAddressString(SocketAddress addr) {
+        if (addr == null) {
+            return null;
+        } else if (addr instanceof InetSocketAddress) {
+            return ((InetSocketAddress) addr).getHostString();
+        } else if (addr instanceof SshdSocketAddress) {
+            return ((SshdSocketAddress) addr).getHostName();
+        } else {
+            return addr.toString();
+        }
+    }
+
+    /**
+     * Attempts to resolve the port value
+     *
+     * @param addr The {@link SocketAddress} to examine
+     * @return The associated port value - negative if failed to resolve
+     */
+    public static int toAddressPort(SocketAddress addr) {
+        if (addr instanceof InetSocketAddress) {
+            return ((InetSocketAddress) addr).getPort();
+        } else if (addr instanceof SshdSocketAddress) {
+            return ((SshdSocketAddress) addr).getPort();
+        } else {
+            return -1;
+        }
+    }
+
+    /**
+     * <P>Converts a {@code SocketAddress} into an {@link InetSocketAddress} if possible:</P></BR>
+     * <UL>
+     *      <LI>If already an {@link InetSocketAddress} then cast it as such</LI>
+     *      <LI>If an {@code SshdSocketAddress} then invoke {@link #toInetSocketAddress()}</LI>
+     *      <LI>Otherwise, throw an exception</LI>
+     * </UL>
+     *
+     * @param remoteAddress The {@link SocketAddress} - ignored if {@code null}
+     * @return The {@link InetSocketAddress} instance
+     * @throws ClassCastException if argument is not already an {@code InetSocketAddress}
+     * or a {@code SshdSocketAddress}
+     */
+    public static InetSocketAddress toInetSocketAddress(SocketAddress remoteAddress) {
+        if (remoteAddress == null) {
+            return null;
+        } else if (remoteAddress instanceof InetSocketAddress) {
+            return (InetSocketAddress) remoteAddress;
+        } else if (remoteAddress instanceof SshdSocketAddress) {
+            return ((SshdSocketAddress) remoteAddress).toInetSocketAddress();
+        } else {
+            throw new ClassCastException("Unknown remote address type: " + remoteAddress);
+        }
+    }
+
+    public static String toAddressString(InetAddress addr) {
+        String ip = (addr == null) ? null : addr.toString();
+        if (GenericUtils.isEmpty(ip)) {
+            return null;
+        } else {
+            return ip.replaceAll(".*/", "");
+        }
+    }
+
+    public static boolean isIPv4Address(String addr) {
+        addr = GenericUtils.trimToEmpty(addr);
+        if (GenericUtils.isEmpty(addr)) {
+            return false;
+        }
+
+        if (WELL_KNOWN_IPV4_ADDRESSES.contains(addr)) {
+            return true;
+        }
+
+        String[] comps = GenericUtils.split(addr, '.');
+        if (GenericUtils.length(comps) != 4) {
+            return false;
+        }
+
+        for (String c : comps) {
+            if (!isValidIPv4AddressComponent(c)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Checks if the address is one of the allocated private blocks
+     * @param addr The address string
+     * @return {@code true} if this is one of the allocated private
+     * blocks. <B>Note:</B> it assumes that the address string is
+     * indeed an IPv4 address
+     * @see #isIPv4Address(String)
+     * @see #PRIVATE_CLASS_A_PREFIX
+     * @see #PRIVATE_CLASS_B_PREFIX
+     * @see #PRIVATE_CLASS_C_PREFIX
+     * @see <A HREF="http://en.wikipedia.org/wiki/Private_network#Private_IPv4_address_spaces">Wiki page</A>
+     */
+    public static boolean isPrivateIPv4Address(String addr) {
+        if (GenericUtils.isEmpty(addr)) {
+            return false;
+        }
+
+        if (addr.startsWith(PRIVATE_CLASS_A_PREFIX) || addr.startsWith(PRIVATE_CLASS_C_PREFIX)) {
+            return true;
+        }
+
+        // for 172.x.x.x we need further checks
+        if (!addr.startsWith(PRIVATE_CLASS_B_PREFIX)) {
+            return false;
+        }
+
+        int nextCompPos = addr.indexOf('.', PRIVATE_CLASS_B_PREFIX.length());
+        if (nextCompPos <= PRIVATE_CLASS_B_PREFIX.length()) {
+            return false;
+        }
+
+        String value = addr.substring(PRIVATE_CLASS_B_PREFIX.length(), nextCompPos);
+        if (!isValidIPv4AddressComponent(value)) {
+            return false;
+        }
+
+        int v = Integer.parseInt(value);
+        return (v >= 16) && (v <= 31);
+    }
+
+    /**
+     * @param addr The address to be checked
+     * @return {@code true} if the address is in the 100.64.0.0/10 range
+     * @see <A HREF="http://tools.ietf.org/html/rfc6598">RFC6598</A>
+     */
+    public static boolean isCarrierGradeNatIPv4Address(String addr) {
+        if (GenericUtils.isEmpty(addr)) {
+            return false;
+        }
+
+        if (!addr.startsWith(CARRIER_GRADE_NAT_PREFIX)) {
+            return false;
+        }
+
+        int nextCompPos = addr.indexOf('.', CARRIER_GRADE_NAT_PREFIX.length());
+        if (nextCompPos <= CARRIER_GRADE_NAT_PREFIX.length()) {
+            return false;
+        }
+
+        String value = addr.substring(CARRIER_GRADE_NAT_PREFIX.length(), nextCompPos);
+        if (!isValidIPv4AddressComponent(value)) {
+            return false;
+        }
+
+        int v = Integer.parseInt(value);
+        return (v >= 64) && (v <= 127);
+    }
+
+    /**
+     * <P>Checks if the provided argument is a valid IPv4 address component:</P></BR>
+     * <UL>
+     *     <LI>Not {@code null}/empty</LI>
+     *     <LI>Has at most 3 <U>digits</U></LI>
+     *     <LI>Its value is &le; 255</LI>
+     * </UL>
+     * @param c The {@link CharSequence} to be validate
+     * @return {@code true} if valid IPv4 address component
+     */
+    public static boolean isValidIPv4AddressComponent(CharSequence c) {
+        if (GenericUtils.isEmpty(c) || (c.length() > 3)) {
+            return false;
+        }
+
+        char ch = c.charAt(0);
+        if ((ch < '0') || (ch > '9')) {
+            return false;
+        }
+
+        if (!NumberUtils.isIntegerNumber(c)) {
+            return false;
+        }
+
+        int v = Integer.parseInt(c.toString());
+        return (v >= 0) && (v <= 255);
+    }
+
+    // Based on org.apache.commons.validator.routines.InetAddressValidator#isValidInet6Address
+    public static boolean isIPv6Address(String address) {
+        address = GenericUtils.trimToEmpty(address);
+        if (GenericUtils.isEmpty(address)) {
+            return false;
+        }
+
+        if (WELL_KNOWN_IPV6_ADDRESSES.contains(address)) {
+            return true;
+        }
+
+        boolean containsCompressedZeroes = address.contains("::");
+        if (containsCompressedZeroes && (address.indexOf("::") != address.lastIndexOf("::"))) {
+            return false;
+        }
+
+        if (((address.indexOf(':') == 0) && (!address.startsWith("::")))
+                || (address.endsWith(":") && (!address.endsWith("::")))) {
+            return false;
+        }
+
+        String[] splitOctets = GenericUtils.split(address, ':');
+        List<String> octetList = new ArrayList<>(Arrays.asList(splitOctets));
+        if (containsCompressedZeroes) {
+            if (address.endsWith("::")) {
+                // String.split() drops ending empty segments
+                octetList.add("");
+            } else if (address.startsWith("::") && (!octetList.isEmpty())) {
+                octetList.remove(0);
+            }
+        }
+
+        int numOctests = octetList.size();
+        if (numOctests > IPV6_MAX_HEX_GROUPS) {
+            return false;
+        }
+
+        int validOctets = 0;
+        int emptyOctets = 0; // consecutive empty chunks
+        for (int index = 0; index < numOctests; index++) {
+            String octet = octetList.get(index);
+            int pos = octet.indexOf('%');   // is it a zone index
+            if (pos >= 0) {
+                // zone index must come last
+                if (index != (numOctests - 1)) {
+                    return false;
+                }
+
+                octet = (pos > 0) ? octet.substring(0, pos) : "";
+            }
+
+            int octetLength = octet.length();
+            if (octetLength == 0) {
+                emptyOctets++;
+                if (emptyOctets > 1) {
+                    return false;
+                }
+
+                validOctets++;
+                continue;
+            }
+
+            emptyOctets = 0;
+
+            // Is last chunk an IPv4 address?
+            if ((index == (numOctests - 1)) && (octet.indexOf('.') > 0)) {
+                if (!isIPv4Address(octet)) {
+                    return false;
+                }
+                validOctets += 2;
+                continue;
+            }
+
+            if (octetLength > IPV6_MAX_HEX_DIGITS_PER_GROUP) {
+                return false;
+            }
+
+            int octetInt = 0;
+            try {
+                octetInt = Integer.parseInt(octet, 16);
+            } catch (NumberFormatException e) {
+                return false;
+            }
+
+            if ((octetInt < 0) || (octetInt > 0x000ffff)) {
+                return false;
+            }
+
+            validOctets++;
+        }
+
+        if ((validOctets > IPV6_MAX_HEX_GROUPS)
+                || ((validOctets < IPV6_MAX_HEX_GROUPS) && (!containsCompressedZeroes))) {
+            return false;
+        }
+        return true;
+    }
+}


[08/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common utilities code from sshd-core into sshd-common (new artifact)

Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/digest/BuiltinDigestsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/digest/BuiltinDigestsTest.java b/sshd-core/src/test/java/org/apache/sshd/common/digest/BuiltinDigestsTest.java
deleted file mode 100644
index 8cf7adf..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/digest/BuiltinDigestsTest.java
+++ /dev/null
@@ -1,65 +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.digest;
-
-import java.lang.reflect.Field;
-import java.util.EnumSet;
-import java.util.Set;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class BuiltinDigestsTest extends BaseTestSupport {
-    public BuiltinDigestsTest() {
-        super();
-    }
-
-    @Test
-    public void testFromName() {
-        for (BuiltinDigests expected : BuiltinDigests.VALUES) {
-            String name = expected.getName();
-            BuiltinDigests actual = BuiltinDigests.fromFactoryName(name);
-            assertSame(name, expected, actual);
-        }
-    }
-
-    @Test
-    public void testAllConstantsCovered() throws Exception {
-        Set<BuiltinDigests> avail = EnumSet.noneOf(BuiltinDigests.class);
-        Field[] fields = BuiltinDigests.Constants.class.getFields();
-        for (Field f : fields) {
-            String name = (String) f.get(null);
-            BuiltinDigests value = BuiltinDigests.fromFactoryName(name);
-            assertNotNull("No match found for " + name, value);
-            assertTrue(name + " re-specified", avail.add(value));
-        }
-
-        assertEquals("Incomplete coverage", BuiltinDigests.VALUES, avail);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/file/root/RootedFileSystemProviderTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/file/root/RootedFileSystemProviderTest.java b/sshd-core/src/test/java/org/apache/sshd/common/file/root/RootedFileSystemProviderTest.java
index 1425400..86c8c93 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/file/root/RootedFileSystemProviderTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/file/root/RootedFileSystemProviderTest.java
@@ -37,8 +37,8 @@ import java.util.Objects;
 import java.util.Random;
 import java.util.TreeSet;
 
+import org.apache.sshd.util.test.CommonTestSupportUtils;
 import org.apache.sshd.util.test.NoIoTestCase;
-import org.apache.sshd.util.test.Utils;
 import org.junit.BeforeClass;
 import org.junit.FixMethodOrder;
 import org.junit.Test;
@@ -67,7 +67,7 @@ public class RootedFileSystemProviderTest extends AssertableFile {
     @BeforeClass
     public static void initializeFileSystem() throws IOException {
         Path targetFolder = Objects.requireNonNull(
-                Utils.detectTargetFolder(RootedFileSystemProviderTest.class), "Failed to detect target folder").toPath();
+            CommonTestSupportUtils.detectTargetFolder(RootedFileSystemProviderTest.class), "Failed to detect target folder").toPath();
         rootSandbox = FileHelper.createTestSandbox(targetFolder.resolve(TEMP_SUBFOLDER_NAME));
         fileSystem = (RootedFileSystem) new RootedFileSystemProvider().newFileSystem(rootSandbox, Collections.emptyMap());
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/forward/PortForwardingLoadTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/forward/PortForwardingLoadTest.java b/sshd-core/src/test/java/org/apache/sshd/common/forward/PortForwardingLoadTest.java
index c68e445..8fbf0ce 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/forward/PortForwardingLoadTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/forward/PortForwardingLoadTest.java
@@ -51,9 +51,9 @@ import org.apache.sshd.common.util.net.SshdSocketAddress;
 import org.apache.sshd.server.SshServer;
 import org.apache.sshd.server.forward.AcceptAllForwardingFilter;
 import org.apache.sshd.util.test.BaseTestSupport;
+import org.apache.sshd.util.test.CoreTestSupportUtils;
 import org.apache.sshd.util.test.JSchLogger;
 import org.apache.sshd.util.test.SimpleUserInfo;
-import org.apache.sshd.util.test.Utils;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.BeforeClass;
@@ -365,7 +365,7 @@ public class PortForwardingLoadTest extends BaseTestSupport {
             ss.setReuseAddress(true);
             ss.bind(new InetSocketAddress((InetAddress) null, 0));
             int forwardedPort = ss.getLocalPort();
-            int sinkPort = Utils.getFreePort();
+            int sinkPort = CoreTestSupportUtils.getFreePort();
             session.setPortForwardingR(sinkPort, TEST_LOCALHOST, forwardedPort);
             final boolean started[] = new boolean[1];
             started[0] = false;
@@ -493,7 +493,7 @@ public class PortForwardingLoadTest extends BaseTestSupport {
         Session session = createSession();
         try {
             final int forwardedPort1 = session.setPortForwardingL(0, host, port);
-            final int forwardedPort2 = Utils.getFreePort();
+            final int forwardedPort2 = CoreTestSupportUtils.getFreePort();
             session.setPortForwardingR(forwardedPort2, TEST_LOCALHOST, forwardedPort1);
             outputDebugMessage("URL: http://localhost %s", forwardedPort2);
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/forward/PortForwardingTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/forward/PortForwardingTest.java b/sshd-core/src/test/java/org/apache/sshd/common/forward/PortForwardingTest.java
index 79bc2db..3cd904f 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/forward/PortForwardingTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/forward/PortForwardingTest.java
@@ -58,9 +58,9 @@ import org.apache.sshd.server.forward.AcceptAllForwardingFilter;
 import org.apache.sshd.server.global.CancelTcpipForwardHandler;
 import org.apache.sshd.server.global.TcpipForwardHandler;
 import org.apache.sshd.util.test.BaseTestSupport;
+import org.apache.sshd.util.test.CoreTestSupportUtils;
 import org.apache.sshd.util.test.JSchLogger;
 import org.apache.sshd.util.test.SimpleUserInfo;
-import org.apache.sshd.util.test.Utils;
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
 import org.junit.FixMethodOrder;
@@ -157,7 +157,7 @@ public class PortForwardingTest extends BaseTestSupport {
     @BeforeClass
     public static void setUpTestEnvironment() throws Exception {
         JSchLogger.init();
-        sshd = Utils.setupTestServer(PortForwardingTest.class);
+        sshd = CoreTestSupportUtils.setupTestServer(PortForwardingTest.class);
         PropertyResolverUtils.updateProperty(sshd, FactoryManager.WINDOW_SIZE, 2048);
         PropertyResolverUtils.updateProperty(sshd, FactoryManager.MAX_PACKET_SIZE, 256);
         sshd.setForwardingFilter(AcceptAllForwardingFilter.INSTANCE);
@@ -221,7 +221,7 @@ public class PortForwardingTest extends BaseTestSupport {
         acceptor.bind(new InetSocketAddress(0));
         echoPort = acceptor.getLocalAddress().getPort();
 
-        client = Utils.setupTestClient(PortForwardingTest.class);
+        client = CoreTestSupportUtils.setupTestClient(PortForwardingTest.class);
         client.start();
     }
 
@@ -262,7 +262,7 @@ public class PortForwardingTest extends BaseTestSupport {
     public void testRemoteForwarding() throws Exception {
         Session session = createSession();
         try {
-            int forwardedPort = Utils.getFreePort();
+            int forwardedPort = CoreTestSupportUtils.getFreePort();
             session.setPortForwardingR(forwardedPort, TEST_LOCALHOST, echoPort);
             waitForForwardingRequest(TcpipForwardHandler.REQUEST, TimeUnit.SECONDS.toMillis(5L));
 
@@ -293,7 +293,7 @@ public class PortForwardingTest extends BaseTestSupport {
     public void testRemoteForwardingSecondTimeInSameSession() throws Exception {
         Session session = createSession();
         try {
-            int forwardedPort = Utils.getFreePort();
+            int forwardedPort = CoreTestSupportUtils.getFreePort();
             session.setPortForwardingR(forwardedPort, TEST_LOCALHOST, echoPort);
             waitForForwardingRequest(TcpipForwardHandler.REQUEST, TimeUnit.SECONDS.toMillis(5L));
 
@@ -465,7 +465,7 @@ public class PortForwardingTest extends BaseTestSupport {
     public void testLocalForwarding() throws Exception {
         Session session = createSession();
         try {
-            int forwardedPort = Utils.getFreePort();
+            int forwardedPort = CoreTestSupportUtils.getFreePort();
             session.setPortForwardingL(forwardedPort, TEST_LOCALHOST, echoPort);
 
             try (Socket s = new Socket(TEST_LOCALHOST, forwardedPort);
@@ -674,7 +674,7 @@ public class PortForwardingTest extends BaseTestSupport {
         Session session = createSession();
         try {
             // 1. Create a Port Forward
-            int forwardedPort = Utils.getFreePort();
+            int forwardedPort = CoreTestSupportUtils.getFreePort();
             session.setPortForwardingR(forwardedPort, TEST_LOCALHOST, echoPort);
             waitForForwardingRequest(TcpipForwardHandler.REQUEST, TimeUnit.SECONDS.toMillis(5L));
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/future/DefaultSshFutureTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/future/DefaultSshFutureTest.java b/sshd-core/src/test/java/org/apache/sshd/common/future/DefaultSshFutureTest.java
deleted file mode 100644
index 0b5ec12..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/future/DefaultSshFutureTest.java
+++ /dev/null
@@ -1,137 +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.future;
-
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-import org.mockito.Mockito;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class DefaultSshFutureTest extends BaseTestSupport {
-    public DefaultSshFutureTest() {
-        super();
-    }
-
-    @Test
-    @SuppressWarnings("rawtypes")
-    public void testAwaitUninterrupted() {
-        DefaultSshFuture future = new DefaultSshFuture(getCurrentTestName(), null);
-        Object expected = new Object();
-        new Thread() {
-            @Override
-            public void run() {
-                try {
-                    Thread.sleep(TimeUnit.SECONDS.toMillis(2L));
-                } catch (InterruptedException e) {
-                    throw new RuntimeException(e);
-                }
-                future.setValue(expected);
-            }
-        }.start();
-
-        future.awaitUninterruptibly();
-        assertSame("Mismatched signalled value", expected, future.getValue());
-    }
-
-    @Test
-    @SuppressWarnings({ "rawtypes", "unchecked" })
-    public void testNotifyMultipleListeners() {
-        DefaultSshFuture future = new DefaultSshFuture(getCurrentTestName(), null);
-        AtomicInteger listenerCount = new AtomicInteger(0);
-        Object expected = new Object();
-        SshFutureListener listener = f -> {
-            assertSame("Mismatched future instance", future, f);
-            assertSame("Mismatched value object", expected, future.getValue());
-            listenerCount.incrementAndGet();
-        };
-
-        int numListeners = Byte.SIZE;
-        for (int index = 0; index < numListeners; index++) {
-            future.addListener(listener);
-        }
-
-        future.setValue(expected);
-        assertEquals("Mismatched listeners invocation count", numListeners, listenerCount.get());
-    }
-
-    @Test
-    @SuppressWarnings({ "rawtypes", "unchecked" })
-    public void testListenerInvokedDirectlyAfterResultSet() {
-        DefaultSshFuture future = new DefaultSshFuture(getCurrentTestName(), null);
-        AtomicInteger listenerCount = new AtomicInteger(0);
-        Object expected = new Object();
-        SshFutureListener listener = f -> {
-            assertSame("Mismatched future instance", future, f);
-            assertSame("Mismatched value object", expected, future.getValue());
-            listenerCount.incrementAndGet();
-        };
-        future.setValue(expected);
-
-        future.addListener(listener);
-        assertEquals("Mismatched number of registered listeners", 0, future.getNumRegisteredListeners());
-        assertEquals("Listener not invoked", 1, listenerCount.get());
-    }
-
-    @Test
-    @SuppressWarnings({ "rawtypes", "unchecked" })
-    public void testAddAndRemoveRegisteredListenersBeforeResultSet() {
-        DefaultSshFuture future = new DefaultSshFuture(getCurrentTestName(), null);
-        SshFutureListener listener = Mockito.mock(SshFutureListener.class);
-        for (int index = 1; index <= Byte.SIZE; index++) {
-            future.addListener(listener);
-            assertEquals("Mismatched number of added listeners", index, future.getNumRegisteredListeners());
-        }
-
-        for (int index = future.getNumRegisteredListeners() - 1; index >= 0; index--) {
-            future.removeListener(listener);
-            assertEquals("Mismatched number of remaining listeners", index, future.getNumRegisteredListeners());
-        }
-    }
-
-    @Test
-    @SuppressWarnings({ "rawtypes", "unchecked" })
-    public void testListenerNotRemovedIfResultSet() {
-        DefaultSshFuture future = new DefaultSshFuture(getCurrentTestName(), null);
-        AtomicInteger listenerCount = new AtomicInteger(0);
-        Object expected = new Object();
-        SshFutureListener listener = f -> {
-            assertSame("Mismatched future instance", future, f);
-            assertSame("Mismatched value object", expected, future.getValue());
-            listenerCount.incrementAndGet();
-        };
-        future.addListener(listener);
-        future.setValue(expected);
-        assertEquals("Mismatched number of registered listeners", 1, future.getNumRegisteredListeners());
-        assertEquals("Listener not invoked", 1, listenerCount.get());
-
-        future.removeListener(listener);
-        assertEquals("Mismatched number of remaining listeners", 1, future.getNumRegisteredListeners());
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/keyprovider/KeyPairProviderTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/keyprovider/KeyPairProviderTest.java b/sshd-core/src/test/java/org/apache/sshd/common/keyprovider/KeyPairProviderTest.java
deleted file mode 100644
index 1c1bb9f..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/keyprovider/KeyPairProviderTest.java
+++ /dev/null
@@ -1,79 +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.keyprovider;
-
-import java.security.KeyPair;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.util.Arrays;
-import java.util.Map;
-import java.util.function.Function;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-import org.mockito.Mockito;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class KeyPairProviderTest extends BaseTestSupport {
-    public KeyPairProviderTest() {
-        super();
-    }
-
-    @Test
-    public void testEmptyKeyProvider() {
-        KeyPairProvider provider = KeyPairProvider.EMPTY_KEYPAIR_PROVIDER;
-        assertTrue("Non empty loaded keys", GenericUtils.isEmpty(provider.loadKeys()));
-        assertTrue("Non empty key type", GenericUtils.isEmpty(provider.getKeyTypes()));
-
-        for (String keyType : new String[]{null, "", getCurrentTestName()}) {
-            assertNull("Unexpected key-pair loaded for type='" + keyType + "'", provider.loadKey(keyType));
-        }
-    }
-
-    @Test
-    public void testMapToKeyPairProvider() {
-        PublicKey pubKey = Mockito.mock(PublicKey.class);
-        PrivateKey prvKey = Mockito.mock(PrivateKey.class);
-        String[] testKeys = {getCurrentTestName(), getClass().getSimpleName()};
-        Map<String, KeyPair> pairsMap = GenericUtils.toSortedMap(
-            Arrays.asList(testKeys),
-            Function.identity(),
-            k -> new KeyPair(pubKey, prvKey),
-            String.CASE_INSENSITIVE_ORDER);
-
-        KeyPairProvider provider = MappedKeyPairProvider.MAP_TO_KEY_PAIR_PROVIDER.apply(pairsMap);
-        assertEquals("Key types", pairsMap.keySet(), provider.getKeyTypes());
-        assertEquals("Key pairs", pairsMap.values(), provider.loadKeys());
-
-        pairsMap.forEach((keyType, expected) -> {
-            KeyPair actual = provider.loadKey(keyType);
-            assertSame(keyType, expected, actual);
-        });
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/mac/BuiltinMacsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/mac/BuiltinMacsTest.java b/sshd-core/src/test/java/org/apache/sshd/common/mac/BuiltinMacsTest.java
deleted file mode 100644
index ab9e7ae..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/mac/BuiltinMacsTest.java
+++ /dev/null
@@ -1,166 +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.mac;
-
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.List;
-import java.util.Random;
-import java.util.Set;
-
-import org.apache.sshd.common.NamedResource;
-import org.apache.sshd.common.mac.BuiltinMacs.ParseResult;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-import org.mockito.Mockito;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class BuiltinMacsTest extends BaseTestSupport {
-    public BuiltinMacsTest() {
-        super();
-    }
-
-    @Test
-    public void testFromName() {
-        for (BuiltinMacs expected : BuiltinMacs.VALUES) {
-            String name = expected.getName();
-            BuiltinMacs actual = BuiltinMacs.fromFactoryName(name);
-            assertSame(name, expected, actual);
-        }
-    }
-
-    @Test
-    public void testAllConstantsCovered() throws Exception {
-        Set<BuiltinMacs> avail = EnumSet.noneOf(BuiltinMacs.class);
-        Field[] fields = BuiltinMacs.Constants.class.getFields();
-        for (Field f : fields) {
-            String name = (String) f.get(null);
-            BuiltinMacs value = BuiltinMacs.fromFactoryName(name);
-            assertNotNull("No match found for " + name, value);
-            assertTrue(name + " re-specified", avail.add(value));
-        }
-
-        assertEquals("Incomplete coverage", BuiltinMacs.VALUES, avail);
-    }
-
-    @Test
-    public void testParseMacsList() {
-        List<String> builtin = NamedResource.getNameList(BuiltinMacs.VALUES);
-        List<String> unknown = Arrays.asList(getClass().getPackage().getName(), getClass().getSimpleName(), getCurrentTestName());
-        Random rnd = new Random();
-        for (int index = 0; index < (builtin.size() + unknown.size()); index++) {
-            Collections.shuffle(builtin, rnd);
-            Collections.shuffle(unknown, rnd);
-
-            List<String> weavedList = new ArrayList<>(builtin.size() + unknown.size());
-            for (int bIndex = 0, uIndex = 0; (bIndex < builtin.size()) || (uIndex < unknown.size());) {
-                boolean useBuiltin = false;
-                if (bIndex < builtin.size()) {
-                    useBuiltin = uIndex >= unknown.size() || rnd.nextBoolean();
-                }
-
-                if (useBuiltin) {
-                    weavedList.add(builtin.get(bIndex));
-                    bIndex++;
-                } else if (uIndex < unknown.size()) {
-                    weavedList.add(unknown.get(uIndex));
-                    uIndex++;
-                }
-            }
-
-            String fullList = GenericUtils.join(weavedList, ',');
-            ParseResult result = BuiltinMacs.parseMacsList(fullList);
-            List<String> parsed = NamedResource.getNameList(result.getParsedFactories());
-            List<String> missing = result.getUnsupportedFactories();
-
-            // makes sure not only that the contents are the same but also the order
-            assertListEquals(fullList + "[parsed]", builtin, parsed);
-            assertListEquals(fullList + "[unsupported]", unknown, missing);
-        }
-    }
-
-    @Test
-    public void testResolveFactoryOnBuiltinValues() {
-        for (MacFactory expected : BuiltinMacs.VALUES) {
-            String name = expected.getName();
-            MacFactory actual = BuiltinMacs.resolveFactory(name);
-            assertSame(name, expected, actual);
-        }
-    }
-
-    @Test
-    public void testNotAllowedToRegisterBuiltinFactories() {
-        for (MacFactory expected : BuiltinMacs.VALUES) {
-            try {
-                BuiltinMacs.registerExtension(expected);
-                fail("Unexpected success for " + expected.getName());
-            } catch (IllegalArgumentException e) {
-                // expected - ignored
-            }
-        }
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void testNotAllowedToOverrideRegisteredFactories() {
-        MacFactory expected = Mockito.mock(MacFactory.class);
-        Mockito.when(expected.getName()).thenReturn(getCurrentTestName());
-
-        String name = expected.getName();
-        try {
-            for (int index = 1; index <= Byte.SIZE; index++) {
-                BuiltinMacs.registerExtension(expected);
-                assertEquals("Unexpected success at attempt #" + index, 1, index);
-            }
-        } finally {
-            BuiltinMacs.unregisterExtension(name);
-        }
-    }
-
-    @Test
-    public void testResolveFactoryOnRegisteredExtension() {
-        MacFactory expected = Mockito.mock(MacFactory.class);
-        Mockito.when(expected.getName()).thenReturn(getCurrentTestName());
-
-        String name = expected.getName();
-        try {
-            assertNull("Extension already registered", BuiltinMacs.resolveFactory(name));
-            BuiltinMacs.registerExtension(expected);
-
-            MacFactory actual = BuiltinMacs.resolveFactory(name);
-            assertSame("Mismatched resolved instance", expected, actual);
-        } finally {
-            MacFactory actual = BuiltinMacs.unregisterExtension(name);
-            assertSame("Mismatched unregistered instance", expected, actual);
-            assertNull("Extension not un-registered", BuiltinMacs.resolveFactory(name));
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/mac/MacTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/mac/MacTest.java b/sshd-core/src/test/java/org/apache/sshd/common/mac/MacTest.java
index 823955b..4d3bfa8 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/mac/MacTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/mac/MacTest.java
@@ -32,10 +32,11 @@ import org.apache.sshd.common.channel.Channel;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.server.SshServer;
 import org.apache.sshd.util.test.BaseTestSupport;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
+import org.apache.sshd.util.test.CoreTestSupportUtils;
 import org.apache.sshd.util.test.JSchLogger;
 import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
 import org.apache.sshd.util.test.SimpleUserInfo;
-import org.apache.sshd.util.test.Utils;
 import org.junit.AfterClass;
 import org.junit.Assume;
 import org.junit.Before;
@@ -115,8 +116,8 @@ public class MacTest extends BaseTestSupport {
     public static void setupClientAndServer() throws Exception {
         JSchLogger.init();
 
-        sshd = Utils.setupTestServer(MacTest.class);
-        sshd.setKeyPairProvider(Utils.createTestHostKeyProvider(MacTest.class));
+        sshd = CoreTestSupportUtils.setupTestServer(MacTest.class);
+        sshd.setKeyPairProvider(CommonTestSupportUtils.createTestHostKeyProvider(MacTest.class));
         sshd.start();
         port = sshd.getPort();
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/mac/MacVectorsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/mac/MacVectorsTest.java b/sshd-core/src/test/java/org/apache/sshd/common/mac/MacVectorsTest.java
deleted file mode 100644
index 4887099..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/mac/MacVectorsTest.java
+++ /dev/null
@@ -1,311 +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.mac;
-
-import java.nio.charset.StandardCharsets;
-import java.util.AbstractMap.SimpleImmutableEntry;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-
-import org.apache.sshd.common.Factory;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.buffer.BufferUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runner.RunWith;
-import org.junit.runners.MethodSorters;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-import org.junit.runners.Parameterized.UseParametersRunnerFactory;
-
-/**
- * @see <A HREF="https://tools.ietf.org/html/rfc4231">RFC 4321</A>
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@RunWith(Parameterized.class)   // see https://github.com/junit-team/junit/wiki/Parameterized-tests
-@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
-@Category({ NoIoTestCase.class })
-public class MacVectorsTest extends BaseTestSupport {
-    private final VectorSeed seed;
-    private final Factory<? extends Mac> macFactory;
-    private final byte[] expected;
-
-    public MacVectorsTest(VectorSeed seed, String factoryName, String expected) {
-        this.seed = Objects.requireNonNull(seed, "No seed");
-        this.macFactory = ValidateUtils.checkNotNull(BuiltinMacs.fromFactoryName(factoryName), "Unknown MAC: %s", factoryName);
-        this.expected = BufferUtils.decodeHex(BufferUtils.EMPTY_HEX_SEPARATOR, expected);
-    }
-
-    @Parameters(name = "factory={1}, expected={2}, seed={0}")
-    public static Collection<Object[]> parameters() {
-        List<Object[]> ret = new ArrayList<>();
-        for (VectorTestData vector : Collections.unmodifiableList(
-                Arrays.asList(
-                    ///////////////// Test Cases for HMAC-MD5 ///////////////////////
-                    // see https://tools.ietf.org/html/rfc2202
-                    new VectorTestData("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", false, "Hi There",
-                       Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_MD5,     // test case 1
-                                                "9294727a3638bb1c13f48ef8158bfc9d"))),
-                    new VectorTestData("Jefe", "what do ya want for nothing?",
-                        Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_MD5,    // test case 2
-                                                 "750c783e6ab0b503eaa86e310a5db738"))),
-                    new VectorTestData("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", false, repeat("dd", 50), false,
-                        Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_MD5,    // test case 3
-                                                 "56be34521d144c88dbb8c733f0e8b3f6"))),
-                    /* TODO see why it fails
-                    new VectorTestData("0102030405060708090a0b0c0d0e0f10111213141516171819", false, repeat("cd", 50), false,
-                        Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_MD5,    // test case 4
-                                                 "697eaf0aca3a3aea3a75164746ffaa79"))),
-                    */
-                    new VectorTestData("0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c", false, "Test With Truncation",
-                        Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_MD5,    // test case 5
-                                                 "56461ef2342edc00f9bab995690efd4c"),
-                                      new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_MD5_96,
-                                                 "56461ef2342edc00f9bab995"))),
-                    /* TODO see why it fails
-                    new VectorTestData(repeat("aa", 80), false, "Test Using Larger Than Block-Size Key - Hash Key First",
-                        Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_MD5,    // test case 6
-                                                 "6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd"))),
-                    */
-                    /* TODO see why it fails
-                    new VectorTestData(repeat("aa", 80), false, "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data",
-                        Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_MD5,    // test case 7
-                                                 "6f630fad67cda0ee1fb1f562db3aa53e"))),
-                    */
-                    ///////////////// Test Cases for HMAC-SHA-1 ///////////////////////
-                    // see https://tools.ietf.org/html/rfc2202
-                    new VectorTestData("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", false, "Hi There",
-                        Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA1,     // test case 1
-                                                 "b617318655057264e28bc0b6fb378c8ef146be00"))),
-                    new VectorTestData("Jefe", "what do ya want for nothing?",
-                        Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA1,     // test case 2
-                                                 "effcdf6ae5eb2fa2d27416d5f184df9c259a7c79"))),
-                    new VectorTestData("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", false, repeat("dd", 50), false,
-                        Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA1,     // test case 3
-                                                 "125d7342b9ac11cd91a39af48aa17b4f63f175d3"))),
-                    /* TODO see why it fails
-                    new VectorTestData("0102030405060708090a0b0c0d0e0f10111213141516171819", false, repeat("cd", 50), false,
-                        Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA1,     // test case 4
-                                                 "4c9007f4026250c6bc8414f9bf50c86c2d7235da"))),
-                    */
-                    new VectorTestData("0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c", false, "Test With Truncation",
-                        Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA1,     // test case 5
-                                                 "4c1a03424b55e07fe7f27be1d58bb9324a9a5a04"),
-                                      new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA1_96,
-                                                 "4c1a03424b55e07fe7f27be1"))),
-                    /* TODO see why this fails
-                    new VectorTestData(repeat("aa", 80), false, "Test Using Larger Than Block-Size Key - Hash Key First",
-                        Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA1,     // test case 6
-                                                 "aa4ae5e15272d00e95705637ce8a3b55ed402112"))),
-                    */
-
-                    /* TODO see why it fails
-                    new VectorTestData(repeat("aa", 80), false, "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data",
-                        Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA1,     // test case 7
-                                                 "4c1a03424b55e07fe7f27be1d58bb9324a9a5a04"),
-                                      new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA1_96,
-                                                 "4c1a03424b55e07fe7f27be1"))),
-                    */
-
-                    /* TODO see why it fails
-                    new VectorTestData(repeat("aa", 80), false, "Test Using Larger Than Block-Size Key - Hash Key First",
-                        Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA1,     // test case 8
-                                                 "aa4ae5e15272d00e95705637ce8a3b55ed402112"))),
-                    */
-
-                    /* TODO see why it fails
-                    new VectorTestData(repeat("aa", 80), false, "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data",
-                        Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA1,     // test case 9
-                                                 "e8e99d0f45237d786d6bbaa7965c7808bbff1a91"))),
-                    */
-
-                    ///////////////// Test Cases for HMAC-SHA-2 ///////////////////////
-                    // see https://tools.ietf.org/html/rfc4231
-                    new VectorTestData(repeat("0b", 20), false, "Hi There",
-                       // test case 1
-                       Arrays.asList(
-                           new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_256,
-                                      "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7"),
-                           new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_512,
-                                      "87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cdedaa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a126854"))),
-                    new VectorTestData("Jefe", "what do ya want for nothing?",
-                        // test case 2
-                        Arrays.asList(
-                            new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_256,
-                                       "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843"),
-                            new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_512,
-                                       "164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea2505549758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737"))),
-                    new VectorTestData(repeat("aa", 20), false, repeat("dd", 50), false,
-                        // test case 3
-                        Arrays.asList(
-                            new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_256,
-                                       "773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe"),
-                            new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_512,
-                                       "fa73b0089d56a284efb0f0756c890be9b1b5dbdd8ee81a3655f83e33b2279d39bf3e848279a722c806b485a47e67c807b946a337bee8942674278859e13292fb"))),
-                    new VectorTestData("0102030405060708090a0b0c0d0e0f10111213141516171819", false, repeat("cd", 50), false,
-                        // test case 4
-                        Arrays.asList(
-                            new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_256,
-                                       "82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b"),
-                            new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_512,
-                                       "b0ba465637458c6990e5a8c5f61d4af7e576d97ff94b872de76f8050361ee3dba91ca5c11aa25eb4d679275cc5788063a5f19741120c4f2de2adebeb10a298dd"))),
-
-                    /* TODO see why it fails
-                    new VectorTestData(repeat("0c", 20), false, "Test With Truncation",
-                        Arrays.asList(   // test case 5
-                            new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_256,
-                                       "a3b6167473100ee06e0c796c2955552b"),
-                            new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_512,
-                                       "415fad6271580a531d4179bc891d87a6"))),
-                    */
-
-                    /* TODO see why it fails
-                    new VectorTestData(repeat("aa", 131), false, "Test Using Larger Than Block-Size Key - Hash Key First",
-                        Arrays.asList(   // test case 6
-                            new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_256,
-                                       "60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54"),
-                            new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_512,
-                                       "80b24263c7c1a3ebb71493c1dd7be8b49b46d1f41b4aeec1121b013783f8f3526b56d037e05f2598bd0fd2215d6a1e5295e64f73f63f0aec8b915a985d786598"))),
-                    */
-
-                    /* TODO see why it fails
-                    new VectorTestData(repeat("aa", 131), false, "This is a test using a larger than block-size"
-                                                               + " key and a larger than block-size data."
-                                                               + " The key needs to be hashed before being used"
-                                                               + " by the HMAC algorithm",
-                        Arrays.asList(   // test case 7
-                            new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_256,
-                                       "9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2"),
-                            new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_512,
-                                       "e37b6a775dc87dbaa4dfa9f96e5e3ffddebd71f8867289865df5a32d20cdc944b6022cac3c4982b10d5eeb55c3e4de15134676fb6de0446065c97440fa8c6a58")))
-                    */
-
-                    // mark end
-                    new VectorTestData("", false, "", false, Collections.emptyList())))) {
-            for (Map.Entry<String, String> tc : vector.getResults()) {
-                ret.add(new Object[]{vector, tc.getKey(), tc.getValue()});
-            }
-        }
-
-        return ret;
-    }
-
-    @Test
-    public void testStandardVectorMac() throws Exception {
-        Mac mac = macFactory.create();
-        mac.init(seed.getKey());
-        mac.update(seed.getData());
-
-        byte[] actual = new byte[mac.getBlockSize()];
-        mac.doFinal(actual);
-        assertArrayEquals("Mismatched results for actual=" + BufferUtils.toHex(BufferUtils.EMPTY_HEX_SEPARATOR, actual), expected, actual);
-    }
-
-    private static class VectorSeed {
-        private final byte[] key;
-        private final String keyString;
-        private final byte[] data;
-        private final String dataString;
-
-        VectorSeed(String key, String data) {
-            this.key = key.getBytes(StandardCharsets.UTF_8);
-            this.keyString = key;
-            this.data = data.getBytes(StandardCharsets.UTF_8);
-            this.dataString = data;
-        }
-
-        VectorSeed(String key, boolean useKeyString, String data) {
-            this.key = BufferUtils.decodeHex(BufferUtils.EMPTY_HEX_SEPARATOR, key);
-            this.keyString = useKeyString ? new String(this.key, StandardCharsets.UTF_8) : key;
-            this.data = data.getBytes(StandardCharsets.UTF_8);
-            this.dataString = data;
-        }
-
-        VectorSeed(String key, boolean useKeyString, String data, boolean useDataString) {
-            this.key = BufferUtils.decodeHex(BufferUtils.EMPTY_HEX_SEPARATOR, key);
-            this.keyString = useKeyString ? new String(this.key, StandardCharsets.UTF_8) : key;
-            this.data = BufferUtils.decodeHex(BufferUtils.EMPTY_HEX_SEPARATOR, data);
-            this.dataString = useDataString ? new String(this.data, StandardCharsets.UTF_8) : data;
-        }
-
-        public byte[] getKey() {
-            return key.clone(); // clone to avoid inadvertent change
-        }
-
-        public String getKeyString() {
-            return keyString;
-        }
-
-        public byte[] getData() {
-            return data.clone();  // clone to avoid inadvertent change
-        }
-
-        public String getDataString() {
-            return dataString;
-        }
-
-        @Override
-        public String toString() {
-            return "key=" + trimToLength(getKeyString(), 32) + ", data=" + trimToLength(getDataString(), 32);
-        }
-
-        private static CharSequence trimToLength(CharSequence csq, int maxLen) {
-            if (GenericUtils.length(csq) <= maxLen) {
-                return csq;
-            }
-
-            return csq.subSequence(0, maxLen) + "...";
-        }
-    }
-
-    private static class VectorTestData extends VectorSeed {
-        private final Collection<Map.Entry<String, String>> results;
-
-        VectorTestData(String key, String data, Collection<Map.Entry<String, String>> results) {
-            super(key, data);
-            this.results = results;
-        }
-
-        VectorTestData(String key, boolean useKeyString, String data, Collection<Map.Entry<String, String>> results) {
-            super(key, useKeyString, data);
-            this.results = results;
-        }
-
-        VectorTestData(String key, boolean useKeyString, String data, boolean useDataString, Collection<Map.Entry<String, String>> results) {
-            super(key, useKeyString, data, useDataString);
-            this.results = results;
-        }
-
-        public Collection<Map.Entry<String, String>> getResults() {
-            return results;
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/random/RandomFactoryTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/random/RandomFactoryTest.java b/sshd-core/src/test/java/org/apache/sshd/common/random/RandomFactoryTest.java
deleted file mode 100644
index f3f1634..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/random/RandomFactoryTest.java
+++ /dev/null
@@ -1,83 +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.random;
-
-import java.util.Collection;
-import java.util.LinkedList;
-import java.util.concurrent.TimeUnit;
-
-import org.apache.sshd.common.util.security.SecurityUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.Assume;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runner.RunWith;
-import org.junit.runners.MethodSorters;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-import org.junit.runners.Parameterized.UseParametersRunnerFactory;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@RunWith(Parameterized.class)   // see https://github.com/junit-team/junit/wiki/Parameterized-tests
-@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
-@Category({ NoIoTestCase.class })
-public class RandomFactoryTest extends BaseTestSupport {
-    private final RandomFactory factory;
-
-    public RandomFactoryTest(RandomFactory factory) {
-        this.factory = factory;
-    }
-
-    @Parameters(name = "type={0}")
-    public static Collection<Object[]> parameters() {
-        Collection<RandomFactory> testCases = new LinkedList<>();
-        testCases.add(JceRandomFactory.INSTANCE);
-        if (SecurityUtils.isBouncyCastleRegistered()) {
-            testCases.add(SecurityUtils.getRandomFactory());
-        } else {
-            System.out.println("Skip BouncyCastleRandomFactory - unsupported");
-        }
-
-        return parameterize(testCases);
-    }
-
-    @Test
-    public void testRandomFactory() {
-        Assume.assumeTrue("Skip unsupported factory: " + factory.getName(), factory.isSupported());
-        long t = testRandom(factory.create());
-        System.out.println(factory.getName() + " duration: " + t + " " + TimeUnit.MICROSECONDS);
-    }
-
-    // returns duration in microseconds
-    private static long testRandom(Random random) {
-        byte[] bytes = new byte[32];
-        long l0 = System.nanoTime();
-        for (int i = 0; i < 1000; i++) {
-            random.fill(bytes, 8, 16);
-        }
-        long l1 = System.nanoTime();
-        return TimeUnit.NANOSECONDS.toMicros(l1 - l0);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/session/helpers/AbstractSessionTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/session/helpers/AbstractSessionTest.java b/sshd-core/src/test/java/org/apache/sshd/common/session/helpers/AbstractSessionTest.java
index 83a183e..4c56d14 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/session/helpers/AbstractSessionTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/session/helpers/AbstractSessionTest.java
@@ -406,7 +406,7 @@ public class AbstractSessionTest extends BaseTestSupport {
 
     public static class MySession extends AbstractSession {
         public MySession() {
-            super(true, org.apache.sshd.util.test.Utils.setupTestServer(AbstractSessionTest.class), new MyIoSession());
+            super(true, org.apache.sshd.util.test.CoreTestSupportUtils.setupTestServer(AbstractSessionTest.class), new MyIoSession());
         }
 
         @Override

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/signature/BuiltinSignaturesTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/signature/BuiltinSignaturesTest.java b/sshd-core/src/test/java/org/apache/sshd/common/signature/BuiltinSignaturesTest.java
deleted file mode 100644
index 458abfc..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/signature/BuiltinSignaturesTest.java
+++ /dev/null
@@ -1,149 +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.signature;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Random;
-
-import org.apache.sshd.common.NamedResource;
-import org.apache.sshd.common.signature.BuiltinSignatures.ParseResult;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-import org.mockito.Mockito;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class BuiltinSignaturesTest extends BaseTestSupport {
-    public BuiltinSignaturesTest() {
-        super();
-    }
-
-    @Test
-    public void testFromName() {
-        for (BuiltinSignatures expected : BuiltinSignatures.VALUES) {
-            String name = expected.getName();
-            BuiltinSignatures actual = BuiltinSignatures.fromFactoryName(name);
-            assertSame(name, expected, actual);
-        }
-    }
-
-    @Test
-    public void testParseSignaturesList() {
-        List<String> builtin = NamedResource.getNameList(BuiltinSignatures.VALUES);
-        List<String> unknown = Arrays.asList(getClass().getPackage().getName(), getClass().getSimpleName(), getCurrentTestName());
-        Random rnd = new Random();
-        for (int index = 0; index < (builtin.size() + unknown.size()); index++) {
-            Collections.shuffle(builtin, rnd);
-            Collections.shuffle(unknown, rnd);
-
-            List<String> weavedList = new ArrayList<>(builtin.size() + unknown.size());
-            for (int bIndex = 0, uIndex = 0; (bIndex < builtin.size()) || (uIndex < unknown.size());) {
-                boolean useBuiltin = false;
-                if (bIndex < builtin.size()) {
-                    useBuiltin = uIndex >= unknown.size() || rnd.nextBoolean();
-                }
-
-                if (useBuiltin) {
-                    weavedList.add(builtin.get(bIndex));
-                    bIndex++;
-                } else if (uIndex < unknown.size()) {
-                    weavedList.add(unknown.get(uIndex));
-                    uIndex++;
-                }
-            }
-
-            String fullList = GenericUtils.join(weavedList, ',');
-            ParseResult result = BuiltinSignatures.parseSignatureList(fullList);
-            List<String> parsed = NamedResource.getNameList(result.getParsedFactories());
-            List<String> missing = result.getUnsupportedFactories();
-
-            // makes sure not only that the contents are the same but also the order
-            assertListEquals(fullList + "[parsed]", builtin, parsed);
-            assertListEquals(fullList + "[unsupported]", unknown, missing);
-        }
-    }
-
-    @Test
-    public void testResolveFactoryOnBuiltinValues() {
-        for (SignatureFactory expected : BuiltinSignatures.VALUES) {
-            String name = expected.getName();
-            SignatureFactory actual = BuiltinSignatures.resolveFactory(name);
-            assertSame(name, expected, actual);
-        }
-    }
-
-    @Test
-    public void testNotAllowedToRegisterBuiltinFactories() {
-        for (SignatureFactory expected : BuiltinSignatures.VALUES) {
-            try {
-                BuiltinSignatures.registerExtension(expected);
-                fail("Unexpected success for " + expected.getName());
-            } catch (IllegalArgumentException e) {
-                // expected - ignored
-            }
-        }
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void testNotAllowedToOverrideRegisteredFactories() {
-        SignatureFactory expected = Mockito.mock(SignatureFactory.class);
-        Mockito.when(expected.getName()).thenReturn(getCurrentTestName());
-
-        String name = expected.getName();
-        try {
-            for (int index = 1; index <= Byte.SIZE; index++) {
-                BuiltinSignatures.registerExtension(expected);
-                assertEquals("Unexpected success at attempt #" + index, 1, index);
-            }
-        } finally {
-            BuiltinSignatures.unregisterExtension(name);
-        }
-    }
-
-    @Test
-    public void testResolveFactoryOnRegisteredExtension() {
-        SignatureFactory expected = Mockito.mock(SignatureFactory.class);
-        Mockito.when(expected.getName()).thenReturn(getCurrentTestName());
-
-        String name = expected.getName();
-        try {
-            assertNull("Extension already registered", BuiltinSignatures.resolveFactory(name));
-            BuiltinSignatures.registerExtension(expected);
-
-            SignatureFactory actual = BuiltinSignatures.resolveFactory(name);
-            assertSame("Mismatched resolved instance", expected, actual);
-        } finally {
-            SignatureFactory actual = BuiltinSignatures.unregisterExtension(name);
-            assertSame("Mismatched unregistered instance", expected, actual);
-            assertNull("Extension not un-registered", BuiltinSignatures.resolveFactory(name));
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/signature/SignatureDSATest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/signature/SignatureDSATest.java b/sshd-core/src/test/java/org/apache/sshd/common/signature/SignatureDSATest.java
deleted file mode 100644
index 0998bbd..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/signature/SignatureDSATest.java
+++ /dev/null
@@ -1,109 +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.signature;
-
-import java.math.BigInteger;
-import java.security.GeneralSecurityException;
-import java.security.KeyFactory;
-import java.security.spec.DSAPublicKeySpec;
-
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class SignatureDSATest extends BaseTestSupport {
-    public SignatureDSATest() {
-        super();
-    }
-
-    @Test
-    public void testTooShortSignature() throws Exception {
-        KeyFactory kf = SecurityUtils.getKeyFactory(KeyUtils.DSS_ALGORITHM);
-        SignatureDSA signatureDSA = new SignatureDSA(KeyUtils.DSS_ALGORITHM) {
-            @Override
-            protected java.security.Signature doInitSignature(String algo, boolean forSigning) throws GeneralSecurityException {
-                return java.security.Signature.getInstance(algo);
-
-            }
-        };
-
-        byte[] y = new byte[] {
-            0, -92, 59, 5, 72, 124, 101, 124, -18, 114, 7, 100, 98, -61, 73, -104,
-            120, -98, 54, 118, 17, -62, 91, -110, 29, 98, 50, -101, -41, 99, -116,
-            101, 107, -123, 124, -97, 62, 119, 88, -109, -110, -1, 109, 119, -51,
-            69, -98, -105, 2, -69, -121, -82, -118, 23, -6, 96, -61, -65, 102, -58,
-            -74, 32, -104, 116, -6, -35, -83, -10, -88, -68, 106, -112, 72, -2, 35,
-            38, 15, -11, -22, 30, -114, -46, -47, -18, -17, -71, 24, -25, 28, 13, 29,
-            -40, 101, 18, 81, 45, -120, -67, -53, -41, 11, 50, -89, -33, 50, 54, -14,
-            -91, -35, 12, -42, 13, -84, -19, 100, -3, -85, -18, 74, 99, -49, 64, -49,
-            51, -83, -82, -127, 116, 64 };
-        byte[] p = new byte[] {
-            0, -3, 127, 83, -127, 29, 117, 18, 41, 82, -33, 74, -100, 46, -20, -28,
-            -25, -10, 17, -73, 82, 60, -17, 68, 0, -61, 30, 63, -128, -74, 81, 38,
-            105, 69, 93, 64, 34, 81, -5, 89, 61, -115, 88, -6, -65, -59, -11, -70,
-            48, -10, -53, -101, 85, 108, -41, -127, 59, -128, 29, 52, 111, -14, 102,
-            96, -73, 107, -103, 80, -91, -92, -97, -97, -24, 4, 123, 16, 34, -62, 79,
-            -69, -87, -41, -2, -73, -58, 27, -8, 59, 87, -25, -58, -88, -90, 21, 15, 4,
-            -5, -125, -10, -45, -59, 30, -61, 2, 53, 84, 19, 90, 22, -111, 50, -10, 117,
-            -13, -82, 43, 97, -41, 42, -17, -14, 34, 3, 25, -99, -47, 72, 1, -57 };
-        byte[] q = new byte[] {
-            0, -105, 96, 80, -113, 21, 35, 11, -52, -78, -110, -71, -126, -94, -21,
-            -124, 11, -16, 88, 28, -11 };
-        byte[] g = new byte[] {
-            0, -9, -31, -96, -123, -42, -101, 61, -34, -53, -68, -85, 92, 54, -72, 87,
-            -71, 121, -108, -81, -69, -6, 58, -22, -126, -7, 87, 76, 11, 61, 7, -126,
-            103, 81, 89, 87, -114, -70, -44, 89, 79, -26, 113, 7, 16, -127, -128, -76,
-            73, 22, 113, 35, -24, 76, 40, 22, 19, -73, -49, 9, 50, -116, -56, -90, -31,
-            60, 22, 122, -117, 84, 124, -115, 40, -32, -93, -82, 30, 43, -77, -90, 117,
-            -111, 110, -93, 127, 11, -6, 33, 53, 98, -15, -5, 98, 122, 1, 36, 59, -52,
-            -92, -15, -66, -88, 81, -112, -119, -88, -125, -33, -31, 90, -27, -97, 6,
-            -110, -117, 102, 94, -128, 123, 85, 37, 100, 1, 76, 59, -2, -49, 73, 42 };
-
-        BigInteger bigY = new BigInteger(y);
-        BigInteger bigP = new BigInteger(p);
-        BigInteger bigQ = new BigInteger(q);
-        BigInteger bigG = new BigInteger(g);
-
-        DSAPublicKeySpec dsaPublicKey = new DSAPublicKeySpec(bigY, bigP, bigQ, bigG);
-        signatureDSA.initVerifier(kf.generatePublic(dsaPublicKey));
-        byte[] h = new byte[] {
-            -4, 111, -103, 111, 72, -106, 105, -19, 81, -123, 84, -13, -40, -53, -3,
-            -97, -8, 43, -22, -2, -23, -15, 28, 116, -63, 96, -79, -127, -84, 63, -6, -94 };
-        signatureDSA.update(h);
-
-        byte[] sig_of_h = new byte[] {
-            0, 0, 0, 7, 115, 115, 104, 45, 100, 115, 115, 0, 0, 0, 40, 0, 79,
-            84, 118, -50, 11, -117, -112, 52, -25, -78, -50, -20, 6, -69, -26,
-            7, 90, -34, -124, 80, 76, -32, -23, -8, 43, 38, -48, -89, -17, -60,
-            -1, -78, 112, -88, 14, -39, -78, -98, -80 };
-        boolean verified = signatureDSA.verify(sig_of_h);
-
-        assertTrue(verified);
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/signature/SignatureFactoriesTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/signature/SignatureFactoriesTest.java b/sshd-core/src/test/java/org/apache/sshd/common/signature/SignatureFactoriesTest.java
index 8b2943b..0733967 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/signature/SignatureFactoriesTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/signature/SignatureFactoriesTest.java
@@ -43,8 +43,8 @@ import org.apache.sshd.common.util.ValidateUtils;
 import org.apache.sshd.common.util.security.SecurityUtils;
 import org.apache.sshd.server.SshServer;
 import org.apache.sshd.util.test.BaseTestSupport;
+import org.apache.sshd.util.test.CoreTestSupportUtils;
 import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
-import org.apache.sshd.util.test.Utils;
 import org.junit.AfterClass;
 import org.junit.Assume;
 import org.junit.BeforeClass;
@@ -122,11 +122,11 @@ public class SignatureFactoriesTest extends BaseTestSupport implements OptionalF
 
     @BeforeClass
     public static void setupClientAndServer() throws Exception {
-        sshd = Utils.setupTestServer(SignatureFactoriesTest.class);
+        sshd = CoreTestSupportUtils.setupTestServer(SignatureFactoriesTest.class);
         sshd.start();
         port = sshd.getPort();
 
-        client = Utils.setupTestClient(SignatureFactoriesTest.class);
+        client = CoreTestSupportUtils.setupTestClient(SignatureFactoriesTest.class);
         client.start();
     }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/signature/SignatureRSATest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/signature/SignatureRSATest.java b/sshd-core/src/test/java/org/apache/sshd/common/signature/SignatureRSATest.java
deleted file mode 100644
index 6f0cb6e..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/signature/SignatureRSATest.java
+++ /dev/null
@@ -1,119 +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.signature;
-
-import java.math.BigInteger;
-import java.security.GeneralSecurityException;
-import java.security.KeyFactory;
-import java.security.Provider;
-import java.security.PublicKey;
-import java.security.spec.RSAPublicKeySpec;
-import java.util.Base64;
-import java.util.Map;
-
-import org.apache.sshd.common.Factory;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.junit.BeforeClass;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-public class SignatureRSATest extends BaseTestSupport {
-    private static final Base64.Decoder B64_DECODER = Base64.getDecoder();
-    @SuppressWarnings("checkstyle:linelength")
-    private static final byte[] TEST_MSG =
-            B64_DECODER.decode("AAAAFPHgK1MeV9zNnok3pwNJhCd8SONqMgAAAAlidWlsZHVzZXIAAAAOc3NoLWNvbm5lY3Rpb24AAAAJcHVibGlja2V5AQAAAAdzc2gtcnNhAAABFQAAAAdzc2gtcnNhAAAAASMAAAEBAMs9HO/NH/Now+6fSnESebaG4wzaYQWA1b/q1TGV1wHNtCg9fGFGVSKs0VxKF4cfVyrSLtgLjnlXQTn+Lm7xiYKGbBbsTQWOqEDaBVBsRbAkxIkpuvr6/EBxwrtDbKmSQYTJZVJSD2bZRYjGsR9gpZXPorOOKFd5EPCMHXsqnhp2hidTGH7cK6RuLk7MNnPISsY0Nbx8/ZvikiPROGcoTZ8bzUv4IaLr3veW6epSeQem8tJqhnrpTHhbLU99zf045M0Gsnk/azjjlBM+qrHZ5FNdC1kowJnLtf2Oy/rUQNpkGJtcBPT8xvreV0wLsn9t3hSxzsc0+VkDNTQRlfU+o3M=");
-    @SuppressWarnings("checkstyle:linelength")
-    private static final byte[] TEST_SIGNATURE =
-            B64_DECODER.decode("AAAAB3NzaC1yc2EAAAD/+Ntnf4qfr2J1voDS6I+u3VRjtMn+LdWJsAZfkLDxRkK1rQxP7QAjLdNqpT4CkWHp8dtoTGFlBFt6NieNJCMTA2KSOxJMZKsX7e/lHkh7C+vhQvJ9eLTKWjCxSFUrcM0NvFhmwbRCffwXSHvAKak4wbmofxQMpd+G4jZkNMz5kGpmeICBcNjRLPb7oXzuGr/g4x/3ge5Qaawqrg/gcZr/sKN6SdE8SszgKYO0SB320N4gcUoShVdLYr9uwdJ+kJoobfkUK6Or171JCctP/cu2nM79lDqVnJw/2jOG8OnTc8zRDXAh0RKoR5rOU8cOHm0Ls2MATsFdnyRU5FGUxqZ+");
-    private static PublicKey testKey;
-
-    public SignatureRSATest() {
-        super();
-    }
-
-    @BeforeClass
-    public static void initializeTestKey() throws GeneralSecurityException {
-        byte[] exp = B64_DECODER.decode("Iw==");
-        @SuppressWarnings("checkstyle:linelength")
-        byte[] mod = B64_DECODER.decode("AMs9HO/NH/Now+6fSnESebaG4wzaYQWA1b/q1TGV1wHNtCg9fGFGVSKs0VxKF4cfVyrSLtgLjnlXQTn+Lm7xiYKGbBbsTQWOqEDaBVBsRbAkxIkpuvr6/EBxwrtDbKmSQYTJZVJSD2bZRYjGsR9gpZXPorOOKFd5EPCMHXsqnhp2hidTGH7cK6RuLk7MNnPISsY0Nbx8/ZvikiPROGcoTZ8bzUv4IaLr3veW6epSeQem8tJqhnrpTHhbLU99zf045M0Gsnk/azjjlBM+qrHZ5FNdC1kowJnLtf2Oy/rUQNpkGJtcBPT8xvreV0wLsn9t3hSxzsc0+VkDNTQRlfU+o3M=");
-        KeyFactory kf = SecurityUtils.getKeyFactory(KeyUtils.RSA_ALGORITHM);
-        testKey = kf.generatePublic(new RSAPublicKeySpec(new BigInteger(mod), new BigInteger(exp)));
-    }
-
-    @Test   // see SSHD-642
-    public void testLeadingZeroesBC() throws Throwable {
-        testLeadingZeroes(new Factory<SignatureRSA>() {
-            @Override
-            public SignatureRSA create() {
-                return new SignatureRSA() {
-                    @Override
-                    protected java.security.Signature doInitSignature(String algo, boolean forSigning) throws GeneralSecurityException {
-                        assertFalse("Signature not initialized for verification", forSigning);
-                        java.security.Signature signature = super.doInitSignature(algo, forSigning);
-                        if (SecurityUtils.isBouncyCastleRegistered()) {
-                            Provider provider = signature.getProvider();
-                            String name = provider.getName();
-                            assertEquals("Mismatched BC provider name", SecurityUtils.BOUNCY_CASTLE, name);
-                        }
-                        return signature;
-                    }
-                };
-            }
-        });
-    }
-
-    @Test   // see SSHD-642
-    public void testLeadingZeroesJCE() throws Throwable {
-        testLeadingZeroes(() -> new SignatureRSA() {
-            @Override
-            protected java.security.Signature doInitSignature(String algo, boolean forSigning) throws GeneralSecurityException {
-                assertFalse("Signature not initialized for verification", forSigning);
-                java.security.Signature signature = java.security.Signature.getInstance(algo);
-                Provider provider = signature.getProvider();
-                String name = provider.getName();
-                assertNotEquals("BC provider used although not required", SecurityUtils.BOUNCY_CASTLE, name);
-                return signature;
-            }
-        });
-    }
-
-    private void testLeadingZeroes(Factory<? extends SignatureRSA> factory) throws Exception {
-        SignatureRSA rsa = factory.create();
-        rsa.initVerifier(testKey);
-
-        int vSize = rsa.getVerifierSignatureSize();
-        assertTrue("Verifier signature size not initialized", vSize > 0);
-
-        // make sure padding is required
-        Map.Entry<String, byte[]> encoding = rsa.extractEncodedSignature(TEST_SIGNATURE);
-        assertNotNull("Signature is not encoded", encoding);
-        byte[] data = encoding.getValue();
-        assertTrue("Signature data size (" + data.length + ") not below verifier size (" + vSize + ")", data.length < vSize);
-
-        rsa.update(TEST_MSG);
-        assertTrue("Failed to verify", rsa.verify(TEST_SIGNATURE));
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/signature/SignaturesDevelopment.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/signature/SignaturesDevelopment.java b/sshd-core/src/test/java/org/apache/sshd/common/signature/SignaturesDevelopment.java
deleted file mode 100644
index 180e997..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/signature/SignaturesDevelopment.java
+++ /dev/null
@@ -1,79 +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.signature;
-
-import java.security.KeyPair;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.buffer.BufferUtils;
-import org.apache.sshd.common.util.security.eddsa.EdDSASecurityProviderUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-
-/**
- * A &quot;scratch-pad&quot; class for testing signatures related code during development
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class SignaturesDevelopment extends BaseTestSupport {
-    public SignaturesDevelopment() {
-        super();
-    }
-
-    public static void testSignatureFactory(
-            SignatureFactory factory, KeyPair kp, byte[] data, boolean generateSignature, byte[] signature)
-                throws Exception {
-        Signature signer = factory.create();
-        if (generateSignature) {
-            signer.initSigner(kp.getPrivate());
-            signer.update(data);
-            signature = signer.sign();
-            System.out.append('\t').append("Signature: ").println(BufferUtils.toHex(':', signature));
-        } else {
-            signer.initVerifier(kp.getPublic());
-            signer.update(data);
-            if (signer.verify(signature)) {
-                System.out.append('\t').println("Valid signature");
-            } else {
-                System.err.append('\t').println("Invalid signature");
-            }
-        }
-    }
-
-    //////////////////////////////////////////////////////////////////////////
-
-    // args[0]=signatureName, args[1]=publicKey, args[2]=privateKey, args[3]=sign/verify, args[4]=data, args[5]=signature(if verify required)
-    public static void main(String[] args) throws Exception {
-        SignatureFactory factory = BuiltinSignatures.resolveFactory(args[0]);
-        // TODO recover public/private keys according to factory name
-        byte[] publicKey = BufferUtils.decodeHex(':', args[1]);
-        PublicKey pubKey = EdDSASecurityProviderUtils.generateEDDSAPublicKey(publicKey);
-        byte[] privateKey = BufferUtils.decodeHex(':', args[2]);
-        PrivateKey prvKey = EdDSASecurityProviderUtils.generateEDDSAPrivateKey(privateKey);
-        String op = args[3];
-        byte[] data = BufferUtils.decodeHex(':', args[4]);
-        byte[] signature = GenericUtils.EMPTY_BYTE_ARRAY;
-        if ("verify".equalsIgnoreCase(op)) {
-            signature = BufferUtils.decodeHex(':', args[5]);
-        }
-
-        testSignatureFactory(factory, new KeyPair(pubKey, prvKey), data, "sign".equalsIgnoreCase(op), signature);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/EventListenerUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/EventListenerUtilsTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/EventListenerUtilsTest.java
deleted file mode 100644
index 3732f8a..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/EventListenerUtilsTest.java
+++ /dev/null
@@ -1,149 +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.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.EventListener;
-import java.util.List;
-import java.util.Set;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class EventListenerUtilsTest extends BaseTestSupport {
-    public EventListenerUtilsTest() {
-        super();
-    }
-
-    @Test
-    public void testProxyWrapper() {
-        List<ProxyListenerImpl> impls = new ArrayList<>();
-        for (int index = 0; index < Byte.SIZE; index++) {
-            impls.add(new ProxyListenerImpl());
-        }
-
-        ProxyListener listener = EventListenerUtils.proxyWrapper(ProxyListener.class, impls);
-        String expStr = getCurrentTestName();
-        Number expNum = System.currentTimeMillis();
-        listener.callMeWithString(expStr);
-        listener.callMeWithNumber(expNum);
-
-        for (int index = 0; index < impls.size(); index++) {
-            ProxyListenerImpl l = impls.get(index);
-            assertSame("Mismatched string at listener #" + index, expStr, l.getStringValue());
-            assertSame("Mismatched number at listener #" + index, expNum, l.getNumberValue());
-        }
-    }
-
-    @Test
-    public void testListenerInstanceComparatorOnProxy() {
-        Comparator<? super EventListener> comparator = EventListenerUtils.LISTENER_INSTANCE_COMPARATOR;
-        ProxyListener p1 = EventListenerUtils.proxyWrapper(ProxyListener.class, Collections.singletonList(new ProxyListenerImpl()));
-        assertEquals("Mismatched self reference comparison", 0, comparator.compare(p1, p1));
-
-        EventListener l = new EventListener() { /* nothing extra */ };
-        assertEquals("Mismatched proxy vs. non-proxy result", 1, Integer.signum(comparator.compare(p1, l)));
-        assertEquals("Mismatched non-proxy vs. proxy result", -1, Integer.signum(comparator.compare(l, p1)));
-
-        ProxyListener p2 = EventListenerUtils.proxyWrapper(ProxyListener.class, Collections.singletonList(new ProxyListenerImpl()));
-        int p1vsp2 = Integer.signum(comparator.compare(p1, p2));
-        assertNotEquals("Mismatched p1 vs. p2 comparison", 0, p1vsp2);
-        assertEquals("Mismatched p2 vs. p1 comparison result", 0 - p1vsp2, Integer.signum(comparator.compare(p2, p1)));
-    }
-
-    @Test
-    public void testListenerInstanceComparatorOnNonProxy() {
-        Comparator<? super EventListener> comparator = EventListenerUtils.LISTENER_INSTANCE_COMPARATOR;
-        EventListener l1 = new EventListener() { /* nothing extra */ };
-        assertEquals("Mismatched self reference comparison", 0, comparator.compare(l1, l1));
-
-        EventListener l2 = new EventListener() { /* nothing extra */ };
-        int l1vsl2 = Integer.signum(comparator.compare(l1, l2));
-        assertNotEquals("Mismatched l1 vs. l2 comparison result", 0, l1vsl2);
-        assertEquals("Mismatched l2 vs. l1 comparison result", 0 - l1vsl2, Integer.signum(comparator.compare(l2, l1)));
-    }
-
-    @Test
-    public void testSynchronizedListenersSetOnProxies() {
-        ProxyListener p1 = EventListenerUtils.proxyWrapper(ProxyListener.class, Collections.singletonList(new ProxyListenerImpl()));
-        Set<ProxyListener> s = EventListenerUtils.synchronizedListenersSet();
-        for (int index = 1; index <= Byte.SIZE; index++) {
-            boolean modified = s.add(p1);
-            assertEquals("Mismatched p1 modification indicator at attempt #" + index, index == 1, modified);
-            assertEquals("Mismatched p1 set size at attempt #" + index, 1, s.size());
-        }
-
-        ProxyListener p2 = EventListenerUtils.proxyWrapper(ProxyListener.class, Collections.singletonList(new ProxyListenerImpl()));
-        for (int index = 1; index <= Byte.SIZE; index++) {
-            boolean modified = s.add(p2);
-            assertEquals("Mismatched p2 modification indicator at attempt #" + index, index == 1, modified);
-            assertEquals("Mismatched p2 set size at attempt #" + index, 2, s.size());
-        }
-
-        assertTrue("Failed to remove p1", s.remove(p1));
-        assertEquals("Mismatched post p1-remove size", 1, s.size());
-        assertTrue("Failed to remove p2", s.remove(p2));
-        assertEquals("Mismatched post p2-remove size", 0, s.size());
-    }
-
-    interface ProxyListener extends SshdEventListener {
-        void callMeWithString(String s);
-
-        void callMeWithNumber(Number n);
-    }
-
-    static class ProxyListenerImpl implements ProxyListener {
-        private String strValue;
-        private Number numValue;
-
-        ProxyListenerImpl() {
-            super();
-        }
-
-        public String getStringValue() {
-            return strValue;
-        }
-
-        @Override
-        public void callMeWithString(String s) {
-            strValue = s;
-        }
-
-        public Number getNumberValue() {
-            return numValue;
-        }
-
-        @Override
-        public void callMeWithNumber(Number n) {
-            numValue = n;
-        }
-    }
-}


[48/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common utilities code from sshd-core into sshd-common (new artifact)

Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/PropertyResolverUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/PropertyResolverUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/PropertyResolverUtils.java
new file mode 100644
index 0000000..6677685
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/PropertyResolverUtils.java
@@ -0,0 +1,482 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common;
+
+import java.nio.charset.Charset;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+import java.util.Properties;
+import java.util.TreeMap;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class PropertyResolverUtils {
+    private PropertyResolverUtils() {
+        throw new UnsupportedOperationException("No instance allowed");
+    }
+
+    /**
+     * @param resolver     The {@link PropertyResolver} instance - ignored if {@code null}
+     * @param name         The property name
+     * @param defaultValue The default value to return if the specified property
+     *                     does not exist in the properties map
+     * @return The resolved property
+     * @throws NumberFormatException if malformed value
+     * @see #toLong(Object, long)
+     */
+    public static long getLongProperty(PropertyResolver resolver, String name, long defaultValue) {
+        return toLong(resolvePropertyValue(resolver, name), defaultValue);
+    }
+
+    public static long getLongProperty(Map<String, ?> props, String name, long defaultValue) {
+        return toLong(resolvePropertyValue(props, name), defaultValue);
+    }
+
+    /**
+     * Converts a generic object value to a {@code long} if possible:
+     * <UL>
+     *      <LI>
+     *      If value is {@code null} the default is returned
+     *      </LI>
+     *
+     *      <LI>
+     *      If value is a {@link Number} then its {@link Number#longValue()} is returned
+     *      </LI>
+     *
+     *      <LI>
+     *      Otherwise, the value's {@link #toString()} is parsed as a {@code long}
+     *      </LI>
+     * </UL>
+     *
+     * @param value         The resolved value - may be {@code null}
+     * @param defaultValue  The default to use if {@code null} resolved value
+     * @return The resolved value
+     * @throws NumberFormatException if malformed value
+     * @see Long#parseLong(String)
+     */
+    public static long toLong(Object value, long defaultValue) {
+        if (value == null) {
+            return defaultValue;
+        } else if (value instanceof Number) {
+            return ((Number) value).longValue();
+        } else {    // we parse the string in case it is not a valid long value
+            return Long.parseLong(value.toString());
+        }
+    }
+
+    /**
+     * @param resolver The {@link PropertyResolver} instance - ignored if {@code null}
+     * @param name     The property name
+     * @return The {@link Long} value or {@code null} if property not found
+     * @throws NumberFormatException if malformed value
+     * @see #toLong(Object)
+     */
+    public static Long getLong(PropertyResolver resolver, String name) {
+        return toLong(resolvePropertyValue(resolver, name));
+    }
+
+    public static Long getLong(Map<String, ?> props, String name) {
+        return toLong(resolvePropertyValue(props, name));
+    }
+
+    /**
+     * Converts a generic object into a {@link Long}:
+     * <UL>
+     *      <LI>
+     *      If the value is {@code null} then returns {@code null}.
+     *      </LI>
+     *
+     *      <LI>
+     *      If the value is already a {@link Long} then it is returned as such.
+     *      </LI>
+
+     *      <LI>
+     *      If value is a {@link Number} then its {@link Number#longValue()} is
+     *      wrapped as a {@link Long}
+     *      </LI>
+     *
+     *      <LI>
+     *      Otherwise, the value's {@link #toString()} is parsed as a {@link Long}
+     *      </LI>
+     * </UL>
+     *
+     * @param value The resolved value - may be {@code null}
+     * @return The {@link Long} value or {@code null} if property not found
+     * @throws NumberFormatException if malformed value
+     * @see Long#valueOf(long)
+     * @see Long#valueOf(String)
+     */
+    public static Long toLong(Object value) {
+        if (value == null) {
+            return null;
+        } else if (value instanceof Long) {
+            return (Long) value;
+        } else if (value instanceof Number) {
+            return ((Number) value).longValue();
+        } else {    // we parse the string in case it is not a valid long value
+            return Long.valueOf(value.toString());
+        }
+    }
+
+    /**
+     * Converts an enumerated configuration value:
+     * <UL>
+     *      <P><LI>
+     *      If value is {@code null} then return {@code null}
+     *      </LI></P>
+     *
+     *      <P><LI>
+     *      If value already of the expected type then simply
+     *      cast and return it.
+     *      </LI></P>
+     *
+     *      <P><LI>
+     *      If value is a {@link CharSequence} then convert it
+     *      to a string and look for a matching enumerated value
+     *      name - case <U>insensitive</U>.
+     *      </LI></P>>
+     * </UL>
+     *
+     * @param <E> Type of enumerated value
+     * @param enumType The enumerated class type
+     * @param value The configured value - ignored if {@code null}
+     * @param failIfNoMatch Whether to fail if no matching name found
+     * @param available The available values to compare the name
+     * @return The matching enumerated value - {@code null} if no match found
+     * @throws IllegalArgumentException If value is neither {@code null},
+     * nor the enumerated type nor a {@link CharSequence}
+     * @throws NoSuchElementException If no matching string name found and
+     * <tt>failIfNoMatch</tt> is {@code true}
+     */
+    public static <E extends Enum<E>> E toEnum(Class<E> enumType, Object value, boolean failIfNoMatch, Collection<E> available) {
+        if (value == null) {
+            return null;
+        } else if (enumType.isInstance(value)) {
+            return enumType.cast(value);
+        } else if (value instanceof CharSequence) {
+            String name = value.toString();
+            if (GenericUtils.size(available) > 0) {
+                for (E v : available) {
+                    if (name.equalsIgnoreCase(v.name())) {
+                        return v;
+                    }
+                }
+            }
+
+            if (failIfNoMatch) {
+                throw new NoSuchElementException("No match found for " + enumType.getSimpleName() + "[" + name + "]");
+            }
+
+            return null;
+        } else {
+            throw new IllegalArgumentException("Bad value type for enum conversion: " + value.getClass().getSimpleName());
+        }
+    }
+
+    public static Object updateProperty(PropertyResolver resolver, String name, long value) {
+        return updateProperty(resolver.getProperties(), name, value);
+    }
+
+    public static Object updateProperty(Map<String, Object> props, String name, long value) {
+        return updateProperty(props, name, Long.valueOf(value));
+    }
+
+    public static int getIntProperty(PropertyResolver resolver, String name, int defaultValue) {
+        return toInteger(resolvePropertyValue(resolver, name), defaultValue);
+    }
+
+    public static int getIntProperty(Map<String, ?> props, String name, int defaultValue) {
+        return toInteger(resolvePropertyValue(props, name), defaultValue);
+    }
+
+    public static int toInteger(Object value, int defaultValue) {
+        if (value == null) {
+            return defaultValue;
+        } else if (value instanceof Number) {
+            return ((Number) value).intValue();
+        } else {    // we parse the string in case this is NOT an integer
+            return Integer.parseInt(value.toString());
+        }
+    }
+
+    public static Integer getInteger(PropertyResolver resolver, String name) {
+        return toInteger(resolvePropertyValue(resolver, name));
+    }
+
+    public static Integer getInteger(Map<String, ?> props, String name) {
+        return toInteger(resolvePropertyValue(props, name));
+    }
+
+    public static Integer toInteger(Object value) {
+        if (value == null) {
+            return null;
+        } else if (value instanceof Integer) {
+            return (Integer) value;
+        } else if (value instanceof Number) {
+            return ((Number) value).intValue();
+        } else {    // we parse the string in case this is NOT an integer
+            return Integer.valueOf(value.toString());
+        }
+    }
+
+    public static Object updateProperty(PropertyResolver resolver, String name, int value) {
+        return updateProperty(resolver.getProperties(), name, value);
+    }
+
+    public static Object updateProperty(Map<String, Object> props, String name, int value) {
+        return updateProperty(props, name, Integer.valueOf(value));
+    }
+
+    public static boolean getBooleanProperty(PropertyResolver resolver, String name, boolean defaultValue) {
+        return toBoolean(getObject(resolver, name), defaultValue);
+    }
+
+    public static boolean getBooleanProperty(Map<String, ?> props, String name, boolean defaultValue) {
+        return toBoolean(getObject(props, name), defaultValue);
+    }
+
+    public static boolean toBoolean(Object value, boolean defaultValue) {
+        if (value == null) {
+            return defaultValue;
+        } else {
+            return toBoolean(value);
+        }
+    }
+
+    public static Boolean getBoolean(PropertyResolver resolver, String name) {
+        return toBoolean(resolvePropertyValue(resolver, name));
+    }
+
+    public static Boolean getBoolean(Map<String, ?> props, String name) {
+        return toBoolean(resolvePropertyValue(props, name));
+    }
+
+    public static Boolean toBoolean(Object value) {
+        if (value == null) {
+            return null;
+        } else if (value instanceof Boolean) {
+            return (Boolean) value;
+        } else {
+            return Boolean.valueOf(value.toString());
+        }
+    }
+
+    public static Object updateProperty(PropertyResolver resolver, String name, boolean value) {
+        return updateProperty(resolver.getProperties(), name, value);
+    }
+
+    public static Object updateProperty(Map<String, Object> props, String name, boolean value) {
+        return updateProperty(props, name, Boolean.valueOf(value));
+    }
+
+    /**
+     * @param resolver     The {@link PropertyResolver} to use - ignored if {@code null}
+     * @param name         The property name
+     * @param defaultValue The default value to return if property not set or empty
+     * @return The set value (if not {@code null}/empty) or default one
+     */
+    public static String getStringProperty(PropertyResolver resolver, String name, String defaultValue) {
+        String value = getString(resolver, name);
+        if (GenericUtils.isEmpty(value)) {
+            return defaultValue;
+        } else {
+            return value;
+        }
+    }
+
+    public static String getStringProperty(Map<String, ?> props, String name, String defaultValue) {
+        Object value = resolvePropertyValue(props, name);
+        if (value == null) {
+            return defaultValue;
+        } else {
+            return Objects.toString(value);
+        }
+    }
+
+    public static Charset getCharset(PropertyResolver resolver, String name, Charset defaultValue) {
+        Object value = getObject(resolver, name);
+        return (value == null) ? defaultValue : toCharset(value);
+    }
+
+    public static Charset getCharset(Map<String, ?> props, String name, Charset defaultValue) {
+        Object value = getObject(props, name);
+        return (value == null) ? defaultValue : toCharset(value);
+    }
+
+    public static Charset toCharset(Object value) {
+        if (value == null) {
+            return null;
+        } else if (value instanceof Charset) {
+            return (Charset) value;
+        } else if (value instanceof CharSequence) {
+            return Charset.forName(value.toString());
+        } else {
+            throw new IllegalArgumentException("Invalid charset conversion value: " + value);
+        }
+    }
+
+    public static String getString(PropertyResolver resolver, String name) {
+        Object value = getObject(resolver, name);
+        return Objects.toString(value, null);
+    }
+
+    public static String getString(Map<String, ?> props, String name) {
+        Object value = getObject(props, name);
+        return Objects.toString(value, null);
+    }
+
+    public static Object getObject(PropertyResolver resolver, String name) {
+        return resolvePropertyValue(resolver, name);
+    }
+
+    // for symmetrical reasons...
+    public static Object getObject(Map<String, ?> props, String name) {
+        return resolvePropertyValue(props, name);
+    }
+
+    public static Object resolvePropertyValue(Map<String, ?> props, String name) {
+        String key = ValidateUtils.checkNotNullAndNotEmpty(name, "No property name");
+        return props != null ? props.get(key) : null;
+    }
+
+    /**
+     * @param resolver The {@link PropertyResolver} instance
+     * @param name     The property name
+     * @param value    The new value - if {@code null} or an empty {@link CharSequence}
+     *                 the property is <U>removed</U>
+     * @return The previous value - {@code null} if none
+     */
+    public static Object updateProperty(PropertyResolver resolver, String name, Object value) {
+        return updateProperty(resolver.getProperties(), name, value);
+    }
+
+    public static Object updateProperty(Map<String, Object> props, String name, Object value) {
+        String key = ValidateUtils.checkNotNullAndNotEmpty(name, "No property name");
+        if ((value == null) || ((value instanceof CharSequence) && GenericUtils.isEmpty((CharSequence) value))) {
+            return props.remove(key);
+        } else {
+            return props.put(key, value);
+        }
+    }
+
+    /**
+     * Unwinds the resolvers hierarchy until found one with a non-{@code null} value
+     * for the requested property or reached top. If still no value found and the key
+     * starts with &quot;org.apache.sshd&quot; then the system properties are also
+     * consulted
+     *
+     * @param resolver The {@link PropertyResolver} to start from - ignored if {@code null}
+     * @param name     The requested property name
+     * @return The found value or {@code null}
+     */
+    public static Object resolvePropertyValue(PropertyResolver resolver, String name) {
+        String key = ValidateUtils.checkNotNullAndNotEmpty(name, "No property name");
+        for (PropertyResolver r = resolver; r != null; r = r.getParentPropertyResolver()) {
+            Map<String, ?> props = r.getProperties();
+            if (props != null) {
+                Object value = props.get(key);
+                if (value != null) {
+                    return value;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Unwinds the resolvers hierarchy until found one with a non-{@code null} value
+     * for the requested property or reached top.
+     *
+     * @param resolver The {@link PropertyResolver} to start from - ignored if {@code null}
+     * @param name     The requested property name
+     * @return The found properties {@link Map} or {@code null}
+     */
+    public static Map<String, Object> resolvePropertiesSource(PropertyResolver resolver, String name) {
+        String key = ValidateUtils.checkNotNullAndNotEmpty(name, "No property name");
+        for (PropertyResolver r = resolver; r != null; r = r.getParentPropertyResolver()) {
+            Map<String, Object> props = r.getProperties();
+            if (props != null) {
+                Object value = props.get(key);
+                if (value != null) {
+                    return props;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    public static PropertyResolver toPropertyResolver(Properties props) {
+        if (GenericUtils.isEmpty(props)) {
+            return PropertyResolver.EMPTY;
+        }
+
+        Map<String, Object> propsMap = new TreeMap<>(Comparator.naturalOrder());
+        Collection<String> names = props.stringPropertyNames();
+        for (String key : names) {
+            String value = props.getProperty(key);
+            if (value == null) {
+                continue;
+            }
+            propsMap.put(key, value);
+        }
+
+        return toPropertyResolver(propsMap);
+    }
+
+    /**
+     * Wraps a {@link Map} into a {@link PropertyResolver} so it can be used
+     * with these utilities
+     *
+     * @param props The properties map - may be {@code null}/empty if no properties
+     * are updated
+     * @return The resolver wrapper
+     */
+    public static PropertyResolver toPropertyResolver(Map<String, Object> props) {
+        return toPropertyResolver(props, null);
+    }
+
+    public static PropertyResolver toPropertyResolver(Map<String, Object> props, PropertyResolver parent) {
+        return new PropertyResolver() {
+            @Override
+            public PropertyResolver getParentPropertyResolver() {
+                return parent;
+            }
+
+            @Override
+            public Map<String, Object> getProperties() {
+                return props;
+            }
+
+            @Override
+            public String toString() {
+                return Objects.toString(props);
+            }
+        };
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/RuntimeSshException.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/RuntimeSshException.java b/sshd-common/src/main/java/org/apache/sshd/common/RuntimeSshException.java
new file mode 100644
index 0000000..8c9164f
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/RuntimeSshException.java
@@ -0,0 +1,48 @@
+/*
+ * 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;
+
+/**
+ * Exception used in the SSH client or server.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class RuntimeSshException extends RuntimeException {
+    private static final long serialVersionUID = -2423550196146939503L;
+
+    public RuntimeSshException() {
+        this(null, null);
+    }
+
+    public RuntimeSshException(String message) {
+        this(message, null);
+    }
+
+    public RuntimeSshException(Throwable cause) {
+        this(null, cause);
+    }
+
+    public RuntimeSshException(String message, Throwable cause) {
+        super(message);
+        if (cause != null) {
+            initCause(cause);
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/SshConstants.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/SshConstants.java b/sshd-common/src/main/java/org/apache/sshd/common/SshConstants.java
new file mode 100644
index 0000000..01e8ae7
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/SshConstants.java
@@ -0,0 +1,245 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.logging.LoggingUtils;
+
+/**
+ * This interface defines constants for the SSH protocol.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class SshConstants {
+    //
+    // SSH message identifiers
+    //
+
+    public static final byte SSH_MSG_DISCONNECT = 1;
+    public static final byte SSH_MSG_IGNORE = 2;
+    public static final byte SSH_MSG_UNIMPLEMENTED = 3;
+    public static final byte SSH_MSG_DEBUG = 4;
+    public static final byte SSH_MSG_SERVICE_REQUEST = 5;
+    public static final byte SSH_MSG_SERVICE_ACCEPT = 6;
+    public static final byte SSH_MSG_KEXINIT = 20;
+    public static final int MSG_KEX_COOKIE_SIZE = 16;
+    public static final byte SSH_MSG_NEWKEYS = 21;
+
+    public static final byte SSH_MSG_KEX_FIRST = 30;
+    public static final byte SSH_MSG_KEX_LAST = 49;
+
+    public static final byte SSH_MSG_KEXDH_INIT = 30;
+    public static final byte SSH_MSG_KEXDH_REPLY = 31;
+
+    public static final byte SSH_MSG_KEX_DH_GEX_REQUEST_OLD = 30;
+    public static final byte SSH_MSG_KEX_DH_GEX_GROUP = 31;
+    public static final byte SSH_MSG_KEX_DH_GEX_INIT = 32;
+    public static final byte SSH_MSG_KEX_DH_GEX_REPLY = 33;
+    public static final byte SSH_MSG_KEX_DH_GEX_REQUEST = 34;
+
+    public static final byte SSH_MSG_USERAUTH_REQUEST = 50;
+    public static final byte SSH_MSG_USERAUTH_FAILURE = 51;
+    public static final byte SSH_MSG_USERAUTH_SUCCESS = 52;
+    public static final byte SSH_MSG_USERAUTH_BANNER = 53;
+
+    public static final byte SSH_MSG_USERAUTH_INFO_REQUEST = 60;
+    public static final byte SSH_MSG_USERAUTH_INFO_RESPONSE = 61;
+
+    public static final byte SSH_MSG_USERAUTH_PK_OK = 60;
+
+    public static final byte SSH_MSG_USERAUTH_PASSWD_CHANGEREQ = 60;
+
+    public static final byte SSH_MSG_USERAUTH_GSSAPI_MIC = 66;
+
+    public static final byte SSH_MSG_GLOBAL_REQUEST = 80;
+    public static final byte SSH_MSG_REQUEST_SUCCESS = 81;
+    public static final byte SSH_MSG_REQUEST_FAILURE = 82;
+    public static final byte SSH_MSG_CHANNEL_OPEN = 90;
+    public static final byte SSH_MSG_CHANNEL_OPEN_CONFIRMATION = 91;
+    public static final byte SSH_MSG_CHANNEL_OPEN_FAILURE = 92;
+    public static final byte SSH_MSG_CHANNEL_WINDOW_ADJUST = 93;
+    public static final byte SSH_MSG_CHANNEL_DATA = 94;
+    public static final byte SSH_MSG_CHANNEL_EXTENDED_DATA = 95;
+    public static final byte SSH_MSG_CHANNEL_EOF = 96;
+    public static final byte SSH_MSG_CHANNEL_CLOSE = 97;
+    public static final byte SSH_MSG_CHANNEL_REQUEST = 98;
+    public static final byte SSH_MSG_CHANNEL_SUCCESS = 99;
+    public static final byte SSH_MSG_CHANNEL_FAILURE = 100;
+
+    //
+    // Disconnect error codes
+    //
+    public static final int SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT = 1;
+    public static final int SSH2_DISCONNECT_PROTOCOL_ERROR = 2;
+    public static final int SSH2_DISCONNECT_KEY_EXCHANGE_FAILED = 3;
+    public static final int SSH2_DISCONNECT_HOST_AUTHENTICATION_FAILED = 4;
+    public static final int SSH2_DISCONNECT_RESERVED = 4;
+    public static final int SSH2_DISCONNECT_MAC_ERROR = 5;
+    public static final int SSH2_DISCONNECT_COMPRESSION_ERROR = 6;
+    public static final int SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE = 7;
+    public static final int SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED = 8;
+    public static final int SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE = 9;
+    public static final int SSH2_DISCONNECT_CONNECTION_LOST = 10;
+    public static final int SSH2_DISCONNECT_BY_APPLICATION = 11;
+    public static final int SSH2_DISCONNECT_TOO_MANY_CONNECTIONS = 12;
+    public static final int SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER = 13;
+    public static final int SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE = 14;
+    public static final int SSH2_DISCONNECT_ILLEGAL_USER_NAME = 15;
+
+    //
+    // Open error codes
+    //
+
+    public static final int SSH_OPEN_ADMINISTRATIVELY_PROHIBITED = 1;
+    public static final int SSH_OPEN_CONNECT_FAILED = 2;
+    public static final int SSH_OPEN_UNKNOWN_CHANNEL_TYPE = 3;
+    public static final int SSH_OPEN_RESOURCE_SHORTAGE = 4;
+
+    // Some more constants
+    public static final int SSH_EXTENDED_DATA_STDERR = 1;   // see RFC4254 section 5.2
+    public static final int SSH_PACKET_HEADER_LEN = 5;  // 32-bit length + 8-bit pad length
+
+    private SshConstants() {
+        throw new UnsupportedOperationException("No instance allowed");
+    }
+
+    private static final class LazyAmbiguousOpcodesHolder {
+        private static final Set<Integer> AMBIGUOUS_OPCODES =
+            Collections.unmodifiableSet(
+                new HashSet<>(
+                    LoggingUtils.getAmbiguousMenmonics(SshConstants.class, "SSH_MSG_").values()));
+
+        private LazyAmbiguousOpcodesHolder() {
+            throw new UnsupportedOperationException("No instance allowed");
+        }
+    }
+
+    /**
+     * @param cmd The command value
+     * @return {@code true} if this value is used by several <U>different</U> messages
+     * @see #getAmbiguousOpcodes()
+     */
+    public static boolean isAmbiguousOpcode(int cmd) {
+        Collection<Integer> ambiguousOpcodes = getAmbiguousOpcodes();
+        return ambiguousOpcodes.contains(cmd);
+    }
+
+    /**
+     * @return A {@link Set} of opcodes that are used by several <U>different</U> messages
+     */
+    @SuppressWarnings("synthetic-access")
+    public static Set<Integer> getAmbiguousOpcodes() {
+        return LazyAmbiguousOpcodesHolder.AMBIGUOUS_OPCODES;
+    }
+
+    private static final class LazyMessagesMapHolder {
+        private static final Map<Integer, String> MESSAGES_MAP =
+            LoggingUtils.generateMnemonicMap(SshConstants.class, f -> {
+                String name = f.getName();
+                if (!name.startsWith("SSH_MSG_")) {
+                    return false;
+                }
+
+                try {
+                    return !isAmbiguousOpcode(f.getByte(null));
+                } catch (Exception e) {
+                    return false;
+                }
+            });
+
+        private LazyMessagesMapHolder() {
+            throw new UnsupportedOperationException("No instance allowed");
+        }
+    }
+
+    /**
+     * Converts a command value to a user-friendly name
+     *
+     * @param cmd The command value
+     * @return The user-friendly name - if not one of the defined {@code SSH_MSG_XXX}
+     * values then returns the string representation of the command's value
+     */
+    public static String getCommandMessageName(int cmd) {
+        @SuppressWarnings("synthetic-access")
+        String name = LazyMessagesMapHolder.MESSAGES_MAP.get(cmd);
+        if (GenericUtils.isEmpty(name)) {
+            return Integer.toString(cmd);
+        } else {
+            return name;
+        }
+    }
+
+    private static final class LazyReasonsMapHolder {
+        private static final Map<Integer, String> REASONS_MAP =
+            LoggingUtils.generateMnemonicMap(SshConstants.class, "SSH2_DISCONNECT_");
+
+        private LazyReasonsMapHolder() {
+            throw new UnsupportedOperationException("No instance allowed");
+        }
+    }
+
+    /**
+     * Converts a disconnect reason value to a user-friendly name
+     *
+     * @param reason The disconnect reason value
+     * @return The user-friendly name - if not one of the defined {@code SSH2_DISCONNECT_}
+     * values then returns the string representation of the reason's value
+     */
+    public static String getDisconnectReasonName(int reason) {
+        @SuppressWarnings("synthetic-access")
+        String name = LazyReasonsMapHolder.REASONS_MAP.get(reason);
+        if (GenericUtils.isEmpty(name)) {
+            return Integer.toString(reason);
+        } else {
+            return name;
+        }
+    }
+
+    private static final class LazyOpenCodesMapHolder {
+        private static final Map<Integer, String> OPEN_CODES_MAP =
+            LoggingUtils.generateMnemonicMap(SshConstants.class, "SSH_OPEN_");
+
+        private LazyOpenCodesMapHolder() {
+            throw new UnsupportedOperationException("No instance allowed");
+        }
+    }
+
+    /**
+     * Converts an open error value to a user-friendly name
+     *
+     * @param code The open error value
+     * @return The user-friendly name - if not one of the defined {@code SSH_OPEN_}
+     * values then returns the string representation of the reason's value
+     */
+    public static String getOpenErrorCodeName(int code) {
+        @SuppressWarnings("synthetic-access")
+        String name = LazyOpenCodesMapHolder.OPEN_CODES_MAP.get(code);
+        if (GenericUtils.isEmpty(name)) {
+            return Integer.toString(code);
+        } else {
+            return name;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/SshException.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/SshException.java b/sshd-common/src/main/java/org/apache/sshd/common/SshException.java
new file mode 100644
index 0000000..4280d08
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/SshException.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common;
+
+import java.io.IOException;
+import java.util.Objects;
+
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * Represents an SSH related exception
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class SshException extends IOException {
+
+    private static final long serialVersionUID = -7349477687125144606L;
+
+    private final int disconnectCode;
+
+    public SshException(String message) {
+        this(message, null);
+    }
+
+    public SshException(Throwable cause) {
+        this(Objects.requireNonNull(cause, "No cause").getMessage(), cause);
+    }
+
+    public SshException(String message, Throwable cause) {
+        this(0, message, cause);
+    }
+
+    public SshException(int disconnectCode) {
+        this(disconnectCode, SshConstants.getDisconnectReasonName(disconnectCode));
+    }
+
+    public SshException(int disconnectCode, String message) {
+        this(disconnectCode, message, null);
+    }
+
+    public SshException(int disconnectCode, Throwable cause) {
+        this(disconnectCode, SshConstants.getDisconnectReasonName(disconnectCode), cause);
+    }
+
+    public SshException(int disconnectCode, String message, Throwable cause) {
+        super(GenericUtils.isEmpty(message) ? SshConstants.getDisconnectReasonName(disconnectCode) : message);
+        this.disconnectCode = disconnectCode;
+        if (cause != null) {
+            initCause(cause);
+        }
+    }
+
+    public int getDisconnectCode() {
+        return disconnectCode;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/SyspropsMapWrapper.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/SyspropsMapWrapper.java b/sshd-common/src/main/java/org/apache/sshd/common/SyspropsMapWrapper.java
new file mode 100644
index 0000000..4408be2
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/SyspropsMapWrapper.java
@@ -0,0 +1,209 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common;
+
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Properties;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.stream.Collectors;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.MapEntryUtils;
+
+/**
+ * A wrapper that exposes a read-only {@link Map} access to the system
+ * properties. Any attempt to modify it will throw {@link UnsupportedOperationException}.
+ * The mapper uses the {@link #SYSPROPS_MAPPED_PREFIX} to filter and access'
+ * only these properties, ignoring all others
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class SyspropsMapWrapper implements Map<String, Object> {
+    /**
+     * Prefix of properties used by the mapper to identify SSHD related settings
+     */
+    public static final String SYSPROPS_MAPPED_PREFIX = "org.apache.sshd.config";
+
+    /**
+     * The one and only wrapper instance
+     */
+    public static final SyspropsMapWrapper INSTANCE = new SyspropsMapWrapper();
+
+    /**
+     * A {@link PropertyResolver} with no parent that exposes the system properties
+     */
+    public static final PropertyResolver SYSPROPS_RESOLVER = new PropertyResolver() {
+        @Override
+        public Map<String, Object> getProperties() {
+            return SyspropsMapWrapper.INSTANCE;
+        }
+
+        @Override
+        public PropertyResolver getParentPropertyResolver() {
+            return null;
+        }
+
+        @Override
+        public String toString() {
+            return "SYSPROPS";
+        }
+    };
+
+    private SyspropsMapWrapper() {
+        super();
+    }
+
+    @Override
+    public void clear() {
+        throw new UnsupportedOperationException("sysprops#clear() N/A");
+    }
+
+    @Override
+    public boolean containsKey(Object key) {
+        return get(key) != null;
+    }
+
+    @Override
+    public boolean containsValue(Object value) {
+        // not the most efficient implementation, but we do not expect it to be called much
+        Properties props = System.getProperties();
+        for (String key : props.stringPropertyNames()) {
+            if (!isMappedSyspropKey(key)) {
+                continue;
+            }
+
+            Object v = props.getProperty(key);
+            if (Objects.equals(v, value)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    @Override
+    public Set<Entry<String, Object>> entrySet() {
+        Properties props = System.getProperties();
+        // return a copy in order to avoid concurrent modifications
+        Set<Entry<String, Object>> entries = new TreeSet<>(MapEntryUtils.byKeyEntryComparator());
+        for (String key : props.stringPropertyNames()) {
+            if (!isMappedSyspropKey(key)) {
+                continue;
+            }
+
+            Object v = props.getProperty(key);
+            if (v != null) {
+                entries.add(new SimpleImmutableEntry<>(getUnmappedSyspropKey(key), v));
+            }
+        }
+
+        return entries;
+    }
+
+    @Override
+    public Object get(Object key) {
+        return (key instanceof String) ? System.getProperty(getMappedSyspropKey(key)) : null;
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return GenericUtils.isEmpty(keySet());
+    }
+
+    @Override
+    public Set<String> keySet() {
+        return System.getProperties()
+                .stringPropertyNames().stream()
+                // filter out any non-SSHD properties
+                .filter(SyspropsMapWrapper::isMappedSyspropKey)
+                .map(SyspropsMapWrapper::getUnmappedSyspropKey)
+                .collect(Collectors.toSet());
+    }
+
+    @Override
+    public Object put(String key, Object value) {
+        throw new UnsupportedOperationException("sysprops#put(" + key + ")[" + value + "] N/A");
+    }
+
+    @Override
+    public void putAll(Map<? extends String, ?> m) {
+        throw new UnsupportedOperationException("sysprops#putAll(" + m + ") N/A");
+    }
+
+    @Override
+    public Object remove(Object key) {
+        throw new UnsupportedOperationException("sysprops#remove(" + key + ") N/A");
+    }
+
+    @Override
+    public int size() {
+        return GenericUtils.size(keySet());
+    }
+
+    @Override
+    public Collection<Object> values() {
+        Properties props = System.getProperties();
+        // return a copy in order to avoid concurrent modifications
+        return props
+                .stringPropertyNames().stream()
+                .filter(SyspropsMapWrapper::isMappedSyspropKey)
+                .map(props::get)
+                .collect(Collectors.toList());
+    }
+
+    @Override
+    public String toString() {
+        return Objects.toString(System.getProperties(), null);
+    }
+
+    /**
+     * @param key Key to be tested
+     * @return {@code true} if key starts with {@link #SYSPROPS_MAPPED_PREFIX}
+     * and continues with a dot followed by some characters
+     */
+    public static boolean isMappedSyspropKey(String key) {
+        return (GenericUtils.length(key) > (SYSPROPS_MAPPED_PREFIX.length() + 1))
+            && key.startsWith(SYSPROPS_MAPPED_PREFIX)
+            && (key.charAt(SYSPROPS_MAPPED_PREFIX.length()) == '.');
+    }
+
+    /**
+     * @param key Key to be transformed
+     * @return The &quot;pure&quot; key name if a mapped one, same as input otherwise
+     * @see #isMappedSyspropKey(String)
+     */
+    public static String getUnmappedSyspropKey(Object key) {
+        String s = Objects.toString(key);
+        return isMappedSyspropKey(s) ? s.substring(SYSPROPS_MAPPED_PREFIX.length() + 1 /* skip dot */) : s;
+    }
+
+    /**
+     * @param key The original key
+     * @return A key prefixed by {@link #SYSPROPS_MAPPED_PREFIX}
+     * @see #isMappedSyspropKey(String)
+     */
+    public static String getMappedSyspropKey(Object key) {
+        return SYSPROPS_MAPPED_PREFIX + "." + key;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/auth/MutableUserHolder.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/auth/MutableUserHolder.java b/sshd-common/src/main/java/org/apache/sshd/common/auth/MutableUserHolder.java
new file mode 100644
index 0000000..485f2f2
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/auth/MutableUserHolder.java
@@ -0,0 +1,27 @@
+/*
+ * 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.auth;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface MutableUserHolder extends UsernameHolder {
+    void setUsername(String username);
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/auth/UsernameHolder.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/auth/UsernameHolder.java b/sshd-common/src/main/java/org/apache/sshd/common/auth/UsernameHolder.java
new file mode 100644
index 0000000..7ee76ad
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/auth/UsernameHolder.java
@@ -0,0 +1,32 @@
+/*
+ * 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.auth;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FunctionalInterface
+public interface UsernameHolder {
+    /**
+     * @return The attached username - may be {@code null}/empty if holder
+     * not yet initialized
+     */
+    String getUsername();
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/cipher/BaseCipher.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/cipher/BaseCipher.java b/sshd-common/src/main/java/org/apache/sshd/common/cipher/BaseCipher.java
new file mode 100644
index 0000000..c3d9426
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/cipher/BaseCipher.java
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.cipher;
+
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.apache.sshd.common.SshException;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * Base class for all Cipher implementations delegating to the JCE provider.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class BaseCipher implements Cipher {
+
+    protected javax.crypto.Cipher cipher;
+    private final int ivsize;
+    private final int bsize;
+    private final String algorithm;
+    private final String transformation;
+    private String s;
+
+    public BaseCipher(int ivsize, int bsize, String algorithm, String transformation) {
+        this.ivsize = ivsize;
+        this.bsize = bsize;
+        this.algorithm = ValidateUtils.checkNotNullAndNotEmpty(algorithm, "No algorithm");
+        this.transformation = ValidateUtils.checkNotNullAndNotEmpty(transformation, "No transformation");
+    }
+
+    @Override
+    public String getAlgorithm() {
+        return algorithm;
+    }
+
+    @Override
+    public String getTransformation() {
+        return transformation;
+    }
+
+    @Override
+    public int getIVSize() {
+        return ivsize;
+    }
+
+    @Override
+    public int getBlockSize() {
+        return bsize;
+    }
+
+    @Override
+    public void init(Mode mode, byte[] key, byte[] iv) throws Exception {
+        key = resize(key, getBlockSize());
+        iv = resize(iv, getIVSize());
+        try {
+            cipher = SecurityUtils.getCipher(getTransformation());
+            cipher.init(Mode.Encrypt.equals(mode) ? javax.crypto.Cipher.ENCRYPT_MODE : javax.crypto.Cipher.DECRYPT_MODE,
+                    new SecretKeySpec(key, getAlgorithm()),
+                    new IvParameterSpec(iv));
+        } catch (Exception e) {
+            cipher = null;
+            throw new SshException("Unable to initialize cipher " + this, e);
+        }
+    }
+
+    @Override
+    public void update(byte[] input, int inputOffset, int inputLen) throws Exception {
+        cipher.update(input, inputOffset, inputLen, input, inputOffset);
+    }
+
+    protected static byte[] resize(byte[] data, int size) {
+        if (data.length > size) {
+            byte[] tmp = new byte[size];
+            System.arraycopy(data, 0, tmp, 0, size);
+            data = tmp;
+        }
+        return data;
+    }
+
+    @Override
+    public String toString() {
+        synchronized (this) {
+            if (s == null) {
+                s = getClass().getSimpleName()
+                    + "[" + getAlgorithm()
+                    + "," + getIVSize()
+                    + "," + getBlockSize()
+                    + "," + getTransformation()
+                    + "]";
+            }
+        }
+
+        return s;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/cipher/BaseRC4Cipher.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/cipher/BaseRC4Cipher.java b/sshd-common/src/main/java/org/apache/sshd/common/cipher/BaseRC4Cipher.java
new file mode 100644
index 0000000..2e3fc1e
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/cipher/BaseRC4Cipher.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.cipher;
+
+import javax.crypto.spec.SecretKeySpec;
+
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class BaseRC4Cipher extends BaseCipher {
+
+    public static final int SKIP_SIZE = 1536;
+
+    public BaseRC4Cipher(int ivsize, int bsize) {
+        super(ivsize, bsize, "ARCFOUR", "RC4");
+    }
+
+    @Override
+    public void init(Mode mode, byte[] key, byte[] iv) throws Exception {
+        key = resize(key, getBlockSize());
+        try {
+            cipher = SecurityUtils.getCipher(getTransformation());
+            cipher.init(Mode.Encrypt.equals(mode)  ? javax.crypto.Cipher.ENCRYPT_MODE : javax.crypto.Cipher.DECRYPT_MODE,
+                    new SecretKeySpec(key, getAlgorithm()));
+
+            byte[] foo = new byte[1];
+            for (int i = 0; i < SKIP_SIZE; i++) {
+                cipher.update(foo, 0, 1, foo, 0);
+            }
+        } catch (Exception e) {
+            cipher = null;
+            throw e;
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/cipher/BuiltinCiphers.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/cipher/BuiltinCiphers.java b/sshd-common/src/main/java/org/apache/sshd/common/cipher/BuiltinCiphers.java
new file mode 100644
index 0000000..8609d50
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/cipher/BuiltinCiphers.java
@@ -0,0 +1,348 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.cipher;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Map;
+import java.util.NavigableSet;
+import java.util.Objects;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.config.NamedFactoriesListParseResult;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * Provides easy access to the currently implemented ciphers
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public enum BuiltinCiphers implements CipherFactory {
+    none(Constants.NONE, 0, 0, "None", "None") {
+        @Override
+        public Cipher create() {
+            return new CipherNone();
+        }
+    },
+    aes128cbc(Constants.AES128_CBC, 16, 16, "AES", "AES/CBC/NoPadding"),
+    aes128ctr(Constants.AES128_CTR, 16, 16, "AES", "AES/CTR/NoPadding"),
+    aes192cbc(Constants.AES192_CBC, 16, 24, "AES", "AES/CBC/NoPadding"),
+    aes192ctr(Constants.AES192_CTR, 16, 24, "AES", "AES/CTR/NoPadding"),
+    aes256cbc(Constants.AES256_CBC, 16, 32, "AES", "AES/CBC/NoPadding"),
+    aes256ctr(Constants.AES256_CTR, 16, 32, "AES", "AES/CTR/NoPadding"),
+    arcfour128(Constants.ARCFOUR128, 8, 16, "ARCFOUR", "RC4") {
+        @Override
+        public Cipher create() {
+            return new BaseRC4Cipher(getIVSize(), getBlockSize());
+        }
+    },
+    arcfour256(Constants.ARCFOUR256, 8, 32, "ARCFOUR", "RC4") {
+        @Override
+        public Cipher create() {
+            return new BaseRC4Cipher(getIVSize(), getBlockSize());
+        }
+    },
+    blowfishcbc(Constants.BLOWFISH_CBC, 8, 16, "Blowfish", "Blowfish/CBC/NoPadding"),
+    tripledescbc(Constants.TRIPLE_DES_CBC, 8, 24, "DESede", "DESede/CBC/NoPadding");
+
+    public static final Set<BuiltinCiphers> VALUES =
+            Collections.unmodifiableSet(EnumSet.allOf(BuiltinCiphers.class));
+
+    private static final Map<String, CipherFactory> EXTENSIONS =
+            new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+
+    private final String factoryName;
+    private final int ivsize;
+    private final int blocksize;
+    private final int keysize;
+    private final String algorithm;
+    private final String transformation;
+    private final boolean supported;
+
+    BuiltinCiphers(String factoryName, int ivsize, int blocksize, String algorithm, String transformation) {
+        this.factoryName = factoryName;
+        this.ivsize = ivsize;
+        this.blocksize = blocksize;
+        this.keysize = blocksize * Byte.SIZE;
+        this.algorithm = algorithm;
+        this.transformation = transformation;
+        /*
+         * This can be done once since in order to change the support the JVM
+         * needs to be stopped, some unlimited-strength files need be installed
+         * and then the JVM re-started. Therefore, the answer is not going to
+         * change while the JVM is running
+         */
+        this.supported = Constants.NONE.equals(factoryName) || Cipher.checkSupported(this.transformation, this.keysize);
+    }
+
+    @Override
+    public final String getName() {
+        return factoryName;
+    }
+
+    @Override
+    public final String toString() {
+        return getName();
+    }
+
+    /**
+     * @return {@code true} if the current JVM configuration supports this
+     * cipher - e.g., AES-256 requires the <A HREF="http://www.oracle.com/technetwork/java/javase/downloads/">
+     * Java Cryptography Extension (JCE)</A>
+     */
+    @Override
+    public boolean isSupported() {
+        return supported;
+    }
+
+    /**
+     * @return The key size (in bits) for the cipher
+     */
+    public int getKeySize() {
+        return keysize;
+    }
+
+    @Override
+    public int getIVSize() {
+        return ivsize;
+    }
+
+    @Override
+    public int getBlockSize() {
+        return blocksize;
+    }
+
+    @Override
+    public String getAlgorithm() {
+        return algorithm;
+    }
+
+    @Override
+    public String getTransformation() {
+        return transformation;
+    }
+
+    @Override
+    public Cipher create() {
+        return new BaseCipher(getIVSize(), getBlockSize(), getAlgorithm(), getTransformation());
+    }
+
+    /**
+     * Registered a {@link NamedFactory} to be available besides the built-in
+     * ones when parsing configuration
+     *
+     * @param extension The factory to register
+     * @throws IllegalArgumentException if factory instance is {@code null},
+     * or overrides a built-in one or overrides another registered factory
+     * with the same name (case <U>insensitive</U>).
+     */
+    public static void registerExtension(CipherFactory extension) {
+        String name = Objects.requireNonNull(extension, "No extension provided").getName();
+        ValidateUtils.checkTrue(fromFactoryName(name) == null, "Extension overrides built-in: %s", name);
+
+        synchronized (EXTENSIONS) {
+            ValidateUtils.checkTrue(!EXTENSIONS.containsKey(name), "Extension overrides existing: %s", name);
+            EXTENSIONS.put(name, extension);
+        }
+    }
+
+    /**
+     * @return A {@link SortedSet} of the currently registered extensions, sorted
+     * according to the factory name (case <U>insensitive</U>)
+     */
+    public static NavigableSet<CipherFactory> getRegisteredExtensions() {
+        synchronized (EXTENSIONS) {
+            return GenericUtils.asSortedSet(NamedResource.BY_NAME_COMPARATOR, EXTENSIONS.values());
+        }
+    }
+
+    /**
+     * Unregisters specified extension
+     *
+     * @param name The factory name - ignored if {@code null}/empty
+     * @return The registered extension - {@code null} if not found
+     */
+    public static NamedFactory<Cipher> unregisterExtension(String name) {
+        if (GenericUtils.isEmpty(name)) {
+            return null;
+        }
+
+        synchronized (EXTENSIONS) {
+            return EXTENSIONS.remove(name);
+        }
+    }
+
+    /**
+     * @param s The {@link Enum}'s name - ignored if {@code null}/empty
+     * @return The matching {@link BuiltinCiphers} whose {@link Enum#name()} matches
+     * (case <U>insensitive</U>) the provided argument - {@code null} if no match
+     */
+    public static BuiltinCiphers fromString(String s) {
+        if (GenericUtils.isEmpty(s)) {
+            return null;
+        }
+
+        for (BuiltinCiphers c : VALUES) {
+            if (s.equalsIgnoreCase(c.name())) {
+                return c;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * @param factory The {@link NamedFactory} for the cipher - ignored if {@code null}
+     * @return The matching {@link BuiltinCiphers} whose factory name matches
+     * (case <U>insensitive</U>) the cipher factory name
+     * @see #fromFactoryName(String)
+     */
+    public static BuiltinCiphers fromFactory(NamedFactory<Cipher> factory) {
+        if (factory == null) {
+            return null;
+        } else {
+            return fromFactoryName(factory.getName());
+        }
+    }
+
+    /**
+     * @param name The factory name - ignored if {@code null}/empty
+     * @return The matching {@link BuiltinCiphers} whose factory name matches
+     * (case <U>insensitive</U>) the provided name - {@code null} if no match
+     */
+    public static BuiltinCiphers fromFactoryName(String name) {
+        return NamedResource.findByName(name, String.CASE_INSENSITIVE_ORDER, VALUES);
+    }
+
+    /**
+     * @param ciphers A comma-separated list of ciphers' names - ignored if {@code null}/empty
+     * @return A {@link ParseResult} containing the successfully parsed
+     * factories and the unknown ones. <B>Note:</B> it is up to caller to
+     * ensure that the lists do not contain duplicates
+     */
+    public static ParseResult parseCiphersList(String ciphers) {
+        return parseCiphersList(GenericUtils.split(ciphers, ','));
+    }
+
+    public static ParseResult parseCiphersList(String... ciphers) {
+        return parseCiphersList(GenericUtils.isEmpty((Object[]) ciphers) ? Collections.emptyList() : Arrays.asList(ciphers));
+    }
+
+    public static ParseResult parseCiphersList(Collection<String> ciphers) {
+        if (GenericUtils.isEmpty(ciphers)) {
+            return ParseResult.EMPTY;
+        }
+
+        List<CipherFactory> factories = new ArrayList<>(ciphers.size());
+        List<String> unknown = Collections.emptyList();
+        for (String name : ciphers) {
+            CipherFactory c = resolveFactory(name);
+            if (c != null) {
+                factories.add(c);
+            } else {
+                // replace the (unmodifiable) empty list with a real one
+                if (unknown.isEmpty()) {
+                    unknown = new ArrayList<>();
+                }
+                unknown.add(name);
+            }
+        }
+
+        return new ParseResult(factories, unknown);
+    }
+
+    /**
+     * @param name The factory name
+     * @return The factory or {@code null} if it is neither a built-in one
+     * or a registered extension
+     */
+    public static CipherFactory resolveFactory(String name) {
+        if (GenericUtils.isEmpty(name)) {
+            return null;
+        }
+
+        CipherFactory c = fromFactoryName(name);
+        if (c != null) {
+            return c;
+        }
+
+        synchronized (EXTENSIONS) {
+            return EXTENSIONS.get(name);
+        }
+    }
+
+    /**
+     * Holds the result of {@link BuiltinCiphers#parseCiphersList(String)}
+     *
+     * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+     */
+    public static class ParseResult extends NamedFactoriesListParseResult<Cipher, CipherFactory> {
+        public static final ParseResult EMPTY = new ParseResult(Collections.emptyList(), Collections.emptyList());
+
+        public ParseResult(List<CipherFactory> parsed, List<String> unsupported) {
+            super(parsed, unsupported);
+        }
+    }
+
+    public static final class Constants {
+        public static final String NONE = "none";
+        public static final Pattern NONE_CIPHER_PATTERN =
+                Pattern.compile("(^|.*,)" + NONE + "($|,.*)");
+
+        public static final String AES128_CBC = "aes128-cbc";
+        public static final String AES128_CTR = "aes128-ctr";
+        public static final String AES192_CBC = "aes192-cbc";
+        public static final String AES192_CTR = "aes192-ctr";
+        public static final String AES256_CBC = "aes256-cbc";
+        public static final String AES256_CTR = "aes256-ctr";
+        public static final String ARCFOUR128 = "arcfour128";
+        public static final String ARCFOUR256 = "arcfour256";
+        public static final String BLOWFISH_CBC = "blowfish-cbc";
+        public static final String TRIPLE_DES_CBC = "3des-cbc";
+
+        private Constants() {
+            throw new UnsupportedOperationException("No instance allowed");
+        }
+
+        /**
+         * @param s A comma-separated list of ciphers - ignored if {@code null}/empty
+         * @return {@code true} if the {@link #NONE} cipher name appears in it
+         */
+        public static boolean isNoneCipherIncluded(String s) {
+            if (GenericUtils.isEmpty(s)) {
+                return false;
+            }
+            Matcher m = NONE_CIPHER_PATTERN.matcher(s);
+            return m.matches();
+        }
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/cipher/Cipher.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/cipher/Cipher.java b/sshd-common/src/main/java/org/apache/sshd/common/cipher/Cipher.java
new file mode 100644
index 0000000..868e983
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/cipher/Cipher.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.cipher;
+
+import org.apache.sshd.common.util.NumberUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * Wrapper for a cryptographic cipher, used either for encryption
+ * or decryption.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface Cipher extends CipherInformation {
+
+    enum Mode {
+        Encrypt, Decrypt
+    }
+
+    /**
+     * Initialize the cipher for encryption or decryption with
+     * the given key and initialization vector
+     *
+     * @param mode Encrypt/Decrypt initialization
+     * @param key  Key bytes
+     * @param iv   Initialization vector bytes
+     * @throws Exception If failed to initialize
+     */
+    void init(Mode mode, byte[] key, byte[] iv) throws Exception;
+
+    /**
+     * Performs in-place encryption or decryption on the given data.
+     *
+     * @param input The input/output bytes
+     * @throws Exception If failed to execute
+     * @see #update(byte[], int, int)
+     */
+    default void update(byte[] input) throws Exception {
+        update(input, 0, NumberUtils.length(input));
+    }
+
+    /**
+     * Performs in-place encryption or decryption on the given data.
+     *
+     * @param input       The input/output bytes
+     * @param inputOffset The offset of the data in the data buffer
+     * @param inputLen    The number of bytes to update - starting at the given offset
+     * @throws Exception If failed to execute
+     */
+    void update(byte[] input, int inputOffset, int inputLen) throws Exception;
+
+    /**
+     * @param xform The full cipher transformation - e.g., AES/CBC/NoPadding -
+     * never {@code null}/empty
+     * @param keyLength The required key length in bits - always positive
+     * @return {@code true} if the cipher transformation <U>and</U> required
+     * key length are supported
+     * @see javax.crypto.Cipher#getMaxAllowedKeyLength(String)
+     */
+    static boolean checkSupported(String xform, int keyLength) {
+        ValidateUtils.checkNotNullAndNotEmpty(xform, "No transformation");
+        if (keyLength <= 0) {
+            throw new IllegalArgumentException("Bad key length (" + keyLength + ") for cipher=" + xform);
+        }
+
+        try {
+            int maxKeyLength = javax.crypto.Cipher.getMaxAllowedKeyLength(xform);
+            return maxKeyLength >= keyLength;
+        } catch (Exception e) {
+            return false;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/cipher/CipherFactory.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/cipher/CipherFactory.java b/sshd-common/src/main/java/org/apache/sshd/common/cipher/CipherFactory.java
new file mode 100644
index 0000000..36909f3
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/cipher/CipherFactory.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.cipher;
+
+import org.apache.sshd.common.BuiltinFactory;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+// CHECKSTYLE:OFF
+public interface CipherFactory extends BuiltinFactory<Cipher>, CipherInformation {
+    // nothing extra
+}
+//CHECKSTYLE:ON

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/cipher/CipherInformation.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/cipher/CipherInformation.java b/sshd-common/src/main/java/org/apache/sshd/common/cipher/CipherInformation.java
new file mode 100644
index 0000000..f17fd16
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/cipher/CipherInformation.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.cipher;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface CipherInformation {
+    /**
+     * @return The cipher's algorithm
+     */
+    String getAlgorithm();
+
+    /**
+     * @return The actual transformation used - e.g., AES/CBC/NoPadding
+     */
+    String getTransformation();
+
+    /**
+     * @return Size of the initialization vector (in bytes)
+     */
+    int getIVSize();
+
+    /**
+     * @return The block size (in bytes) for this cipher
+     */
+    int getBlockSize();
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/cipher/CipherNone.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/cipher/CipherNone.java b/sshd-common/src/main/java/org/apache/sshd/common/cipher/CipherNone.java
new file mode 100644
index 0000000..15b6e9f
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/cipher/CipherNone.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.cipher;
+
+/**
+ * Represents a no-op cipher.
+ * This cipher can not really be used during authentication and should only
+ * be used after, so that authentication remains secured, but not the remaining
+ * of the exchanges.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class CipherNone implements Cipher {
+    public CipherNone() {
+        super();
+    }
+
+    @Override
+    public String getAlgorithm() {
+        return "none";
+    }
+
+    @Override
+    public String getTransformation() {
+        return "none";
+    }
+
+    @Override
+    public int getIVSize() {
+        return 8;   // dummy
+    }
+
+    @Override
+    public int getBlockSize() {
+        return 16;  // dummy
+    }
+
+    @Override
+    public void init(Mode mode, byte[] bytes, byte[] bytes1) throws Exception {
+        // ignored - always succeeds
+    }
+
+    @Override
+    public void update(byte[] input, int inputOffset, int inputLen) throws Exception {
+        // ignored - always succeeds
+    }
+}


[06/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common utilities code from sshd-core into sshd-common (new artifact)

Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/io/NoCloseReaderTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/io/NoCloseReaderTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/io/NoCloseReaderTest.java
deleted file mode 100644
index 193aabd..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/io/NoCloseReaderTest.java
+++ /dev/null
@@ -1,95 +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.io;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.Date;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class NoCloseReaderTest extends BaseTestSupport {
-    public NoCloseReaderTest() {
-        super();
-    }
-
-    @Test
-    public void testCanKeepReadingAfterClose() throws IOException {
-        String expected = getClass().getName() + "#" + getCurrentTestName() + "@" + new Date();
-        Path dir = createTempClassFolder();
-        Path file = Files.write(dir.resolve(getCurrentTestName() + ".txt"), expected.getBytes(StandardCharsets.UTF_8));
-        try (InputStream fileStream = Files.newInputStream(file);
-             Reader rdr = new InputStreamReader(fileStream, StandardCharsets.UTF_8);
-             Reader shielded = new NoCloseReader(rdr)) {
-            int index = 0;
-
-            int availLen = expected.length();
-            for (; index < (availLen / 2); index++) {
-                shielded.close();
-
-                int readValue = shielded.read();
-                if (readValue == -1) {
-                    fail("Premature EOF after shield read of " + index + " bytes");
-                }
-
-                char expValue = expected.charAt(index);
-                char actValue = (char) (readValue & 0xFFFF);
-                if (expValue != actValue) {
-                    fail("Mismatched shielded read value after " + index + " bytes");
-                }
-            }
-
-            for (; index < availLen; index++) {
-                int readValue = rdr.read();
-                if (readValue == -1) {
-                    fail("Premature EOF after original read of " + index + " bytes");
-                }
-
-                char expValue = expected.charAt(index);
-                char actValue = (char) (readValue & 0xFFFF);
-                if (expValue != actValue) {
-                    fail("Mismatched original read value after " + index + " bytes");
-                }
-            }
-
-            int readValue = shielded.read();
-            assertEquals("Shielded EOF not signalled", -1, readValue);
-
-            readValue = rdr.read();
-            assertEquals("Original EOF not signalled", -1, readValue);
-        }
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/io/NoCloseWriterTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/io/NoCloseWriterTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/io/NoCloseWriterTest.java
deleted file mode 100644
index efc6a91..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/io/NoCloseWriterTest.java
+++ /dev/null
@@ -1,73 +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.io;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.Writer;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.Date;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class NoCloseWriterTest extends BaseTestSupport {
-    public NoCloseWriterTest() {
-        super();
-    }
-
-    @Test
-    public void testCanKeepWritingAfterClose() throws IOException {
-        Path dir = createTempClassFolder();
-        Path file = dir.resolve(getCurrentTestName() + ".txt");
-        Files.deleteIfExists(file);
-
-        String expected = getClass().getName() + "#" + getCurrentTestName() + "@" + new Date();
-        try (OutputStream fileStream = Files.newOutputStream(file);
-             Writer w = new OutputStreamWriter(fileStream, StandardCharsets.UTF_8);
-                Writer shielded = new NoCloseWriter(w)) {
-            int index = 0;
-            int availLen = expected.length();
-            for (; index < (availLen / 2); index++) {
-                shielded.close();
-                shielded.write(expected.charAt(index));
-            }
-
-            w.write(expected, index, availLen - index);
-        }
-
-        byte[] actualBytes = Files.readAllBytes(file);
-        String actual = new String(actualBytes, StandardCharsets.UTF_8);
-        assertEquals(expected, actual);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/io/NullInputStreamTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/io/NullInputStreamTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/io/NullInputStreamTest.java
deleted file mode 100644
index bc97b9c..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/io/NullInputStreamTest.java
+++ /dev/null
@@ -1,118 +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.io;
-
-import java.io.EOFException;
-import java.io.IOException;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class NullInputStreamTest extends BaseTestSupport {
-    private static final NullInputStream INSTANCE = new NullInputStream();
-
-    public NullInputStreamTest() {
-        super();
-    }
-
-    @Test
-    public void testReadOneChar() throws IOException {
-        assertEquals(-1, INSTANCE.read());
-    }
-
-    @Test
-    public void testReadFullBuffer() throws IOException {
-        assertEquals(-1, INSTANCE.read(new byte[Byte.SIZE]));
-    }
-
-    @Test
-    public void testReadPartialBuffer() throws IOException {
-        byte[] buf = new byte[Byte.SIZE];
-        assertEquals(-1, INSTANCE.read(buf, buf.length / 2, (buf.length / 2) - 1));
-    }
-
-    @Test
-    public void testSkip() throws IOException {
-        assertEquals(0L, INSTANCE.skip(Long.SIZE));
-    }
-
-    @Test
-    public void testAvailable() throws IOException {
-        assertEquals(0, INSTANCE.available());
-    }
-
-    @Test
-    public void testNotAllowedToAccessAfterClose() throws IOException {
-        NullInputStream stream = new NullInputStream();
-        stream.close();
-        assertFalse("Stream not marked as closed", stream.isOpen());
-
-        try {
-            int nRead = stream.read();
-            fail("Unexpected single byte read: " + nRead);
-        } catch (EOFException e) {
-            // expected
-        }
-
-        byte[] buf = new byte[Byte.SIZE];
-        try {
-            int nRead = stream.read(buf);
-            fail("Unexpected full buffer read: " + nRead);
-        } catch (EOFException e) {
-            // expected
-        }
-
-        try {
-            int nRead = stream.read(buf, buf.length / 2, (buf.length / 2) - 1);
-            fail("Unexpected partial buffer read: " + nRead);
-        } catch (EOFException e) {
-            // expected
-        }
-
-        try {
-            long skip = stream.skip(Long.SIZE);
-            fail("Unexpected skip result: " + skip);
-        } catch (EOFException e) {
-            // expected
-        }
-
-        try {
-            int nRead = stream.available();
-            fail("Unexpected available count: " + nRead);
-        } catch (IOException e) {
-            // expected
-        }
-        try {
-            stream.reset();
-            fail("Unexpected reset success");
-        } catch (EOFException e) {
-            // expected
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/io/NullOutputStreamTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/io/NullOutputStreamTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/io/NullOutputStreamTest.java
deleted file mode 100644
index b6a0230..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/io/NullOutputStreamTest.java
+++ /dev/null
@@ -1,80 +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.io;
-
-import java.io.EOFException;
-import java.io.IOException;
-import java.util.Arrays;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class NullOutputStreamTest extends BaseTestSupport {
-    public NullOutputStreamTest() {
-        super();
-    }
-
-    @Test
-    public void testNoAccessAllowedAfterClose() throws IOException {
-        NullOutputStream stream = new NullOutputStream();
-        stream.close();
-        assertFalse("Stream not marked as closed", stream.isOpen());
-
-        try {
-            stream.write('a');
-            fail("Unexpected single value write success");
-        } catch (EOFException e) {
-            // expected
-        }
-
-        byte[] buf = new byte[Byte.SIZE];
-        try {
-            Arrays.fill(buf, (byte) 0x41);
-            stream.write(buf);
-            fail("Unexpected full buffer write success");
-        } catch (EOFException e) {
-            // expected
-        }
-
-        try {
-            Arrays.fill(buf, (byte) 0x42);
-            stream.write(buf, buf.length / 2, (buf.length / 2) - 1);
-            fail("Unexpected partial buffer write success");
-        } catch (EOFException e) {
-            // expected
-        }
-
-        try {
-            stream.flush();
-            fail("Unexpected flush success");
-        } catch (EOFException e) {
-            // expected
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/io/der/ASN1ClassTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/io/der/ASN1ClassTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/io/der/ASN1ClassTest.java
deleted file mode 100644
index 04bcb0e..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/io/der/ASN1ClassTest.java
+++ /dev/null
@@ -1,67 +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.io.der;
-
-import java.util.List;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runner.RunWith;
-import org.junit.runners.MethodSorters;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-import org.junit.runners.Parameterized.UseParametersRunnerFactory;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@RunWith(Parameterized.class)   // see https://github.com/junit-team/junit/wiki/Parameterized-tests
-@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
-@Category({ NoIoTestCase.class })
-public class ASN1ClassTest extends BaseTestSupport {
-    private final ASN1Class expected;
-
-    public ASN1ClassTest(ASN1Class expected) {
-        this.expected = expected;
-    }
-
-    @Parameters(name = "{0}")
-    public static List<Object[]> parameters() {
-        return parameterize(ASN1Class.VALUES);
-    }
-
-    @Test
-    public void testFromName() {
-        String name = expected.name();
-        for (int index = 1, count = name.length(); index <= count; index++) {
-            assertSame(name, expected, ASN1Class.fromName(name));
-            name = shuffleCase(name);
-        }
-    }
-
-    @Test // NOTE: this also tests "fromTypeValue" since "fromDERValue" invokes it
-    public void testFromDERValue() {
-        assertSame(expected, ASN1Class.fromDERValue((expected.getClassValue() << 6) & 0xFF));
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/io/der/ASN1TypeTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/io/der/ASN1TypeTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/io/der/ASN1TypeTest.java
deleted file mode 100644
index 82b9611..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/io/der/ASN1TypeTest.java
+++ /dev/null
@@ -1,67 +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.io.der;
-
-import java.util.List;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runner.RunWith;
-import org.junit.runners.MethodSorters;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-import org.junit.runners.Parameterized.UseParametersRunnerFactory;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@RunWith(Parameterized.class)   // see https://github.com/junit-team/junit/wiki/Parameterized-tests
-@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
-@Category({ NoIoTestCase.class })
-public class ASN1TypeTest extends BaseTestSupport {
-    private final ASN1Type expected;
-
-    public ASN1TypeTest(ASN1Type expected) {
-        this.expected = expected;
-    }
-
-    @Parameters(name = "{0}")
-    public static List<Object[]> parameters() {
-        return parameterize(ASN1Type.VALUES);
-    }
-
-    @Test
-    public void testFromName() {
-        String name = expected.name();
-        for (int index = 1, count = name.length(); index <= count; index++) {
-            assertSame(name, expected, ASN1Type.fromName(name));
-            name = shuffleCase(name);
-        }
-    }
-
-    @Test
-    public void testFromTypeValue() {
-        assertSame(expected, ASN1Type.fromTypeValue(expected.getTypeValue()));
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/io/der/DERParserTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/io/der/DERParserTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/io/der/DERParserTest.java
deleted file mode 100644
index df8e992..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/io/der/DERParserTest.java
+++ /dev/null
@@ -1,61 +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.io.der;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.StreamCorruptedException;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class DERParserTest extends BaseTestSupport {
-    public DERParserTest() {
-        super();
-    }
-
-    @Test
-    public void testReadLengthConstraint() throws IOException {
-        ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        try {
-            try (DERWriter w = new DERWriter(baos)) {
-                w.writeLength(DERParser.MAX_DER_VALUE_LENGTH + 1);
-            }
-        } finally {
-            baos.close();
-        }
-
-        try (DERParser parser = new DERParser(baos.toByteArray())) {
-            int len = parser.readLength();
-            fail("Unexpected success: len=" + len);
-        } catch (StreamCorruptedException e) {
-            // expected ignored
-        }
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/io/der/DERWriterTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/io/der/DERWriterTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/io/der/DERWriterTest.java
deleted file mode 100644
index 9abe446..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/io/der/DERWriterTest.java
+++ /dev/null
@@ -1,64 +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.io.der;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.math.BigInteger;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class DERWriterTest extends BaseTestSupport {
-    public DERWriterTest() {
-        super();
-    }
-
-    @Test
-    public void testWriteStripLeadingZeroes() throws IOException {
-        ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        try {
-            try (DERWriter w = new DERWriter(baos)) {
-                w.writeBigInteger(BigInteger.valueOf(-1));
-                w.writeBigInteger(BigInteger.valueOf(129));
-                w.writeBigInteger(new byte[] {0, 0}, 0, 2);
-                w.writeBigInteger(new byte[] {0, 1}, 0, 2);
-            }
-        } finally {
-            baos.close();
-        }
-
-        try (DERParser parser = new DERParser(baos.toByteArray())) {
-            assertEquals(BigInteger.valueOf(255), parser.readBigInteger());
-            assertEquals(BigInteger.valueOf(129), parser.readBigInteger());
-            assertEquals(BigInteger.valueOf(0), parser.readBigInteger());
-            assertEquals(BigInteger.valueOf(1), parser.readBigInteger());
-        }
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/net/SshdSocketIpv6AddressTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/net/SshdSocketIpv6AddressTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/net/SshdSocketIpv6AddressTest.java
deleted file mode 100644
index cc69df5..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/net/SshdSocketIpv6AddressTest.java
+++ /dev/null
@@ -1,88 +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.net;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runner.RunWith;
-import org.junit.runners.MethodSorters;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-import org.junit.runners.Parameterized.UseParametersRunnerFactory;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@RunWith(Parameterized.class)   // see https://github.com/junit-team/junit/wiki/Parameterized-tests
-@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class SshdSocketIpv6AddressTest extends BaseTestSupport {
-    public static final List<String> VALID_ADDRESSES =
-        Collections.unmodifiableList(
-            Arrays.asList(
-                "2001:0db8:85a3:0000:0000:8a2e:0370:7334", "2001:db8:85a3:0:0:8a2e:370:7334",
-                "2001:db8:85a3::8a2e:370:7334",
-                "2001:0db8::0001", "2001:db8::1",
-                "2001:db8:0:0:0:0:2:1", "2001:db8::2:1",
-                "2001:db8:0000:1:1:1:1:1", "2001:db8:0:1:1:1:1:1",
-                "2001:db8:85a3:8d3:1319:8a2e:370:7348",
-                "fe80::1ff:fe23:4567:890a", "fe80::1ff:fe23:4567:890a%eth2",
-                "fe80::1ff:fe23:4567:890a%3", "fe80:3::1ff:fe23:4567:890a",
-                "::ffff:c000:0280", "::ffff:192.0.2.128"));
-
-    private final String address;
-    private final boolean matches;
-
-    public SshdSocketIpv6AddressTest(String address, boolean matches) {
-        this.address = address;
-        this.matches = matches;
-    }
-
-    @Parameters(name = "{0}")
-    public static List<Object[]> parameters() {
-        return Stream
-                .concat(SshdSocketAddress.WELL_KNOWN_IPV6_ADDRESSES.stream(), VALID_ADDRESSES.stream())
-                .map(address -> new Object[] {address, Boolean.TRUE})
-                .collect(Collectors.toList());
-    }
-
-    @Test
-    public void testIPv6AddressValidity() {
-        assertEquals(address, matches, SshdSocketAddress.isIPv6Address(address));
-    }
-
-    @Override
-    public String toString() {
-        return getClass().getSimpleName()
-            + "[address=" + address
-            + " , matches=" + matches
-            + "]";
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/security/SecurityProviderRegistrarCipherNameTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/security/SecurityProviderRegistrarCipherNameTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/security/SecurityProviderRegistrarCipherNameTest.java
deleted file mode 100644
index c0c89eb..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/security/SecurityProviderRegistrarCipherNameTest.java
+++ /dev/null
@@ -1,75 +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.security;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.crypto.Cipher;
-
-import org.apache.sshd.common.cipher.BuiltinCiphers;
-import org.apache.sshd.common.cipher.CipherInformation;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runner.RunWith;
-import org.junit.runners.MethodSorters;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-import org.junit.runners.Parameterized.UseParametersRunnerFactory;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@RunWith(Parameterized.class)   // see https://github.com/junit-team/junit/wiki/Parameterized-tests
-@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
-@Category({ NoIoTestCase.class })
-public class SecurityProviderRegistrarCipherNameTest extends BaseTestSupport {
-    private final CipherInformation cipherInfo;
-
-    public SecurityProviderRegistrarCipherNameTest(CipherInformation cipherInfo) {
-        this.cipherInfo = cipherInfo;
-    }
-
-    @Parameters(name = "{0}")
-    public static List<Object[]> parameters() {
-        List<Object[]> params = new ArrayList<>();
-        for (CipherInformation cipherInfo : BuiltinCiphers.VALUES) {
-            String algorithm = cipherInfo.getAlgorithm();
-            String xform = cipherInfo.getTransformation();
-            if (!xform.startsWith(algorithm)) {
-                continue;
-            }
-
-            params.add(new Object[]{cipherInfo});
-        }
-        return params;
-    }
-
-    @Test
-    public void testGetEffectiveSecurityEntityName() {
-        String expected = cipherInfo.getAlgorithm();
-        String actual = SecurityProviderRegistrar.getEffectiveSecurityEntityName(Cipher.class, cipherInfo.getTransformation());
-        assertEquals("Mismatched pure cipher name", expected, actual);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/security/SecurityProviderRegistrarTestSupport.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/security/SecurityProviderRegistrarTestSupport.java b/sshd-core/src/test/java/org/apache/sshd/common/util/security/SecurityProviderRegistrarTestSupport.java
deleted file mode 100644
index 57999de..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/security/SecurityProviderRegistrarTestSupport.java
+++ /dev/null
@@ -1,62 +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.security;
-
-import java.security.Provider;
-import java.util.Arrays;
-import java.util.Collection;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.experimental.categories.Category;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@Category({ NoIoTestCase.class })
-public abstract class SecurityProviderRegistrarTestSupport extends BaseTestSupport {
-    protected SecurityProviderRegistrarTestSupport() {
-        super();
-    }
-
-    public static Provider testGetSecurityProviderCaching(String prefix, SecurityProviderRegistrar registrar) {
-        return testGetSecurityProviderCaching(prefix, registrar, registrar.getSecurityProvider());
-    }
-
-    public static <P extends Provider> P testGetSecurityProviderCaching(String prefix, SecurityProviderRegistrar registrar, P expected) {
-        for (int index = 1; index <= Byte.SIZE; index++) {
-            Provider actual = registrar.getSecurityProvider();
-            assertSame(prefix + ": Mismatched provider instance at invocation #" + index, expected, actual);
-        }
-
-        return expected;
-    }
-
-    public static void assertSecurityEntitySupportState(
-            String prefix, SecurityProviderRegistrar registrar, boolean expected, String name, Class<?>... entities) {
-        assertSecurityEntitySupportState(prefix, registrar, expected, name, Arrays.asList(entities));
-    }
-
-    public static void assertSecurityEntitySupportState(
-            String prefix, SecurityProviderRegistrar registrar, boolean expected, String name, Collection<Class<?>> entities) {
-        for (Class<?> entity : entities) {
-            assertEquals(prefix + "[" + entity.getSimpleName() + "]", expected, registrar.isSecurityEntitySupported(entity, name));
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/security/eddsa/EDDSAProviderTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/security/eddsa/EDDSAProviderTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/security/eddsa/EDDSAProviderTest.java
deleted file mode 100644
index d30fe92..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/security/eddsa/EDDSAProviderTest.java
+++ /dev/null
@@ -1,126 +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.security.eddsa;
-
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.Signature;
-
-import org.apache.sshd.common.config.keys.AuthorizedKeyEntry;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.buffer.Buffer;
-import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
-import org.apache.sshd.common.util.security.SecurityUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.Assume;
-import org.junit.BeforeClass;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-import net.i2p.crypto.eddsa.EdDSAEngine;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class EDDSAProviderTest extends BaseTestSupport {
-    private static KeyPair keyPair;
-
-    public EDDSAProviderTest() {
-        super();
-    }
-
-    @BeforeClass
-    public static void checkProviderSupported() throws GeneralSecurityException {
-        Assume.assumeTrue(SecurityUtils.EDDSA + " not supported", SecurityUtils.isEDDSACurveSupported());
-        KeyPairGenerator g = SecurityUtils.getKeyPairGenerator(SecurityUtils.EDDSA);
-        assertNotNull("No generator instance", g);
-
-        keyPair = g.generateKeyPair();
-        assertNotNull("No key pair generated", keyPair);
-
-        PublicKey pubKey = keyPair.getPublic();
-        assertNotNull("No public key", pubKey);
-        assertEquals("Mismatched public key algorithm", SecurityUtils.EDDSA, pubKey.getAlgorithm());
-        assertEquals("Mismatched public key type", KeyPairProvider.SSH_ED25519, KeyUtils.getKeyType(pubKey));
-
-        PrivateKey prvKey = keyPair.getPrivate();
-        assertNotNull("No private key", prvKey);
-        assertEquals("Mismatched key-pair algorithm", pubKey.getAlgorithm(), prvKey.getAlgorithm());
-        assertEquals("Mismatched private key type", KeyPairProvider.SSH_ED25519, KeyUtils.getKeyType(prvKey));
-    }
-
-    @Test
-    public void testSignature() throws GeneralSecurityException {
-        Signature s = SecurityUtils.getSignature(EdDSAEngine.SIGNATURE_ALGORITHM);
-        assertNotNull("No signature instance", s);
-        s.initSign(keyPair.getPrivate());
-
-        byte[] data = (getClass().getName() + "#" + getCurrentTestName()).getBytes(StandardCharsets.UTF_8);
-        s.update(data);
-        byte[] signed = s.sign();
-
-        s = SecurityUtils.getSignature(EdDSAEngine.SIGNATURE_ALGORITHM);
-        s.initVerify(keyPair.getPublic());
-        s.update(data);
-        assertTrue("Failed to verify", s.verify(signed));
-    }
-
-    @Test
-    public void testPublicKeyEntryDecoder() throws IOException, GeneralSecurityException {
-        String comment = getCurrentTestName() + "@" + getClass().getSimpleName();
-        String expected = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGPKSUTyz1HwHReFVvD5obVsALAgJRNarH4TRpNePnAS " + comment;
-        AuthorizedKeyEntry keyEntry = AuthorizedKeyEntry.parseAuthorizedKeyEntry(expected);
-        assertNotNull("No extracted key entry", keyEntry);
-
-        assertEquals("Mismatched key type", KeyPairProvider.SSH_ED25519, keyEntry.getKeyType());
-        assertEquals("Mismatched comment", comment, keyEntry.getComment());
-
-        StringBuilder sb = new StringBuilder(expected.length());
-        PublicKey pubKey = keyEntry.appendPublicKey(sb, null);
-        assertEquals("Mismatched encoded result", expected, sb.toString());
-
-        testPublicKeyRecovery(pubKey);
-    }
-
-    @Test
-    public void testGeneratedPublicKeyRecovery() throws IOException, GeneralSecurityException {
-        testPublicKeyRecovery(keyPair.getPublic());
-    }
-
-    private void testPublicKeyRecovery(PublicKey pubKey) throws IOException, GeneralSecurityException {
-        assertNotNull("No public key generated", pubKey);
-        assertEquals("Mismatched public key algorithm", SecurityUtils.EDDSA, pubKey.getAlgorithm());
-
-        Buffer buf = SecurityUtils.putRawEDDSAPublicKey(new ByteArrayBuffer(), pubKey);
-        PublicKey actual = buf.getRawPublicKey();
-        assertEquals("Mismatched key algorithm", pubKey.getAlgorithm(), actual.getAlgorithm());
-        assertEquals("Mismatched recovered key", pubKey, actual);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/security/eddsa/Ed25519VectorsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/security/eddsa/Ed25519VectorsTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/security/eddsa/Ed25519VectorsTest.java
deleted file mode 100644
index 23e5517..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/security/eddsa/Ed25519VectorsTest.java
+++ /dev/null
@@ -1,238 +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.security.eddsa;
-
-import java.nio.charset.StandardCharsets;
-import java.security.GeneralSecurityException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-import org.apache.sshd.common.signature.Signature;
-import org.apache.sshd.common.util.buffer.BufferUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.Assume;
-import org.junit.BeforeClass;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runner.RunWith;
-import org.junit.runners.MethodSorters;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-import org.junit.runners.Parameterized.UseParametersRunnerFactory;
-
-import net.i2p.crypto.eddsa.EdDSAPrivateKey;
-import net.i2p.crypto.eddsa.EdDSAPublicKey;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- * @see <A HREF="https://tools.ietf.org/html/draft-josefsson-eddsa-ed25519-02#section-6">
- * EdDSA and Ed25519 draft-josefsson-eddsa-ed25519-02 - section 6 - Test Vectors for Ed25519</A>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@RunWith(Parameterized.class)   // see https://github.com/junit-team/junit/wiki/Parameterized-tests
-@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
-@Category({ NoIoTestCase.class })
-public class Ed25519VectorsTest extends BaseTestSupport {
-    private final byte[] prvBytes;
-    private final PrivateKey privateKey;
-    private final byte[] pubBytes;
-    private final PublicKey publicKey;
-    private final byte[] msgBytes;
-    private final byte[] expSignature;
-
-    public Ed25519VectorsTest(String name, String prvKey, String pubKey, String msg, String signature)
-            throws GeneralSecurityException {
-        prvBytes = BufferUtils.decodeHex(BufferUtils.EMPTY_HEX_SEPARATOR, prvKey);
-        privateKey = EdDSASecurityProviderUtils.generateEDDSAPrivateKey(prvBytes.clone());
-        pubBytes = BufferUtils.decodeHex(BufferUtils.EMPTY_HEX_SEPARATOR, pubKey);
-        publicKey = EdDSASecurityProviderUtils.generateEDDSAPublicKey(pubBytes.clone());
-        msgBytes = BufferUtils.decodeHex(BufferUtils.EMPTY_HEX_SEPARATOR, msg);
-        expSignature = BufferUtils.decodeHex(BufferUtils.EMPTY_HEX_SEPARATOR, signature);
-    }
-
-    @Parameters(name = "{0}")
-    @SuppressWarnings("checkstyle:anoninnerlength")
-    public static List<Object[]> parameters() {
-        return new ArrayList<>(Arrays.asList(
-                new Object[]{
-                    "TEST1 - empty message",
-                    "9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60",
-                    "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a",
-                    "",
-                    "e5564300c360ac729086e2cc806e828a"
-                      + "84877f1eb8e5d974d873e06522490155"
-                      + "5fb8821590a33bacc61e39701cf9b46b"
-                      + "d25bf5f0595bbe24655141438e7a100b"
-                },
-                new Object[]{
-                    "TEST2 - one byte",
-                    "4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb",
-                    "3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c",
-                    "72",
-                    "92a009a9f0d4cab8720e820b5f642540"
-                      + "a2b27b5416503f8fb3762223ebdb69da"
-                      + "085ac1e43e15996e458f3613d0f11d8c"
-                      + "387b2eaeb4302aeeb00d291612bb0c00"
-                },
-                new Object[]{
-                    "TEST3 - 2 bytes",
-                    "c5aa8df43f9f837bedb7442f31dcb7b166d38535076f094b85ce3a2e0b4458f7",
-                    "fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025",
-                    "af82",
-                    "6291d657deec24024827e69c3abe01a3"
-                      + "0ce548a284743a445e3680d7db5ac3ac"
-                      + "18ff9b538d16f290ae67f760984dc659"
-                      + "4a7c15e9716ed28dc027beceea1ec40a"
-                },
-                new Object[]{
-                    "TEST1024 - large message",
-                    "f5e5767cf153319517630f226876b86c8160cc583bc013744c6bf255f5cc0ee5",
-                    "278117fc144c72340f67d0f2316e8386ceffbf2b2428c9c51fef7c597f1d426e",
-                    "08b8b2b733424243760fe426a4b54908"
-                      + "632110a66c2f6591eabd3345e3e4eb98"
-                      + "fa6e264bf09efe12ee50f8f54e9f77b1"
-                      + "e355f6c50544e23fb1433ddf73be84d8"
-                      + "79de7c0046dc4996d9e773f4bc9efe57"
-                      + "38829adb26c81b37c93a1b270b20329d"
-                      + "658675fc6ea534e0810a4432826bf58c"
-                      + "941efb65d57a338bbd2e26640f89ffbc"
-                      + "1a858efcb8550ee3a5e1998bd177e93a"
-                      + "7363c344fe6b199ee5d02e82d522c4fe"
-                      + "ba15452f80288a821a579116ec6dad2b"
-                      + "3b310da903401aa62100ab5d1a36553e"
-                      + "06203b33890cc9b832f79ef80560ccb9"
-                      + "a39ce767967ed628c6ad573cb116dbef"
-                      + "efd75499da96bd68a8a97b928a8bbc10"
-                      + "3b6621fcde2beca1231d206be6cd9ec7"
-                      + "aff6f6c94fcd7204ed3455c68c83f4a4"
-                      + "1da4af2b74ef5c53f1d8ac70bdcb7ed1"
-                      + "85ce81bd84359d44254d95629e9855a9"
-                      + "4a7c1958d1f8ada5d0532ed8a5aa3fb2"
-                      + "d17ba70eb6248e594e1a2297acbbb39d"
-                      + "502f1a8c6eb6f1ce22b3de1a1f40cc24"
-                      + "554119a831a9aad6079cad88425de6bd"
-                      + "e1a9187ebb6092cf67bf2b13fd65f270"
-                      + "88d78b7e883c8759d2c4f5c65adb7553"
-                      + "878ad575f9fad878e80a0c9ba63bcbcc"
-                      + "2732e69485bbc9c90bfbd62481d9089b"
-                      + "eccf80cfe2df16a2cf65bd92dd597b07"
-                      + "07e0917af48bbb75fed413d238f5555a"
-                      + "7a569d80c3414a8d0859dc65a46128ba"
-                      + "b27af87a71314f318c782b23ebfe808b"
-                      + "82b0ce26401d2e22f04d83d1255dc51a"
-                      + "ddd3b75a2b1ae0784504df543af8969b"
-                      + "e3ea7082ff7fc9888c144da2af58429e"
-                      + "c96031dbcad3dad9af0dcbaaaf268cb8"
-                      + "fcffead94f3c7ca495e056a9b47acdb7"
-                      + "51fb73e666c6c655ade8297297d07ad1"
-                      + "ba5e43f1bca32301651339e22904cc8c"
-                      + "42f58c30c04aafdb038dda0847dd988d"
-                      + "cda6f3bfd15c4b4c4525004aa06eeff8"
-                      + "ca61783aacec57fb3d1f92b0fe2fd1a8"
-                      + "5f6724517b65e614ad6808d6f6ee34df"
-                      + "f7310fdc82aebfd904b01e1dc54b2927"
-                      + "094b2db68d6f903b68401adebf5a7e08"
-                      + "d78ff4ef5d63653a65040cf9bfd4aca7"
-                      + "984a74d37145986780fc0b16ac451649"
-                      + "de6188a7dbdf191f64b5fc5e2ab47b57"
-                      + "f7f7276cd419c17a3ca8e1b939ae49e4"
-                      + "88acba6b965610b5480109c8b17b80e1"
-                      + "b7b750dfc7598d5d5011fd2dcc5600a3"
-                      + "2ef5b52a1ecc820e308aa342721aac09"
-                      + "43bf6686b64b2579376504ccc493d97e"
-                      + "6aed3fb0f9cd71a43dd497f01f17c0e2"
-                      + "cb3797aa2a2f256656168e6c496afc5f"
-                      + "b93246f6b1116398a346f1a641f3b041"
-                      + "e989f7914f90cc2c7fff357876e506b5"
-                      + "0d334ba77c225bc307ba537152f3f161"
-                      + "0e4eafe595f6d9d90d11faa933a15ef1"
-                      + "369546868a7f3a45a96768d40fd9d034"
-                      + "12c091c6315cf4fde7cb68606937380d"
-                      + "b2eaaa707b4c4185c32eddcdd306705e"
-                      + "4dc1ffc872eeee475a64dfac86aba41c"
-                      + "0618983f8741c5ef68d3a101e8a3b8ca"
-                      + "c60c905c15fc910840b94c00a0b9d0",
-                    "0aab4c900501b3e24d7cdf4663326a3a"
-                      + "87df5e4843b2cbdb67cbf6e460fec350"
-                      + "aa5371b1508f9f4528ecea23c436d94b"
-                      + "5e8fcd4f681e30a6ac00a9704a188a03"
-                }));
-    }
-
-    @BeforeClass
-    public static void checkEDDSASupported() {
-        Assume.assumeTrue("EDDSA N/A", SecurityUtils.isEDDSACurveSupported());
-    }
-
-    @Test
-    public void testPublicKeyBytes() {
-        byte[] publicSeed = Ed25519PublicKeyDecoder.getSeedValue((EdDSAPublicKey) publicKey);
-        assertArrayEquals("Mismatched public seed value", pubBytes, publicSeed);
-    }
-
-    @Test
-    public void testPrivateKeyBytes() {
-        assertArrayEquals("Mismatched private seed value", prvBytes, ((EdDSAPrivateKey) privateKey).getSeed());
-    }
-
-    @Test
-    public void testSignature() throws Exception {
-        Signature signer = EdDSASecurityProviderUtils.getEDDSASignature();
-        signer.initSigner(privateKey);
-        signer.update(msgBytes.clone());
-
-        byte[] actSignature = signer.sign();
-        assertArrayEquals("Mismatched signature", expSignature, actSignature);
-
-        Signature verifier = EdDSASecurityProviderUtils.getEDDSASignature();
-        verifier.initVerifier(publicKey);
-        verifier.update(msgBytes.clone());
-        assertTrue("Verification failed", verifier.verify(expSignature));
-    }
-
-    @Test
-    public void testPartialBufferSignature() throws Exception {
-        byte[] extraData = getCurrentTestName().getBytes(StandardCharsets.UTF_8);
-        byte[] dataBuf = new byte[msgBytes.length + extraData.length];
-        int offset = extraData.length / 2;
-        System.arraycopy(extraData, 0, dataBuf, 0, offset);
-        System.arraycopy(msgBytes, 0, dataBuf, offset, msgBytes.length);
-        System.arraycopy(extraData, offset, dataBuf, offset + msgBytes.length, extraData.length - offset);
-
-        Signature signer = EdDSASecurityProviderUtils.getEDDSASignature();
-        signer.initSigner(privateKey);
-        signer.update(dataBuf.clone(), offset, msgBytes.length);
-
-        byte[] actSignature = signer.sign();
-        assertArrayEquals("Mismatched signature", expSignature, actSignature);
-
-        Signature verifier = EdDSASecurityProviderUtils.getEDDSASignature();
-        verifier.initVerifier(publicKey);
-        verifier.update(dataBuf.clone(), offset, msgBytes.length);
-        assertTrue("Verification failed", verifier.verify(expSignature));
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderRegistrarTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderRegistrarTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderRegistrarTest.java
deleted file mode 100644
index c2ce550..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderRegistrarTest.java
+++ /dev/null
@@ -1,85 +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.security.eddsa;
-
-import java.security.KeyFactory;
-import java.security.KeyPairGenerator;
-import java.security.Provider;
-import java.security.Signature;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.HashSet;
-
-import org.apache.sshd.common.util.security.SecurityProviderRegistrar;
-import org.apache.sshd.common.util.security.SecurityProviderRegistrarTestSupport;
-import org.apache.sshd.common.util.security.SecurityUtils;
-import org.junit.Assume;
-import org.junit.BeforeClass;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.runners.MethodSorters;
-
-import net.i2p.crypto.eddsa.EdDSASecurityProvider;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-public class EdDSASecurityProviderRegistrarTest extends SecurityProviderRegistrarTestSupport {
-    private static SecurityProviderRegistrar registrarInstance;
-
-    public EdDSASecurityProviderRegistrarTest() {
-        super();
-    }
-
-    @BeforeClass
-    public static void checkEDDSASupported() {
-        Assume.assumeTrue(SecurityUtils.isEDDSACurveSupported());
-        registrarInstance = new EdDSASecurityProviderRegistrar();
-    }
-
-    @Test
-    public void testSupportedSecurityEntities() {
-        assertSecurityEntitySupportState(getCurrentTestName(), registrarInstance, true, registrarInstance.getName(),
-                KeyPairGenerator.class, KeyFactory.class);
-        assertSecurityEntitySupportState(getCurrentTestName(), registrarInstance, true,
-                SecurityUtils.CURVE_ED25519_SHA512, Signature.class);
-
-        Collection<Class<?>> supported = new HashSet<>(Arrays.asList(KeyPairGenerator.class, KeyFactory.class, Signature.class));
-        for (Class<?> entity : SecurityProviderRegistrar.SECURITY_ENTITIES) {
-            if (supported.contains(entity)) {
-                continue;
-            }
-            assertFalse("Unexpected support for " + entity.getSimpleName(), registrarInstance.isSecurityEntitySupported(entity, registrarInstance.getName()));
-        }
-    }
-
-    @Test
-    public void testGetSecurityProvider() {
-        Provider expected = registrarInstance.getSecurityProvider();
-        assertNotNull("No provider created", expected);
-        assertEquals("Mismatched provider name", registrarInstance.getName(), expected.getName());
-        assertObjectInstanceOf("Mismatched provider type", EdDSASecurityProvider.class, expected);
-    }
-
-    @Test
-    public void testGetSecurityProviderCaching() {
-        testGetSecurityProviderCaching(getCurrentTestName(), registrarInstance);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/server/ServerSessionListenerTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/server/ServerSessionListenerTest.java b/sshd-core/src/test/java/org/apache/sshd/server/ServerSessionListenerTest.java
index 01c96b4..50d83ed 100644
--- a/sshd-core/src/test/java/org/apache/sshd/server/ServerSessionListenerTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/server/ServerSessionListenerTest.java
@@ -44,7 +44,7 @@ import org.apache.sshd.server.auth.keyboard.KeyboardInteractiveAuthenticator;
 import org.apache.sshd.server.auth.password.PasswordAuthenticator;
 import org.apache.sshd.server.session.ServerSession;
 import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.Utils;
+import org.apache.sshd.util.test.CoreTestSupportUtils;
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
 import org.junit.FixMethodOrder;
@@ -68,11 +68,11 @@ public class ServerSessionListenerTest extends BaseTestSupport {
 
     @BeforeClass
     public static void setupClientAndServer() throws Exception {
-        sshd = Utils.setupTestServer(ServerSessionListenerTest.class);
+        sshd = CoreTestSupportUtils.setupTestServer(ServerSessionListenerTest.class);
         sshd.start();
         port = sshd.getPort();
 
-        client = Utils.setupTestClient(ServerSessionListenerTest.class);
+        client = CoreTestSupportUtils.setupTestClient(ServerSessionListenerTest.class);
         client.start();
     }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/server/auth/WelcomeBannerPhaseTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/server/auth/WelcomeBannerPhaseTest.java b/sshd-core/src/test/java/org/apache/sshd/server/auth/WelcomeBannerPhaseTest.java
index 35f032b..bcb5311 100644
--- a/sshd-core/src/test/java/org/apache/sshd/server/auth/WelcomeBannerPhaseTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/server/auth/WelcomeBannerPhaseTest.java
@@ -30,8 +30,8 @@ import org.apache.sshd.common.PropertyResolverUtils;
 import org.apache.sshd.server.ServerAuthenticationManager;
 import org.apache.sshd.server.SshServer;
 import org.apache.sshd.util.test.BaseTestSupport;
+import org.apache.sshd.util.test.CoreTestSupportUtils;
 import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
-import org.apache.sshd.util.test.Utils;
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
 import org.junit.FixMethodOrder;
@@ -66,11 +66,11 @@ public class WelcomeBannerPhaseTest extends BaseTestSupport {
 
     @BeforeClass
     public static void setupClientAndServer() throws Exception {
-        sshd = Utils.setupTestServer(WelcomeBannerPhaseTest.class);
+        sshd = CoreTestSupportUtils.setupTestServer(WelcomeBannerPhaseTest.class);
         sshd.start();
         port = sshd.getPort();
 
-        client = Utils.setupTestClient(WelcomeBannerPhaseTest.class);
+        client = CoreTestSupportUtils.setupTestClient(WelcomeBannerPhaseTest.class);
         client.start();
     }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/server/auth/WelcomeBannerTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/server/auth/WelcomeBannerTest.java b/sshd-core/src/test/java/org/apache/sshd/server/auth/WelcomeBannerTest.java
index 04bf7d6..e417aa9 100644
--- a/sshd-core/src/test/java/org/apache/sshd/server/auth/WelcomeBannerTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/server/auth/WelcomeBannerTest.java
@@ -39,7 +39,7 @@ import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.server.ServerAuthenticationManager;
 import org.apache.sshd.server.SshServer;
 import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.Utils;
+import org.apache.sshd.util.test.CoreTestSupportUtils;
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
 import org.junit.FixMethodOrder;
@@ -61,11 +61,11 @@ public class WelcomeBannerTest extends BaseTestSupport {
 
     @BeforeClass
     public static void setupClientAndServer() throws Exception {
-        sshd = Utils.setupTestServer(WelcomeBannerTest.class);
+        sshd = CoreTestSupportUtils.setupTestServer(WelcomeBannerTest.class);
         sshd.start();
         port = sshd.getPort();
 
-        client = Utils.setupTestClient(WelcomeBannerTest.class);
+        client = CoreTestSupportUtils.setupTestClient(WelcomeBannerTest.class);
         client.start();
     }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/server/keyprovider/AbstractGeneratorHostKeyProviderTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/server/keyprovider/AbstractGeneratorHostKeyProviderTest.java b/sshd-core/src/test/java/org/apache/sshd/server/keyprovider/AbstractGeneratorHostKeyProviderTest.java
deleted file mode 100644
index 7a2d326..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/server/keyprovider/AbstractGeneratorHostKeyProviderTest.java
+++ /dev/null
@@ -1,83 +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.server.keyprovider;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class AbstractGeneratorHostKeyProviderTest extends BaseTestSupport {
-    public AbstractGeneratorHostKeyProviderTest() {
-        super();
-    }
-
-    @SuppressWarnings("synthetic-access")
-    @Test
-    public void testOverwriteKey() throws Exception {
-        Path tempDir = assertHierarchyTargetFolderExists(getTempTargetFolder());
-        Path keyPairFile = tempDir.resolve(getCurrentTestName() + ".key");
-        Files.deleteIfExists(keyPairFile);
-
-        TestProvider provider = new TestProvider(keyPairFile);
-        provider.loadKeys();
-        assertEquals("Mismatched generate write count", 1, provider.getWriteCount());
-
-        provider = new TestProvider(keyPairFile);
-        provider.setOverwriteAllowed(false);
-        provider.loadKeys();
-        assertEquals("Mismatched load write count", 0, provider.getWriteCount());
-    }
-
-    private static final class TestProvider extends AbstractGeneratorHostKeyProvider {
-        private final AtomicInteger writes = new AtomicInteger(0);
-
-        private TestProvider(Path file) {
-            setKeySize(512);
-            setPath(file);
-        }
-
-        @Override
-        protected KeyPair doReadKeyPair(String resourceKey, InputStream inputStream) throws IOException, GeneralSecurityException {
-            return null;
-        }
-
-        @Override
-        protected void doWriteKeyPair(String resourceKey, KeyPair kp, OutputStream outputStream) throws IOException, GeneralSecurityException {
-            writes.incrementAndGet();
-        }
-
-        public int getWriteCount() {
-            return writes.get();
-        }
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/server/keyprovider/PEMGeneratorHostKeyProviderTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/server/keyprovider/PEMGeneratorHostKeyProviderTest.java b/sshd-core/src/test/java/org/apache/sshd/server/keyprovider/PEMGeneratorHostKeyProviderTest.java
deleted file mode 100644
index 620033c..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/server/keyprovider/PEMGeneratorHostKeyProviderTest.java
+++ /dev/null
@@ -1,141 +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.server.keyprovider;
-
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.security.KeyPair;
-import java.security.PublicKey;
-import java.security.interfaces.ECPublicKey;
-import java.security.spec.AlgorithmParameterSpec;
-import java.security.spec.ECGenParameterSpec;
-
-import org.apache.sshd.common.cipher.ECCurves;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.Assume;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class PEMGeneratorHostKeyProviderTest extends BaseTestSupport {
-    public PEMGeneratorHostKeyProviderTest() {
-        super();
-    }
-
-    @Test
-    public void testDSA() throws IOException {
-        Assume.assumeTrue("BouncyCastle not registered", SecurityUtils.isBouncyCastleRegistered());
-        testPEMGeneratorHostKeyProvider(KeyUtils.DSS_ALGORITHM, KeyPairProvider.SSH_DSS, 512, null);
-    }
-
-    @Test
-    public void testRSA() throws IOException {
-        Assume.assumeTrue("BouncyCastle not registered", SecurityUtils.isBouncyCastleRegistered());
-        testPEMGeneratorHostKeyProvider(KeyUtils.RSA_ALGORITHM, KeyPairProvider.SSH_RSA, 512, null);
-    }
-
-    @Test
-    public void testECnistp256() throws IOException {
-        Assume.assumeTrue("BouncyCastle not registered", SecurityUtils.isBouncyCastleRegistered());
-        Assume.assumeTrue("ECC not supported", SecurityUtils.isECCSupported());
-        Assume.assumeTrue(ECCurves.nistp256 + " N/A", ECCurves.nistp256.isSupported());
-        testPEMGeneratorHostKeyProvider(KeyUtils.EC_ALGORITHM, KeyPairProvider.ECDSA_SHA2_NISTP256, -1, new ECGenParameterSpec("prime256v1"));
-    }
-
-    @Test
-    public void testECnistp384() throws IOException {
-        Assume.assumeTrue("BouncyCastle not registered", SecurityUtils.isBouncyCastleRegistered());
-        Assume.assumeTrue("ECC not supported", SecurityUtils.isECCSupported());
-        Assume.assumeTrue(ECCurves.nistp384 + " N/A", ECCurves.nistp384.isSupported());
-        testPEMGeneratorHostKeyProvider(KeyUtils.EC_ALGORITHM, KeyPairProvider.ECDSA_SHA2_NISTP384, -1, new ECGenParameterSpec("P-384"));
-    }
-
-    @Test
-    public void testECnistp521() throws IOException {
-        Assume.assumeTrue("BouncyCastle not registered", SecurityUtils.isBouncyCastleRegistered());
-        Assume.assumeTrue("ECC not supported", SecurityUtils.isECCSupported());
-        Assume.assumeTrue(ECCurves.nistp521 + " N/A", ECCurves.nistp521.isSupported());
-        testPEMGeneratorHostKeyProvider(KeyUtils.EC_ALGORITHM, KeyPairProvider.ECDSA_SHA2_NISTP521, -1, new ECGenParameterSpec("P-521"));
-    }
-
-    private Path testPEMGeneratorHostKeyProvider(String algorithm, String keyType, int keySize, AlgorithmParameterSpec keySpec) throws IOException {
-        Path path = initKeyFileLocation(algorithm);
-        KeyPair kpWrite = invokePEMGeneratorHostKeyProvider(path, algorithm, keyType, keySize, keySpec);
-        assertTrue("Key file not generated: " + path, Files.exists(path, IoUtils.EMPTY_LINK_OPTIONS));
-
-        KeyPair kpRead = invokePEMGeneratorHostKeyProvider(path, algorithm, keyType, keySize, keySpec);
-        PublicKey pubWrite = kpWrite.getPublic();
-        PublicKey pubRead = kpRead.getPublic();
-        if (pubWrite instanceof ECPublicKey) {
-            // The algorithm is reported as ECDSA instead of EC
-            assertECPublicKeyEquals("Mismatched EC public key", ECPublicKey.class.cast(pubWrite), ECPublicKey.class.cast(pubRead));
-        } else {
-            assertKeyEquals("Mismatched public keys", pubWrite, pubRead);
-        }
-        return path;
-    }
-
-    private static KeyPair invokePEMGeneratorHostKeyProvider(Path path, String algorithm, String keyType, int keySize, AlgorithmParameterSpec keySpec) {
-        AbstractGeneratorHostKeyProvider provider = SecurityUtils.createGeneratorHostKeyProvider(path.toAbsolutePath().normalize());
-        provider.setAlgorithm(algorithm);
-        provider.setOverwriteAllowed(true);
-        if (keySize > 0) {
-            provider.setKeySize(keySize);
-        }
-        if (keySpec != null) {
-            provider.setKeySpec(keySpec);
-        }
-
-        return validateKeyPairProvider(provider, keyType);
-    }
-
-    private static KeyPair validateKeyPairProvider(KeyPairProvider provider, String keyType) {
-        Iterable<String> types = provider.getKeyTypes();
-        KeyPair kp = null;
-        for (String type : types) {
-            if (keyType.equals(type)) {
-                kp = provider.loadKey(keyType);
-                assertNotNull("Failed to load key for " + keyType, kp);
-                break;
-            }
-        }
-
-        assertNotNull("Expected key type not found: " + keyType, kp);
-        return kp;
-    }
-
-    private Path initKeyFileLocation(String algorithm) throws IOException {
-        Path path = assertHierarchyTargetFolderExists(getTempTargetRelativeFile(getClass().getSimpleName()));
-        path = path.resolve(algorithm + "-PEM.key");
-        Files.deleteIfExists(path);
-        return path;
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProviderTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProviderTest.java b/sshd-core/src/test/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProviderTest.java
deleted file mode 100644
index 1ac3ef9..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProviderTest.java
+++ /dev/null
@@ -1,133 +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.server.keyprovider;
-
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.security.KeyPair;
-import java.security.spec.AlgorithmParameterSpec;
-import java.security.spec.ECGenParameterSpec;
-
-import org.apache.sshd.common.cipher.ECCurves;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.Assume;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * TODO Add javadoc
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class SimpleGeneratorHostKeyProviderTest extends BaseTestSupport {
-    public SimpleGeneratorHostKeyProviderTest() {
-        super();
-    }
-
-    @Test
-    public void testDSA() throws IOException {
-        testSimpleGeneratorHostKeyProvider(KeyUtils.DSS_ALGORITHM, KeyPairProvider.SSH_DSS, 512, null);
-    }
-
-    @Test
-    public void testRSA() throws IOException {
-        testSimpleGeneratorHostKeyProvider(KeyUtils.RSA_ALGORITHM, KeyPairProvider.SSH_RSA, 512, null);
-    }
-
-    @Test
-    public void testECnistp256() throws IOException {
-        Assume.assumeTrue("BouncyCastle not registered", SecurityUtils.isBouncyCastleRegistered());
-        Assume.assumeTrue("ECC not supported", SecurityUtils.isECCSupported());
-        Assume.assumeTrue(ECCurves.nistp256 + " N/A", ECCurves.nistp256.isSupported());
-        testSimpleGeneratorHostKeyProvider(KeyUtils.EC_ALGORITHM, KeyPairProvider.ECDSA_SHA2_NISTP256, -1, new ECGenParameterSpec("prime256v1"));
-    }
-
-    @Test
-    public void testECnistp384() throws IOException {
-        Assume.assumeTrue("BouncyCastle not registered", SecurityUtils.isBouncyCastleRegistered());
-        Assume.assumeTrue("ECC not supported", SecurityUtils.isECCSupported());
-        Assume.assumeTrue(ECCurves.nistp384 + " N/A", ECCurves.nistp384.isSupported());
-        testSimpleGeneratorHostKeyProvider(KeyUtils.EC_ALGORITHM, KeyPairProvider.ECDSA_SHA2_NISTP384, -1, new ECGenParameterSpec("P-384"));
-    }
-
-    @Test
-    public void testECnistp521() throws IOException {
-        Assume.assumeTrue("BouncyCastle not registered", SecurityUtils.isBouncyCastleRegistered());
-        Assume.assumeTrue("ECC not supported", SecurityUtils.isECCSupported());
-        Assume.assumeTrue(ECCurves.nistp521 + " N/A", ECCurves.nistp521.isSupported());
-        testSimpleGeneratorHostKeyProvider(KeyUtils.EC_ALGORITHM, KeyPairProvider.ECDSA_SHA2_NISTP521, -1, new ECGenParameterSpec("P-521"));
-    }
-
-    private Path testSimpleGeneratorHostKeyProvider(String algorithm, String keyType, int keySize, AlgorithmParameterSpec keySpec) throws IOException {
-        Path path = initKeyFileLocation(algorithm);
-        KeyPair kpWrite = invokeSimpleGeneratorHostKeyProvider(path, algorithm, keyType, keySize, keySpec);
-        assertTrue("Key file not generated: " + path, Files.exists(path, IoUtils.EMPTY_LINK_OPTIONS));
-
-        KeyPair kpRead = invokeSimpleGeneratorHostKeyProvider(path, algorithm, keyType, keySize, keySpec);
-        assertKeyPairEquals("Mismatched write/read key pairs", kpWrite, kpRead);
-        return path;
-    }
-
-    private static KeyPair invokeSimpleGeneratorHostKeyProvider(Path path, String algorithm, String keyType, int keySize, AlgorithmParameterSpec keySpec) {
-        SimpleGeneratorHostKeyProvider provider = new SimpleGeneratorHostKeyProvider();
-        provider.setAlgorithm(algorithm);
-        provider.setOverwriteAllowed(true);
-        provider.setPath(path);
-        if (keySize > 0) {
-            provider.setKeySize(keySize);
-        }
-        if (keySpec != null) {
-            provider.setKeySpec(keySpec);
-        }
-
-        return validateKeyPairProvider(provider, keyType);
-    }
-
-    private static KeyPair validateKeyPairProvider(KeyPairProvider provider, String keyType) {
-        Iterable<String> types = provider.getKeyTypes();
-        KeyPair kp = null;
-        for (String type : types) {
-            if (keyType.equals(type)) {
-                kp = provider.loadKey(keyType);
-                assertNotNull("Failed to load key for " + keyType, kp);
-                break;
-            }
-        }
-
-        assertNotNull("Expected key type not found: " + keyType, kp);
-        return kp;
-    }
-
-    private Path initKeyFileLocation(String algorithm) throws IOException {
-        Path path = assertHierarchyTargetFolderExists(getTempTargetRelativeFile(getClass().getSimpleName()));
-        path = path.resolve(algorithm + "-simple.key");
-        Files.deleteIfExists(path);
-        return path;
-    }
-}


[23/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common utilities code from sshd-core into sshd-common (new artifact)

Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/SshConfigFileReader.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/SshConfigFileReader.java b/sshd-core/src/main/java/org/apache/sshd/common/config/SshConfigFileReader.java
index c26a2d1..46f34a1 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/SshConfigFileReader.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/config/SshConfigFileReader.java
@@ -19,25 +19,11 @@
 
 package org.apache.sshd.common.config;
 
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.io.StreamCorruptedException;
-import java.net.URL;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.OpenOption;
-import java.nio.file.Path;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
 import java.util.Properties;
-import java.util.concurrent.TimeUnit;
 import java.util.function.Function;
 
 import org.apache.sshd.common.BuiltinFactory;
@@ -52,17 +38,12 @@ import org.apache.sshd.common.helpers.AbstractFactoryManager;
 import org.apache.sshd.common.kex.BuiltinDHFactories;
 import org.apache.sshd.common.kex.DHFactory;
 import org.apache.sshd.common.kex.KeyExchange;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
 import org.apache.sshd.common.mac.BuiltinMacs;
 import org.apache.sshd.common.mac.Mac;
 import org.apache.sshd.common.signature.BuiltinSignatures;
 import org.apache.sshd.common.signature.Signature;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.common.util.io.NoCloseInputStream;
-import org.apache.sshd.common.util.io.NoCloseReader;
-import org.apache.sshd.common.util.net.SshdSocketAddress;
 
 /**
  * Reads and interprets some useful configurations from an OpenSSH
@@ -72,187 +53,10 @@ import org.apache.sshd.common.util.net.SshdSocketAddress;
  * @see <a href="https://www.freebsd.org/cgi/man.cgi?query=ssh_config&sektion=5">ssh_config(5)</a>
  */
 public final class SshConfigFileReader {
-
-    public static final char COMMENT_CHAR = '#';
-
-    public static final String COMPRESSION_PROP = "Compression";
-    public static final String DEFAULT_COMPRESSION = CompressionConfigValue.NO.getName();
-    public static final String MAX_SESSIONS_CONFIG_PROP = "MaxSessions";
-    public static final int DEFAULT_MAX_SESSIONS = 10;
-    public static final String PASSWORD_AUTH_CONFIG_PROP = "PasswordAuthentication";
-    public static final String DEFAULT_PASSWORD_AUTH = "no";
-    public static final boolean DEFAULT_PASSWORD_AUTH_VALUE = parseBooleanValue(DEFAULT_PASSWORD_AUTH);
-    public static final String LISTEN_ADDRESS_CONFIG_PROP = "ListenAddress";
-    public static final String DEFAULT_BIND_ADDRESS = SshdSocketAddress.IPV4_ANYADDR;
-    public static final String PORT_CONFIG_PROP = "Port";
-    public static final int DEFAULT_PORT = 22;
-    public static final String KEEP_ALIVE_CONFIG_PROP = "TCPKeepAlive";
-    public static final boolean DEFAULT_KEEP_ALIVE = true;
-    public static final String USE_DNS_CONFIG_PROP = "UseDNS";
-    // NOTE: the usual default is TRUE
-    public static final boolean DEFAULT_USE_DNS = true;
-    public static final String PUBKEY_AUTH_CONFIG_PROP = "PubkeyAuthentication";
-    public static final String DEFAULT_PUBKEY_AUTH = "yes";
-    public static final boolean DEFAULT_PUBKEY_AUTH_VALUE = parseBooleanValue(DEFAULT_PUBKEY_AUTH);
-    public static final String AUTH_KEYS_FILE_CONFIG_PROP = "AuthorizedKeysFile";
-    public static final String MAX_AUTH_TRIES_CONFIG_PROP = "MaxAuthTries";
-    public static final int DEFAULT_MAX_AUTH_TRIES = 6;
-    public static final String MAX_STARTUPS_CONFIG_PROP = "MaxStartups";
-    public static final int DEFAULT_MAX_STARTUPS = 10;
-    public static final String LOGIN_GRACE_TIME_CONFIG_PROP = "LoginGraceTime";
-    public static final long DEFAULT_LOGIN_GRACE_TIME = TimeUnit.SECONDS.toMillis(120);
-    public static final String KEY_REGENERATE_INTERVAL_CONFIG_PROP = "KeyRegenerationInterval";
-    public static final long DEFAULT_REKEY_TIME_LIMIT = TimeUnit.HOURS.toMillis(1L);
-    // see http://manpages.ubuntu.com/manpages/precise/en/man5/sshd_config.5.html
-    public static final String CIPHERS_CONFIG_PROP = "Ciphers";
-    public static final String DEFAULT_CIPHERS =
-            "aes128-ctr,aes192-ctr,aes256-ctr,arcfour256,arcfour128,aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc,aes192-cbc,aes256-cbc,arcfour";
-    // see http://manpages.ubuntu.com/manpages/precise/en/man5/sshd_config.5.html
-    public static final String MACS_CONFIG_PROP = "MACs";
-    public static final String DEFAULT_MACS =
-            "hmac-md5,hmac-sha1,umac-64@openssh.com,hmac-ripemd160,hmac-sha1-96,hmac-md5-96,hmac-sha2-256,hmac-sha2-256-96,hmac-sha2-512,hmac-sha2-512-96";
-    // see http://manpages.ubuntu.com/manpages/precise/en/man5/sshd_config.5.html
-    public static final String KEX_ALGORITHMS_CONFIG_PROP = "KexAlgorithms";
-    public static final String DEFAULT_KEX_ALGORITHMS =
-            "ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521"
-                    + "," + "diffie-hellman-group-exchange-sha256,diffie-hellman-group-exchange-sha1"
-                    // RFC-8268 groups
-                    + "," + "diffie-hellman-group18-sha512,diffie-hellman-group17-sha512"
-                    + "," + "diffie-hellman-group16-sha512,diffie-hellman-group15-sha512"
-                    + "," + "diffie-hellman-group14-sha256"
-                    // Legacy groups
-                    + "," + "diffie-hellman-group14-sha1,diffie-hellman-group1-sha1";
-    // see http://linux.die.net/man/5/ssh_config
-    public static final String HOST_KEY_ALGORITHMS_CONFIG_PROP = "HostKeyAlgorithms";
-    // see https://tools.ietf.org/html/rfc5656
-    public static final String DEFAULT_HOST_KEY_ALGORITHMS =
-            KeyPairProvider.SSH_RSA + "," + KeyPairProvider.SSH_DSS;
-    // see http://manpages.ubuntu.com/manpages/precise/en/man5/sshd_config.5.html
-    public static final String LOG_LEVEL_CONFIG_PROP = "LogLevel";
-    public static final LogLevelValue DEFAULT_LOG_LEVEL = LogLevelValue.INFO;
-    // see https://www.freebsd.org/cgi/man.cgi?query=sshd_config&sektion=5
-    public static final String SYSLOG_FACILITY_CONFIG_PROP = "SyslogFacility";
-    public static final SyslogFacilityValue DEFAULT_SYSLOG_FACILITY = SyslogFacilityValue.AUTH;
-    public static final String SUBSYSTEM_CONFIG_PROP = "Subsystem";
-
     private SshConfigFileReader() {
         throw new UnsupportedOperationException("No instance allowed");
     }
 
-    public static Properties readConfigFile(File file) throws IOException {
-        return readConfigFile(file.toPath(), IoUtils.EMPTY_OPEN_OPTIONS);
-    }
-
-    public static Properties readConfigFile(Path path, OpenOption... options) throws IOException {
-        try (InputStream input = Files.newInputStream(path, options)) {
-            return readConfigFile(input, true);
-        }
-    }
-
-    public static Properties readConfigFile(URL url) throws IOException {
-        try (InputStream input = url.openStream()) {
-            return readConfigFile(input, true);
-        }
-    }
-
-    public static Properties readConfigFile(String path) throws IOException {
-        try (InputStream input = new FileInputStream(path)) {
-            return readConfigFile(input, true);
-        }
-    }
-
-    public static Properties readConfigFile(InputStream input, boolean okToClose) throws IOException {
-        try (Reader reader = new InputStreamReader(NoCloseInputStream.resolveInputStream(input, okToClose), StandardCharsets.UTF_8)) {
-            return readConfigFile(reader, true);
-        }
-    }
-
-    public static Properties readConfigFile(Reader reader, boolean okToClose) throws IOException {
-        try (BufferedReader buf = new BufferedReader(NoCloseReader.resolveReader(reader, okToClose))) {
-            return readConfigFile(buf);
-        }
-    }
-
-    /**
-     * Reads the configuration file contents into a {@link Properties} instance.
-     * <B>Note:</B> multiple keys value are concatenated using a comma - it is up to
-     * the caller to know which keys are expected to have multiple values and handle
-     * the split accordingly
-     *
-     * @param rdr The {@link BufferedReader} for reading the file
-     * @return The read properties
-     * @throws IOException If failed to read or malformed content
-     */
-    public static Properties readConfigFile(BufferedReader rdr) throws IOException {
-        Properties props = new Properties();
-        int lineNumber = 1;
-        for (String line = rdr.readLine(); line != null; line = rdr.readLine(), lineNumber++) {
-            line = GenericUtils.replaceWhitespaceAndTrim(line);
-            if (GenericUtils.isEmpty(line)) {
-                continue;
-            }
-
-            int pos = line.indexOf(COMMENT_CHAR);
-            if (pos == 0) {
-                continue;
-            }
-
-            if (pos > 0) {
-                line = line.substring(0, pos);
-                line = line.trim();
-            }
-
-            /*
-             * Some options use '=', others use ' ' - try both
-             * NOTE: we do not validate the format for each option separately
-             */
-            pos = line.indexOf(' ');
-            if (pos < 0) {
-                pos = line.indexOf('=');
-            }
-
-            if (pos < 0) {
-                throw new StreamCorruptedException("No delimiter at line " + lineNumber + ": " + line);
-            }
-
-            String key = line.substring(0, pos);
-            String value = line.substring(pos + 1).trim();
-            // see if need to concatenate multi-valued keys
-            String prev = props.getProperty(key);
-            if (!GenericUtils.isEmpty(prev)) {
-                value = prev + "," + value;
-            }
-
-            props.setProperty(key, value);
-        }
-
-        return props;
-    }
-
-    /**
-     * @param v Checks if the value is &quot;yes&quot;, &quot;y&quot;
-     *          or &quot;on&quot; or &quot;true&quot;.
-     * @return The result - <B>Note:</B> {@code null}/empty values are
-     * interpreted as {@code false}
-     */
-    public static boolean parseBooleanValue(String v) {
-        return "yes".equalsIgnoreCase(v)
-                || "y".equalsIgnoreCase(v)
-                || "on".equalsIgnoreCase(v)
-                || "true".equalsIgnoreCase(v);
-    }
-
-    /**
-     * Returns a &quot;yes&quot; or &quot;no&quot; value based on the input
-     * parameter
-     *
-     * @param flag The required state
-     * @return &quot;yes&quot; if {@code true}, &quot;no&quot; otherwise
-     */
-    public static String yesNoValueOf(boolean flag) {
-        return flag ? "yes" : "no";
-    }
-
     /**
      * @param props The {@link PropertyResolver} - ignored if {@code null}/empty
      * @return A {@code ParseResult} of all the {@link NamedFactory}-ies
@@ -261,11 +65,12 @@ public final class SshConfigFileReader {
      * is the same as the original order - bar the unknown ciphers.
      * <B>Note:</B> it is up to caller to ensure that the lists do not
      * contain duplicates
-     * @see #CIPHERS_CONFIG_PROP
+     * @see ConfigFileReaderSupport#CIPHERS_CONFIG_PROP CIPHERS_CONFIG_PROP
      * @see BuiltinCiphers#parseCiphersList(String)
      */
     public static BuiltinCiphers.ParseResult getCiphers(PropertyResolver props) {
-        return BuiltinCiphers.parseCiphersList((props == null) ? null : props.getString(CIPHERS_CONFIG_PROP));
+        return BuiltinCiphers.parseCiphersList(
+            (props == null) ? null : props.getString(ConfigFileReaderSupport.CIPHERS_CONFIG_PROP));
     }
 
     /**
@@ -276,11 +81,12 @@ public final class SshConfigFileReader {
      * is the same as the original order - bar the unknown MACs.
      * <B>Note:</B> it is up to caller to ensure that the list does not
      * contain duplicates
-     * @see #MACS_CONFIG_PROP
+     * @see ConfigFileReaderSupport#MACS_CONFIG_PROP MACS_CONFIG_PROP
      * @see BuiltinMacs#parseMacsList(String)
      */
     public static BuiltinMacs.ParseResult getMacs(PropertyResolver props) {
-        return BuiltinMacs.parseMacsList((props == null) ? null : props.getString(MACS_CONFIG_PROP));
+        return BuiltinMacs.parseMacsList(
+            (props == null) ? null : props.getString(ConfigFileReaderSupport.MACS_CONFIG_PROP));
     }
 
     /**
@@ -290,11 +96,12 @@ public final class SshConfigFileReader {
      * unknown name is <U>ignored</U>. The order of the returned result is the
      * same as the original order - bar the unknown signatures. <B>Note:</B> it
      * is up to caller to ensure that the list does not contain duplicates
-     * @see #HOST_KEY_ALGORITHMS_CONFIG_PROP
+     * @see ConfigFileReaderSupport#HOST_KEY_ALGORITHMS_CONFIG_PROP HOST_KEY_ALGORITHMS_CONFIG_PROP
      * @see BuiltinSignatures#parseSignatureList(String)
      */
     public static BuiltinSignatures.ParseResult getSignatures(PropertyResolver props) {
-        return BuiltinSignatures.parseSignatureList((props == null) ? null : props.getString(HOST_KEY_ALGORITHMS_CONFIG_PROP));
+        return BuiltinSignatures.parseSignatureList(
+            (props == null) ? null : props.getString(ConfigFileReaderSupport.HOST_KEY_ALGORITHMS_CONFIG_PROP));
     }
 
     /**
@@ -304,30 +111,33 @@ public final class SshConfigFileReader {
      * unknown name is <U>ignored</U>. The order of the returned result is the
      * same as the original order - bar the unknown ones. <B>Note:</B> it is
      * up to caller to ensure that the list does not contain duplicates
-     * @see #KEX_ALGORITHMS_CONFIG_PROP
+     * @see ConfigFileReaderSupport#KEX_ALGORITHMS_CONFIG_PROP KEX_ALGORITHMS_CONFIG_PROP
      * @see BuiltinDHFactories#parseDHFactoriesList(String)
      */
     public static BuiltinDHFactories.ParseResult getKexFactories(PropertyResolver props) {
-        return BuiltinDHFactories.parseDHFactoriesList((props == null) ? null : props.getString(KEX_ALGORITHMS_CONFIG_PROP));
+        return BuiltinDHFactories.parseDHFactoriesList(
+            (props == null) ? null : props.getString(ConfigFileReaderSupport.KEX_ALGORITHMS_CONFIG_PROP));
     }
 
     /**
      * @param props The {@link PropertyResolver} - ignored if {@code null}/empty
      * @return The matching {@link NamedFactory} for the configured value.
      * {@code null} if no configuration or unknown name specified
+     * @see ConfigFileReaderSupport#COMPRESSION_PROP COMPRESSION_PROP
      */
     public static CompressionFactory getCompression(PropertyResolver props) {
-        return CompressionConfigValue.fromName((props == null) ? null : props.getString(COMPRESSION_PROP));
+        return CompressionConfigValue.fromName(
+            (props == null) ? null : props.getString(ConfigFileReaderSupport.COMPRESSION_PROP));
     }
 
     /**
      * <P>Configures an {@link AbstractFactoryManager} with the values read from
      * some configuration. Currently it configures:</P>
      * <UL>
-     * <LI>The {@link Cipher}s - via the {@link #CIPHERS_CONFIG_PROP}</LI>
-     * <LI>The {@link Mac}s - via the {@link #MACS_CONFIG_PROP}</LI>
-     * <LI>The {@link Signature}s - via the {@link #HOST_KEY_ALGORITHMS_CONFIG_PROP}</LI>
-     * <LI>The {@link Compression} - via the {@link #COMPRESSION_PROP}</LI>
+     * <LI>The {@link Cipher}s - via the {@link ConfigFileReaderSupport#CIPHERS_CONFIG_PROP}</LI>
+     * <LI>The {@link Mac}s - via the {@link ConfigFileReaderSupport#MACS_CONFIG_PROP}</LI>
+     * <LI>The {@link Signature}s - via the {@link ConfigFileReaderSupport#HOST_KEY_ALGORITHMS_CONFIG_PROP}</LI>
+     * <LI>The {@link Compression} - via the {@link ConfigFileReaderSupport#COMPRESSION_PROP}</LI>
      * </UL>
      *
      * @param <M>               The generic factory manager
@@ -342,7 +152,8 @@ public final class SshConfigFileReader {
      *                          or unsupported values there is an empty configuration exception is thrown
      * @return The configured manager
      */
-    public static <M extends AbstractFactoryManager> M configure(M manager, PropertyResolver props, boolean lenient, boolean ignoreUnsupported) {
+    public static <M extends AbstractFactoryManager> M configure(
+            M manager, PropertyResolver props, boolean lenient, boolean ignoreUnsupported) {
         configureCiphers(manager, props, lenient, ignoreUnsupported);
         configureSignatures(manager, props, lenient, ignoreUnsupported);
         configureMacs(manager, props, lenient, ignoreUnsupported);
@@ -353,10 +164,13 @@ public final class SshConfigFileReader {
 
     public static <M extends AbstractFactoryManager> M configureCiphers(M manager, PropertyResolver props, boolean lenient, boolean ignoreUnsupported) {
         Objects.requireNonNull(props, "No properties to configure");
-        return configureCiphers(manager, props.getStringProperty(CIPHERS_CONFIG_PROP, DEFAULT_CIPHERS), lenient, ignoreUnsupported);
+        return configureCiphers(manager,
+            props.getStringProperty(ConfigFileReaderSupport.CIPHERS_CONFIG_PROP, ConfigFileReaderSupport.DEFAULT_CIPHERS),
+                lenient, ignoreUnsupported);
     }
 
-    public static <M extends AbstractFactoryManager> M configureCiphers(M manager, String value, boolean lenient, boolean ignoreUnsupported) {
+    public static <M extends AbstractFactoryManager> M configureCiphers(
+            M manager, String value, boolean lenient, boolean ignoreUnsupported) {
         Objects.requireNonNull(manager, "No manager to configure");
 
         BuiltinCiphers.ParseResult result = BuiltinCiphers.parseCiphersList(value);
@@ -371,10 +185,13 @@ public final class SshConfigFileReader {
 
     public static <M extends AbstractFactoryManager> M configureSignatures(M manager, PropertyResolver props, boolean lenient, boolean ignoreUnsupported) {
         Objects.requireNonNull(props, "No properties to configure");
-        return configureSignatures(manager, props.getStringProperty(HOST_KEY_ALGORITHMS_CONFIG_PROP, DEFAULT_HOST_KEY_ALGORITHMS), lenient, ignoreUnsupported);
+        return configureSignatures(manager,
+            props.getStringProperty(ConfigFileReaderSupport.HOST_KEY_ALGORITHMS_CONFIG_PROP, ConfigFileReaderSupport.DEFAULT_HOST_KEY_ALGORITHMS),
+            lenient, ignoreUnsupported);
     }
 
-    public static <M extends AbstractFactoryManager> M configureSignatures(M manager, String value, boolean lenient, boolean ignoreUnsupported) {
+    public static <M extends AbstractFactoryManager> M configureSignatures(
+            M manager, String value, boolean lenient, boolean ignoreUnsupported) {
         Objects.requireNonNull(manager, "No manager to configure");
 
         BuiltinSignatures.ParseResult result = BuiltinSignatures.parseSignatureList(value);
@@ -387,12 +204,16 @@ public final class SshConfigFileReader {
         return manager;
     }
 
-    public static <M extends AbstractFactoryManager> M configureMacs(M manager, PropertyResolver resolver, boolean lenient, boolean ignoreUnsupported) {
+    public static <M extends AbstractFactoryManager> M configureMacs(
+            M manager, PropertyResolver resolver, boolean lenient, boolean ignoreUnsupported) {
         Objects.requireNonNull(resolver, "No properties to configure");
-        return configureMacs(manager, resolver.getStringProperty(MACS_CONFIG_PROP, DEFAULT_MACS), lenient, ignoreUnsupported);
+        return configureMacs(manager,
+            resolver.getStringProperty(ConfigFileReaderSupport.MACS_CONFIG_PROP, ConfigFileReaderSupport.DEFAULT_MACS),
+            lenient, ignoreUnsupported);
     }
 
-    public static <M extends AbstractFactoryManager> M configureMacs(M manager, String value, boolean lenient, boolean ignoreUnsupported) {
+    public static <M extends AbstractFactoryManager> M configureMacs(
+            M manager, String value, boolean lenient, boolean ignoreUnsupported) {
         Objects.requireNonNull(manager, "No manager to configure");
 
         BuiltinMacs.ParseResult result = BuiltinMacs.parseMacsList(value);
@@ -417,13 +238,15 @@ public final class SshConfigFileReader {
      *                          if after ignoring the unknown and un-supported values the result is an empty
      *                          list of factories and exception is thrown
      * @return The configured manager
-     * @see #KEX_ALGORITHMS_CONFIG_PROP
-     * @see #DEFAULT_KEX_ALGORITHMS
+     * @see ConfigFileReaderSupport#KEX_ALGORITHMS_CONFIG_PROP KEX_ALGORITHMS_CONFIG_PROP
+     * @see ConfigFileReaderSupport#DEFAULT_KEX_ALGORITHMS DEFAULT_KEX_ALGORITHMS
      */
     public static <M extends AbstractFactoryManager> M configureKeyExchanges(
             M manager, PropertyResolver props, boolean lenient, Function<? super DHFactory, ? extends NamedFactory<KeyExchange>> xformer, boolean ignoreUnsupported) {
         Objects.requireNonNull(props, "No properties to configure");
-        return configureKeyExchanges(manager, props.getStringProperty(KEX_ALGORITHMS_CONFIG_PROP, DEFAULT_KEX_ALGORITHMS), lenient, xformer, ignoreUnsupported);
+        return configureKeyExchanges(manager,
+            props.getStringProperty(ConfigFileReaderSupport.KEX_ALGORITHMS_CONFIG_PROP, ConfigFileReaderSupport.DEFAULT_KEX_ALGORITHMS),
+            lenient, xformer, ignoreUnsupported);
     }
 
     public static <M extends AbstractFactoryManager> M configureKeyExchanges(
@@ -454,11 +277,12 @@ public final class SshConfigFileReader {
      * @return The configured manager - <B>Note:</B> if the result of filtering due
      * to lenient mode or ignored unsupported value is empty then no factories are set
      */
-    public static <M extends AbstractFactoryManager> M configureCompression(M manager, PropertyResolver props, boolean lenient, boolean ignoreUnsupported) {
+    public static <M extends AbstractFactoryManager> M configureCompression(
+            M manager, PropertyResolver props, boolean lenient, boolean ignoreUnsupported) {
         Objects.requireNonNull(manager, "No manager to configure");
         Objects.requireNonNull(props, "No properties to configure");
 
-        String value = props.getStringProperty(COMPRESSION_PROP, DEFAULT_COMPRESSION);
+        String value = props.getStringProperty(ConfigFileReaderSupport.COMPRESSION_PROP, ConfigFileReaderSupport.DEFAULT_COMPRESSION);
         CompressionFactory factory = CompressionConfigValue.fromName(value);
         ValidateUtils.checkTrue(lenient || (factory != null), "Unsupported compression value: %s", value);
         if ((factory != null) && factory.isSupported()) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/SyslogFacilityValue.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/SyslogFacilityValue.java b/sshd-core/src/main/java/org/apache/sshd/common/config/SyslogFacilityValue.java
deleted file mode 100644
index f52ae36..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/SyslogFacilityValue.java
+++ /dev/null
@@ -1,51 +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.config;
-
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.Set;
-
-import org.apache.sshd.common.util.GenericUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- * @see <A HREF="https://www.freebsd.org/cgi/man.cgi?query=sshd_config&sektion=5"><I>SyslogFacility</I> configuration value</A>
- */
-public enum SyslogFacilityValue {
-    DAEMON, USER, AUTH, LOCAL0, LOCAL1, LOCAL2, LOCAL3, LOCAL4, LOCAL5, LOCAL6, LOCAL7;
-
-    public static final Set<SyslogFacilityValue> VALUES =
-            Collections.unmodifiableSet(EnumSet.allOf(SyslogFacilityValue.class));
-
-    public static SyslogFacilityValue fromName(String n) {
-        if (GenericUtils.isEmpty(n)) {
-            return null;
-        }
-
-        for (SyslogFacilityValue f : VALUES) {
-            if (n.equalsIgnoreCase(f.name())) {
-                return f;
-            }
-        }
-
-        return null;
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/TimeValueConfig.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/TimeValueConfig.java b/sshd-core/src/main/java/org/apache/sshd/common/config/TimeValueConfig.java
deleted file mode 100644
index 222cf84..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/TimeValueConfig.java
+++ /dev/null
@@ -1,180 +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.config;
-
-import java.util.Collections;
-import java.util.EnumMap;
-import java.util.EnumSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.TimeUnit;
-
-import org.apache.sshd.common.util.GenericUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- * @see <A HREF="http://unixhelp.ed.ac.uk/CGI/man-cgi?sshd_config+5">Time formats for SSH configuration values</A>
- */
-public enum TimeValueConfig {
-    SECONDS('s', 'S', TimeUnit.SECONDS.toMillis(1L)),
-    MINUTES('m', 'M', TimeUnit.MINUTES.toMillis(1L)),
-    HOURS('h', 'H', TimeUnit.HOURS.toMillis(1L)),
-    DAYS('d', 'D', TimeUnit.DAYS.toMillis(1L)),
-    WEEKS('w', 'W', TimeUnit.DAYS.toMillis(7L));
-
-    public static final Set<TimeValueConfig> VALUES =
-            Collections.unmodifiableSet(EnumSet.allOf(TimeValueConfig.class));
-
-    private final char loChar;
-    private final char hiChar;
-    private final long interval;
-
-    TimeValueConfig(char lo, char hi, long interval) {
-        loChar = lo;
-        hiChar = hi;
-        this.interval = interval;
-    }
-
-    public final char getLowerCaseValue() {
-        return loChar;
-    }
-
-    public final char getUpperCaseValue() {
-        return hiChar;
-    }
-
-    public final long getInterval() {
-        return interval;
-    }
-
-    public static TimeValueConfig fromValueChar(char ch) {
-        if ((ch <= ' ') || (ch >= 0x7F)) {
-            return null;
-        }
-
-        for (TimeValueConfig v : VALUES) {
-            if ((v.getLowerCaseValue() == ch) || (v.getUpperCaseValue() == ch)) {
-                return v;
-            }
-        }
-
-        return null;
-    }
-
-    /**
-     * @param s A time specification
-     * @return The specified duration in milliseconds
-     * @see #parse(String)
-     * @see #durationOf(Map)
-     */
-    public static long durationOf(String s) {
-        Map<TimeValueConfig, Long> spec = parse(s);
-        return durationOf(spec);
-    }
-
-    /**
-     * @param s An input time specification containing possibly mixed numbers
-     *          and units - e.g., {@code 3h10m} to indicate 3 hours and 10 minutes
-     * @return A {@link Map} specifying for each time unit its count
-     * @throws NumberFormatException    If bad numbers found - e.g., negative counts
-     * @throws IllegalArgumentException If bad format - e.g., unknown unit
-     */
-    public static Map<TimeValueConfig, Long> parse(String s) throws IllegalArgumentException {
-        if (GenericUtils.isEmpty(s)) {
-            return Collections.emptyMap();
-        }
-
-        int lastPos = 0;
-        Map<TimeValueConfig, Long> spec = new EnumMap<>(TimeValueConfig.class);
-        for (int curPos = 0; curPos < s.length(); curPos++) {
-            char ch = s.charAt(curPos);
-            if ((ch >= '0') && (ch <= '9')) {
-                continue;
-            }
-
-            if (curPos <= lastPos) {
-                throw new IllegalArgumentException("parse(" + s + ") missing count value at index=" + curPos);
-            }
-
-            TimeValueConfig c = fromValueChar(ch);
-            if (c == null) {
-                throw new IllegalArgumentException("parse(" + s + ") unknown time value character: '" + ch + "'");
-            }
-
-            String v = s.substring(lastPos, curPos);
-            long count = Long.parseLong(v);
-            if (count < 0L) {
-                throw new IllegalArgumentException("parse(" + s + ") negative count (" + v + ") for " + c.name());
-            }
-
-            Long prev = spec.put(c, count);
-            if (prev != null) {
-                throw new IllegalArgumentException("parse(" + s + ") " + c.name() + " value re-specified: current=" + count + ", previous=" + prev);
-            }
-
-            lastPos = curPos + 1;
-            if (lastPos >= s.length()) {
-                break;
-            }
-        }
-
-        if (lastPos < s.length()) {
-            String v = s.substring(lastPos);
-            long count = Long.parseLong(v);
-            if (count < 0L) {
-                throw new IllegalArgumentException("parse(" + s + ") negative count (" + v + ") for last component");
-            }
-
-            Long prev = spec.put(SECONDS, count);
-            if (prev != null) {
-                throw new IllegalArgumentException("parse(" + s + ") last component (" + SECONDS.name() + ") value re-specified: current=" + count + ", previous=" + prev);
-            }
-        }
-
-        return spec;
-    }
-
-    /**
-     * @param spec The {@link Map} specifying the count for each {@link TimeValueConfig}
-     * @return The total duration in milliseconds
-     * @throws IllegalArgumentException If negative count for a time unit
-     */
-    public static long durationOf(Map<TimeValueConfig, ? extends Number> spec) throws IllegalArgumentException {
-        if (GenericUtils.isEmpty(spec)) {
-            return -1L;
-        }
-
-        long total = 0L;
-        // Cannot use forEach because the total value is not effectively final
-        for (Map.Entry<TimeValueConfig, ? extends Number> se : spec.entrySet()) {
-            TimeValueConfig v = se.getKey();
-            Number c = se.getValue();
-            long factor = c.longValue();
-            if (factor < 0L) {
-                throw new IllegalArgumentException("valueOf(" + spec + ") bad factor (" + c + ") for " + v.name());
-            }
-
-            long added = v.getInterval() * factor;
-            total += added;
-        }
-
-        return total;
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/VersionProperties.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/VersionProperties.java b/sshd-core/src/main/java/org/apache/sshd/common/config/VersionProperties.java
deleted file mode 100644
index 0e351a8..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/VersionProperties.java
+++ /dev/null
@@ -1,98 +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.config;
-
-import java.io.FileNotFoundException;
-import java.io.InputStream;
-import java.util.Collections;
-import java.util.NavigableMap;
-import java.util.Properties;
-import java.util.TreeMap;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.threads.ThreadUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public final class VersionProperties {
-    private static final class LazyVersionPropertiesHolder {
-        private static final NavigableMap<String, String> PROPERTIES =
-            Collections.unmodifiableNavigableMap(loadVersionProperties(LazyVersionPropertiesHolder.class));
-
-        private LazyVersionPropertiesHolder() {
-            throw new UnsupportedOperationException("No instance allowed");
-        }
-
-        private static NavigableMap<String, String> loadVersionProperties(Class<?> anchor) {
-            return loadVersionProperties(anchor, ThreadUtils.resolveDefaultClassLoader(anchor));
-        }
-
-        private static NavigableMap<String, String> loadVersionProperties(Class<?> anchor, ClassLoader loader) {
-            NavigableMap<String, String> result = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
-            try {
-                InputStream input = loader.getResourceAsStream("org/apache/sshd/sshd-version.properties");
-                if (input == null) {
-                    throw new FileNotFoundException("Version resource does not exist");
-                }
-
-                Properties props = new Properties();
-                try {
-                    props.load(input);
-                } finally {
-                    input.close();
-                }
-
-                for (String key : props.stringPropertyNames()) {
-                    String propValue = props.getProperty(key);
-                    String value = GenericUtils.trimToEmpty(propValue);
-                    if (GenericUtils.isEmpty(value)) {
-                        continue;   // we have no need for empty values
-                    }
-
-                    String prev = result.put(key, value);
-                    if (prev != null) {
-                        Logger log = LoggerFactory.getLogger(anchor);
-                        log.warn("Multiple values for key=" + key + ": current=" + value + ", previous=" + prev);
-                    }
-                }
-            } catch (Exception e) {
-                Logger log = LoggerFactory.getLogger(anchor);
-                log.warn("Failed (" + e.getClass().getSimpleName() + ") to load version properties: " + e.getMessage());
-            }
-
-            return result;
-        }
-    }
-
-    private VersionProperties() {
-        throw new UnsupportedOperationException("No instance");
-    }
-
-    /**
-     * @return A case <u>insensitive</u> un-modifiable {@link NavigableMap} of the {@code sshd-version.properties} data
-     */
-    @SuppressWarnings("synthetic-access")
-    public static NavigableMap<String, String> getVersionProperties() {
-        return LazyVersionPropertiesHolder.PROPERTIES;
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntry.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntry.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntry.java
deleted file mode 100644
index a763903..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntry.java
+++ /dev/null
@@ -1,492 +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.config.keys;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.io.StreamCorruptedException;
-import java.net.URL;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.OpenOption;
-import java.nio.file.Path;
-import java.security.GeneralSecurityException;
-import java.security.PublicKey;
-import java.util.AbstractMap.SimpleImmutableEntry;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.NavigableMap;
-import java.util.TreeMap;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.io.NoCloseInputStream;
-import org.apache.sshd.common.util.io.NoCloseReader;
-import org.apache.sshd.server.auth.pubkey.KeySetPublickeyAuthenticator;
-import org.apache.sshd.server.auth.pubkey.PublickeyAuthenticator;
-import org.apache.sshd.server.auth.pubkey.RejectAllPublickeyAuthenticator;
-
-/**
- * Represents an entry in the user's {@code authorized_keys} file according
- * to the <A HREF="http://en.wikibooks.org/wiki/OpenSSH/Client_Configuration_Files#.7E.2F.ssh.2Fauthorized_keys">OpenSSH format</A>.
- * <B>Note:</B> {@code equals/hashCode} check only the key type and data - the
- * comment and/or login options are not considered part of equality
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- * @see <A HREF="http://man.openbsd.org/sshd.8#AUTHORIZED_KEYS_FILE_FORMAT">sshd(8) - AUTHORIZED_KEYS_FILE_FORMAT</A>
- */
-public class AuthorizedKeyEntry extends PublicKeyEntry {
-    public static final char BOOLEAN_OPTION_NEGATION_INDICATOR = '!';
-
-    private static final long serialVersionUID = -9007505285002809156L;
-
-    private String comment;
-    // for options that have no value, "true" is used
-    private Map<String, String> loginOptions = Collections.emptyMap();
-
-    public AuthorizedKeyEntry() {
-        super();
-    }
-
-    public String getComment() {
-        return comment;
-    }
-
-    public void setComment(String value) {
-        this.comment = value;
-    }
-
-    public Map<String, String> getLoginOptions() {
-        return loginOptions;
-    }
-
-    public void setLoginOptions(Map<String, String> value) {
-        if (value == null) {
-            this.loginOptions = Collections.emptyMap();
-        } else {
-            this.loginOptions = value;
-        }
-    }
-
-    @Override
-    public PublicKey appendPublicKey(Appendable sb, PublicKeyEntryResolver fallbackResolver) throws IOException, GeneralSecurityException {
-        Map<String, String> options = getLoginOptions();
-        if (!GenericUtils.isEmpty(options)) {
-            int index = 0;
-            // Cannot use forEach because the index value is not effectively final
-            for (Map.Entry<String, String> oe : options.entrySet()) {
-                String key = oe.getKey();
-                String value = oe.getValue();
-                if (index > 0) {
-                    sb.append(',');
-                }
-                sb.append(key);
-                // TODO figure out a way to remember which options where quoted
-                // TODO figure out a way to remember which options had no value
-                if (!Boolean.TRUE.toString().equals(value)) {
-                    sb.append('=').append(value);
-                }
-                index++;
-            }
-
-            if (index > 0) {
-                sb.append(' ');
-            }
-        }
-
-        PublicKey key = super.appendPublicKey(sb, fallbackResolver);
-        String kc = getComment();
-        if (!GenericUtils.isEmpty(kc)) {
-            sb.append(' ').append(kc);
-        }
-
-        return key;
-    }
-
-    @Override   // to avoid Findbugs[EQ_DOESNT_OVERRIDE_EQUALS]
-    public int hashCode() {
-        return super.hashCode();
-    }
-
-    @Override   // to avoid Findbugs[EQ_DOESNT_OVERRIDE_EQUALS]
-    public boolean equals(Object obj) {
-        return super.equals(obj);
-    }
-
-    @Override
-    public String toString() {
-        String entry = super.toString();
-        String kc = getComment();
-        Map<?, ?> ko = getLoginOptions();
-        return (GenericUtils.isEmpty(ko) ? "" : ko.toString() + " ")
-                + entry
-                + (GenericUtils.isEmpty(kc) ? "" : " " + kc);
-    }
-
-    public static PublickeyAuthenticator fromAuthorizedEntries(PublicKeyEntryResolver fallbackResolver, Collection<? extends AuthorizedKeyEntry> entries)
-            throws IOException, GeneralSecurityException {
-        Collection<PublicKey> keys = resolveAuthorizedKeys(fallbackResolver, entries);
-        if (GenericUtils.isEmpty(keys)) {
-            return RejectAllPublickeyAuthenticator.INSTANCE;
-        } else {
-            return new KeySetPublickeyAuthenticator(keys);
-        }
-    }
-
-    public static List<PublicKey> resolveAuthorizedKeys(PublicKeyEntryResolver fallbackResolver, Collection<? extends AuthorizedKeyEntry> entries)
-            throws IOException, GeneralSecurityException {
-        if (GenericUtils.isEmpty(entries)) {
-            return Collections.emptyList();
-        }
-
-        List<PublicKey> keys = new ArrayList<>(entries.size());
-        for (AuthorizedKeyEntry e : entries) {
-            PublicKey k = e.resolvePublicKey(fallbackResolver);
-            if (k != null) {
-                keys.add(k);
-            }
-        }
-
-        return keys;
-    }
-
-    /**
-     * Reads read the contents of an <code>authorized_keys</code> file
-     *
-     * @param url The {@link URL} to read from
-     * @return A {@link List} of all the {@link AuthorizedKeyEntry}-ies found there
-     * @throws IOException If failed to read or parse the entries
-     * @see #readAuthorizedKeys(InputStream, boolean)
-     */
-    public static List<AuthorizedKeyEntry> readAuthorizedKeys(URL url) throws IOException {
-        try (InputStream in = url.openStream()) {
-            return readAuthorizedKeys(in, true);
-        }
-    }
-
-    /**
-     * Reads read the contents of an <code>authorized_keys</code> file
-     *
-     * @param file The {@link File} to read from
-     * @return A {@link List} of all the {@link AuthorizedKeyEntry}-ies found there
-     * @throws IOException If failed to read or parse the entries
-     * @see #readAuthorizedKeys(InputStream, boolean)
-     */
-    public static List<AuthorizedKeyEntry> readAuthorizedKeys(File file) throws IOException {
-        try (InputStream in = new FileInputStream(file)) {
-            return readAuthorizedKeys(in, true);
-        }
-    }
-
-    /**
-     * Reads read the contents of an <code>authorized_keys</code> file
-     *
-     * @param path    {@link Path} to read from
-     * @param options The {@link OpenOption}s to use - if unspecified then appropriate
-     *                defaults assumed
-     * @return A {@link List} of all the {@link AuthorizedKeyEntry}-ies found there
-     * @throws IOException If failed to read or parse the entries
-     * @see #readAuthorizedKeys(InputStream, boolean)
-     * @see Files#newInputStream(Path, OpenOption...)
-     */
-    public static List<AuthorizedKeyEntry> readAuthorizedKeys(Path path, OpenOption... options) throws IOException {
-        try (InputStream in = Files.newInputStream(path, options)) {
-            return readAuthorizedKeys(in, true);
-        }
-    }
-
-    /**
-     * Reads read the contents of an <code>authorized_keys</code> file
-     *
-     * @param filePath The file path to read from
-     * @return A {@link List} of all the {@link AuthorizedKeyEntry}-ies found there
-     * @throws IOException If failed to read or parse the entries
-     * @see #readAuthorizedKeys(InputStream, boolean)
-     */
-    public static List<AuthorizedKeyEntry> readAuthorizedKeys(String filePath) throws IOException {
-        try (InputStream in = new FileInputStream(filePath)) {
-            return readAuthorizedKeys(in, true);
-        }
-    }
-
-    /**
-     * Reads read the contents of an <code>authorized_keys</code> file
-     *
-     * @param in        The {@link InputStream}
-     * @param okToClose <code>true</code> if method may close the input stream
-     *                  regardless of whether successful or failed
-     * @return A {@link List} of all the {@link AuthorizedKeyEntry}-ies found there
-     * @throws IOException If failed to read or parse the entries
-     * @see #readAuthorizedKeys(Reader, boolean)
-     */
-    public static List<AuthorizedKeyEntry> readAuthorizedKeys(InputStream in, boolean okToClose) throws IOException {
-        try (Reader rdr = new InputStreamReader(NoCloseInputStream.resolveInputStream(in, okToClose), StandardCharsets.UTF_8)) {
-            return readAuthorizedKeys(rdr, true);
-        }
-    }
-
-    /**
-     * Reads read the contents of an <code>authorized_keys</code> file
-     *
-     * @param rdr       The {@link Reader}
-     * @param okToClose <code>true</code> if method may close the input stream
-     *                  regardless of whether successful or failed
-     * @return A {@link List} of all the {@link AuthorizedKeyEntry}-ies found there
-     * @throws IOException If failed to read or parse the entries
-     * @see #readAuthorizedKeys(BufferedReader)
-     */
-    public static List<AuthorizedKeyEntry> readAuthorizedKeys(Reader rdr, boolean okToClose) throws IOException {
-        try (BufferedReader buf = new BufferedReader(NoCloseReader.resolveReader(rdr, okToClose))) {
-            return readAuthorizedKeys(buf);
-        }
-    }
-
-    /**
-     * @param rdr The {@link BufferedReader} to use to read the contents of
-     *            an <code>authorized_keys</code> file
-     * @return A {@link List} of all the {@link AuthorizedKeyEntry}-ies found there
-     * @throws IOException If failed to read or parse the entries
-     * @see #parseAuthorizedKeyEntry(String)
-     */
-    public static List<AuthorizedKeyEntry> readAuthorizedKeys(BufferedReader rdr) throws IOException {
-        List<AuthorizedKeyEntry> entries = null;
-
-        for (String line = rdr.readLine(); line != null; line = rdr.readLine()) {
-            AuthorizedKeyEntry entry;
-            try {
-                entry = parseAuthorizedKeyEntry(line);
-                if (entry == null) {    // null, empty or comment line
-                    continue;
-                }
-            } catch (RuntimeException | Error e) {
-                throw new StreamCorruptedException("Failed (" + e.getClass().getSimpleName() + ")"
-                        + " to parse key entry=" + line + ": " + e.getMessage());
-            }
-
-            if (entries == null) {
-                entries = new ArrayList<>();
-            }
-
-            entries.add(entry);
-        }
-
-        if (entries == null) {
-            return Collections.emptyList();
-        } else {
-            return entries;
-        }
-    }
-
-    /**
-     * @param value Original line from an <code>authorized_keys</code> file
-     * @return {@link AuthorizedKeyEntry} or {@code null} if the line is
-     * {@code null}/empty or a comment line
-     * @throws IllegalArgumentException If failed to parse/decode the line
-     * @see #COMMENT_CHAR
-     */
-    public static AuthorizedKeyEntry parseAuthorizedKeyEntry(String value) throws IllegalArgumentException {
-        String line = GenericUtils.replaceWhitespaceAndTrim(value);
-        if (GenericUtils.isEmpty(line) || (line.charAt(0) == COMMENT_CHAR) /* comment ? */) {
-            return null;
-        }
-
-        int startPos = line.indexOf(' ');
-        if (startPos <= 0) {
-            throw new IllegalArgumentException("Bad format (no key data delimiter): " + line);
-        }
-
-        int endPos = line.indexOf(' ', startPos + 1);
-        if (endPos <= startPos) {
-            endPos = line.length();
-        }
-
-        String keyType = line.substring(0, startPos);
-        PublicKeyEntryDecoder<?, ?> decoder = KeyUtils.getPublicKeyEntryDecoder(keyType);
-        AuthorizedKeyEntry entry;
-        // assume this is due to the fact that it starts with login options
-        if (decoder == null) {
-            Map.Entry<String, String> comps = resolveEntryComponents(line);
-            entry = parseAuthorizedKeyEntry(comps.getValue());
-            ValidateUtils.checkTrue(entry != null, "Bad format (no key data after login options): %s", line);
-            entry.setLoginOptions(parseLoginOptions(comps.getKey()));
-        } else {
-            String encData = (endPos < (line.length() - 1)) ? line.substring(0, endPos).trim() : line;
-            String comment = (endPos < (line.length() - 1)) ? line.substring(endPos + 1).trim() : null;
-            entry = parsePublicKeyEntry(new AuthorizedKeyEntry(), encData);
-            entry.setComment(comment);
-        }
-
-        return entry;
-    }
-
-    /**
-     * Parses a single line from an <code>authorized_keys</code> file that is <U>known</U>
-     * to contain login options and separates it to the options and the rest of the line.
-     *
-     * @param entryLine The line to be parsed
-     * @return A {@link SimpleImmutableEntry} representing the parsed data where key=login options part
-     * and value=rest of the data - {@code null} if no data in line or line starts with comment character
-     * @see <A HREF="http://man.openbsd.org/sshd.8#AUTHORIZED_KEYS_FILE_FORMAT">sshd(8) - AUTHORIZED_KEYS_FILE_FORMAT</A>
-     */
-    public static SimpleImmutableEntry<String, String> resolveEntryComponents(String entryLine) {
-        String line = GenericUtils.replaceWhitespaceAndTrim(entryLine);
-        if (GenericUtils.isEmpty(line) || (line.charAt(0) == COMMENT_CHAR) /* comment ? */) {
-            return null;
-        }
-
-        for (int lastPos = 0; lastPos < line.length();) {
-            int startPos = line.indexOf(' ', lastPos);
-            if (startPos < lastPos) {
-                throw new IllegalArgumentException("Bad format (no key data delimiter): " + line);
-            }
-
-            int quotePos = line.indexOf('"', startPos + 1);
-            // If found quotes after the space then assume part of a login option
-            if (quotePos > startPos) {
-                lastPos = quotePos + 1;
-                continue;
-            }
-
-            String loginOptions = line.substring(0, startPos).trim();
-            String remainder = line.substring(startPos + 1).trim();
-            return new SimpleImmutableEntry<>(loginOptions, remainder);
-        }
-
-        throw new IllegalArgumentException("Bad format (no key data contents): " + line);
-    }
-
-    /**
-     * <P>
-     * Parses login options line according to
-     * <A HREF="http://man.openbsd.org/sshd.8#AUTHORIZED_KEYS_FILE_FORMAT">sshd(8) - AUTHORIZED_KEYS_FILE_FORMAT</A>
-     * guidelines. <B>Note:</B>
-     * </P>
-     *
-     * <UL>
-     *      <P><LI>
-     *      Options that have a value are automatically stripped of any surrounding double quotes./
-     *      </LI></P>
-     *
-     *      <P><LI>
-     *      Options that have no value are marked as {@code true/false} - according
-     *      to the {@link #BOOLEAN_OPTION_NEGATION_INDICATOR}.
-     *      </LI></P>
-     *
-     *      <P><LI>
-     *      Options that appear multiple times are simply concatenated using comma as separator.
-     *      </LI></P>
-     * </UL>
-     *
-     * @param options The options line to parse - ignored if {@code null}/empty/blank
-     * @return A {@link NavigableMap} where key=case <U>insensitive</U> option name and value=the parsed value.
-     * @see #addLoginOption(Map, String) addLoginOption
-     */
-    public static NavigableMap<String, String> parseLoginOptions(String options) {
-        String line = GenericUtils.replaceWhitespaceAndTrim(options);
-        int len = GenericUtils.length(line);
-        if (len <= 0) {
-            return Collections.emptyNavigableMap();
-        }
-
-        NavigableMap<String, String> optsMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
-        int lastPos = 0;
-        for (int curPos = 0; curPos < len; curPos++) {
-            int nextPos = line.indexOf(',', curPos);
-            if (nextPos < curPos) {
-                break;
-            }
-
-            // check if "true" comma or one inside quotes
-            int quotePos = line.indexOf('"', curPos);
-            if ((quotePos >= lastPos) && (quotePos < nextPos)) {
-                nextPos = line.indexOf('"', quotePos + 1);
-                if (nextPos <= quotePos) {
-                    throw new IllegalArgumentException("Bad format (imbalanced quoted command): " + line);
-                }
-
-                // Make sure either comma or no more options follow the 2nd quote
-                for (nextPos++; nextPos < len; nextPos++) {
-                    char ch = line.charAt(nextPos);
-                    if (ch == ',') {
-                        break;
-                    }
-
-                    if (ch != ' ') {
-                        throw new IllegalArgumentException("Bad format (incorrect list format): " + line);
-                    }
-                }
-            }
-
-            addLoginOption(optsMap, line.substring(lastPos, nextPos));
-            lastPos = nextPos + 1;
-            curPos = lastPos;
-        }
-
-        // Any leftovers at end of line ?
-        if (lastPos < len) {
-            addLoginOption(optsMap, line.substring(lastPos));
-        }
-
-        return optsMap;
-    }
-
-    /**
-     * Parses and adds a new option to the options map. If a valued option is re-specified then
-     * its value(s) are concatenated using comma as separator.
-     *
-     * @param optsMap Options map to add to
-     * @param option The option data to parse - ignored if {@code null}/empty/blank
-     * @return The updated entry - {@code null} if no option updated in the map
-     * @throws IllegalStateException If a boolean option is re-specified
-     */
-    public static SimpleImmutableEntry<String, String> addLoginOption(Map<String, String> optsMap, String option) {
-        String p = GenericUtils.trimToEmpty(option);
-        if (GenericUtils.isEmpty(p)) {
-            return null;
-        }
-
-        int pos = p.indexOf('=');
-        String name = (pos < 0) ? p : GenericUtils.trimToEmpty(p.substring(0, pos));
-        CharSequence value = (pos < 0) ? null : GenericUtils.trimToEmpty(p.substring(pos + 1));
-        value = GenericUtils.stripQuotes(value);
-        if (value == null) {
-            value = Boolean.toString(name.charAt(0) != BOOLEAN_OPTION_NEGATION_INDICATOR);
-        }
-
-        SimpleImmutableEntry<String, String> entry = new SimpleImmutableEntry<>(name, value.toString());
-        String prev = optsMap.put(entry.getKey(), entry.getValue());
-        if (prev != null) {
-            if (pos < 0) {
-                throw new IllegalStateException("Bad format (boolean option (" + name + ") re-specified): " + p);
-            }
-            optsMap.put(entry.getKey(), prev + "," + entry.getValue());
-        }
-
-        return entry;
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/BuiltinIdentities.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/BuiltinIdentities.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/BuiltinIdentities.java
deleted file mode 100644
index 53b6b44..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/BuiltinIdentities.java
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.common.config.keys;
-
-import java.security.Key;
-import java.security.KeyPair;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.interfaces.DSAPrivateKey;
-import java.security.interfaces.DSAPublicKey;
-import java.security.interfaces.ECPrivateKey;
-import java.security.interfaces.ECPublicKey;
-import java.security.interfaces.RSAPrivateKey;
-import java.security.interfaces.RSAPublicKey;
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.Objects;
-import java.util.Set;
-
-import org.apache.sshd.common.NamedResource;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public enum BuiltinIdentities implements Identity {
-    RSA(Constants.RSA, RSAPublicKey.class, RSAPrivateKey.class),
-    DSA(Constants.DSA, DSAPublicKey.class, DSAPrivateKey.class),
-    ECDSA(Constants.ECDSA, KeyUtils.EC_ALGORITHM, ECPublicKey.class, ECPrivateKey.class) {
-        @Override
-        public boolean isSupported() {
-            return SecurityUtils.isECCSupported();
-        }
-    },
-    ED25119(Constants.ED25519, SecurityUtils.EDDSA, SecurityUtils.getEDDSAPublicKeyType(), SecurityUtils.getEDDSAPrivateKeyType()) {
-        @Override
-        public boolean isSupported() {
-            return SecurityUtils.isEDDSACurveSupported();
-        }
-    };
-
-    public static final Set<BuiltinIdentities> VALUES =
-            Collections.unmodifiableSet(EnumSet.allOf(BuiltinIdentities.class));
-
-    public static final Set<String> NAMES =
-            Collections.unmodifiableSet(
-                    GenericUtils.asSortedSet(
-                            String.CASE_INSENSITIVE_ORDER, NamedResource.getNameList(VALUES)));
-
-    private final String name;
-    private final String algorithm;
-    private final Class<? extends PublicKey> pubType;
-    private final Class<? extends PrivateKey> prvType;
-
-    BuiltinIdentities(String type, Class<? extends PublicKey> pubType, Class<? extends PrivateKey> prvType) {
-        this(type, type, pubType, prvType);
-    }
-
-    BuiltinIdentities(String name, String algorithm, Class<? extends PublicKey> pubType, Class<? extends PrivateKey> prvType) {
-        this.name = name.toLowerCase();
-        this.algorithm = algorithm.toUpperCase();
-        this.pubType = pubType;
-        this.prvType = prvType;
-    }
-
-    @Override
-    public final String getName() {
-        return name;
-    }
-
-    @Override
-    public boolean isSupported() {
-        return true;
-    }
-
-    @Override
-    public String getAlgorithm() {
-        return algorithm;
-    }
-
-    @Override
-    public final Class<? extends PublicKey> getPublicKeyType() {
-        return pubType;
-    }
-
-    @Override
-    public final Class<? extends PrivateKey> getPrivateKeyType() {
-        return prvType;
-    }
-
-    /**
-     * @param name The identity name - ignored if {@code null}/empty
-     * @return The matching {@link BuiltinIdentities} whose {@link #getName()}
-     * value matches case <U>insensitive</U> or {@code null} if no match found
-     */
-    public static BuiltinIdentities fromName(String name) {
-        return NamedResource.findByName(name, String.CASE_INSENSITIVE_ORDER, VALUES);
-    }
-
-    /**
-     * @param algorithm The algorithm  - ignored if {@code null}/empty
-     * @return The matching {@link BuiltinIdentities} whose {@link #getAlgorithm()}
-     * value matches case <U>insensitive</U> or {@code null} if no match found
-     */
-    public static BuiltinIdentities fromAlgorithm(String algorithm) {
-        if (GenericUtils.isEmpty(algorithm)) {
-            return null;
-        }
-
-        for (BuiltinIdentities id : VALUES) {
-            if (algorithm.equalsIgnoreCase(id.getAlgorithm())) {
-                return id;
-            }
-        }
-
-        return null;
-    }
-
-    /**
-     * @param kp The {@link KeyPair} - ignored if {@code null}
-     * @return The matching {@link BuiltinIdentities} provided <U>both</U>
-     * public and public keys are of the same type - {@code null} if no
-     * match could be found
-     * @see #fromKey(Key)
-     */
-    public static BuiltinIdentities fromKeyPair(KeyPair kp) {
-        if (kp == null) {
-            return null;
-        }
-
-        BuiltinIdentities i1 = fromKey(kp.getPublic());
-        BuiltinIdentities i2 = fromKey(kp.getPrivate());
-        if (Objects.equals(i1, i2)) {
-            return i1;
-        } else {
-            return null;    // some kind of mixed keys...
-        }
-    }
-
-    /**
-     * @param key The {@link Key} instance - ignored if {@code null}
-     * @return The matching {@link BuiltinIdentities} whose either public or
-     * private key type matches the requested one or {@code null} if no match found
-     * @see #fromKeyType(Class)
-     */
-    public static BuiltinIdentities fromKey(Key key) {
-        return fromKeyType((key == null) ? null : key.getClass());
-    }
-
-    /**
-     * @param clazz The key type - ignored if {@code null} or not
-     *              a {@link Key} class
-     * @return The matching {@link BuiltinIdentities} whose either public or
-     * private key type matches the requested one or {@code null} if no match found
-     * @see #getPublicKeyType()
-     * @see #getPrivateKeyType()
-     */
-    public static BuiltinIdentities fromKeyType(Class<?> clazz) {
-        if ((clazz == null) || (!Key.class.isAssignableFrom(clazz))) {
-            return null;
-        }
-
-        for (BuiltinIdentities id : VALUES) {
-            Class<?> pubType = id.getPublicKeyType();
-            Class<?> prvType = id.getPrivateKeyType();
-            // Ignore placeholder classes (e.g., if ed25519 is not supported)
-            if ((prvType == null) || (pubType == null)) {
-                continue;
-            }
-            if ((prvType == PrivateKey.class) || (pubType == PublicKey.class)) {
-                continue;
-            }
-            if (pubType.isAssignableFrom(clazz) || prvType.isAssignableFrom(clazz)) {
-                return id;
-            }
-        }
-
-        return null;
-    }
-
-    /**
-     * Contains the names of the identities
-     */
-    public static final class Constants {
-        public static final String RSA = KeyUtils.RSA_ALGORITHM;
-        public static final String DSA = KeyUtils.DSS_ALGORITHM;
-        public static final String ECDSA = "ECDSA";
-        public static final String ED25519 = "ED25519";
-
-        private Constants() {
-            throw new UnsupportedOperationException("No instance allowed");
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/FilePasswordProvider.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/FilePasswordProvider.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/FilePasswordProvider.java
deleted file mode 100644
index 064f75c..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/FilePasswordProvider.java
+++ /dev/null
@@ -1,45 +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.config.keys;
-
-import java.io.IOException;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FunctionalInterface
-public interface FilePasswordProvider {
-    /**
-     * An &quot;empty&quot; provider that returns {@code null} - i.e., unprotected key file
-     */
-    FilePasswordProvider EMPTY = resourceKey -> null;
-
-    /**
-     * @param resourceKey The resource key representing the <U>private</U>
-     *                    file
-     * @return The password - if {@code null}/empty then no password is required
-     * @throws IOException if cannot resolve password
-     */
-    String getPassword(String resourceKey) throws IOException;
-
-    static FilePasswordProvider of(String password) {
-        return r -> password;
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/Identity.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/Identity.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/Identity.java
deleted file mode 100644
index eaec413..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/Identity.java
+++ /dev/null
@@ -1,42 +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.config.keys;
-
-import java.security.PrivateKey;
-import java.security.PublicKey;
-
-import org.apache.sshd.common.NamedResource;
-import org.apache.sshd.common.OptionalFeature;
-
-/**
- * Represents an SSH key type
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface Identity extends NamedResource, OptionalFeature {
-    /**
-     * @return The key algorithm - e.g., RSA, DSA, EC
-     */
-    String getAlgorithm();
-
-    Class<? extends PublicKey> getPublicKeyType();
-
-    Class<? extends PrivateKey> getPrivateKeyType();
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/IdentityResourceLoader.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/IdentityResourceLoader.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/IdentityResourceLoader.java
deleted file mode 100644
index d826821..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/IdentityResourceLoader.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.config.keys;
-
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.util.Collection;
-
-/**
- * @param <PUB> Type of {@link PublicKey}
- * @param <PRV> Type of {@link PrivateKey}
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface IdentityResourceLoader<PUB extends PublicKey, PRV extends PrivateKey> {
-    /**
-     * @return The {@link Class} of the {@link PublicKey} that is the result
-     * of decoding
-     */
-    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
-     * are supported by this decoder - e.g., ECDSA keys have several curve names.
-     * <B>Caveat:</B> this collection may be un-modifiable...
-     */
-    Collection<String> getSupportedTypeNames();
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/IdentityUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/IdentityUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/IdentityUtils.java
deleted file mode 100644
index fbc3ce7..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/IdentityUtils.java
+++ /dev/null
@@ -1,159 +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.config.keys;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.file.Files;
-import java.nio.file.OpenOption;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.util.Collections;
-import java.util.Map;
-import java.util.TreeMap;
-
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.keyprovider.MappedKeyPairProvider;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public final class IdentityUtils {
-    private IdentityUtils() {
-        throw new UnsupportedOperationException("No instance");
-    }
-
-    private static final class LazyDefaultUserHomeFolderHolder {
-        private static final Path PATH =
-            Paths.get(ValidateUtils.checkNotNullAndNotEmpty(System.getProperty("user.home"), "No user home"))
-                .toAbsolutePath()
-                .normalize();
-
-        private LazyDefaultUserHomeFolderHolder() {
-            throw new UnsupportedOperationException("No instance allowed");
-        }
-    }
-
-    /**
-     * @return The {@link Path} to the currently running user home
-     */
-    @SuppressWarnings("synthetic-access")
-    public static Path getUserHomeFolder() {
-        return LazyDefaultUserHomeFolderHolder.PATH;
-    }
-
-    /**
-     * @param prefix The file name prefix - ignored if {@code null}/empty
-     * @param type   The identity type - ignored if {@code null}/empty
-     * @param suffix The file name suffix - ignored if {@code null}/empty
-     * @return The identity file name or {@code null} if no name
-     */
-    public static String getIdentityFileName(String prefix, String type, String suffix) {
-        if (GenericUtils.isEmpty(type)) {
-            return null;
-        } else {
-            return GenericUtils.trimToEmpty(prefix)
-                    + type.toLowerCase() + GenericUtils.trimToEmpty(suffix);
-        }
-    }
-
-    /**
-     * @param ids           A {@link Map} of the loaded identities where key=the identity type,
-     *                      value=the matching {@link KeyPair} - ignored if {@code null}/empty
-     * @param supportedOnly If {@code true} then ignore identities that are not
-     *                      supported internally
-     * @return A {@link KeyPair} for the identities - {@code null} if no identities
-     * available (e.g., after filtering unsupported ones)
-     * @see BuiltinIdentities
-     */
-    public static KeyPairProvider createKeyPairProvider(Map<String, KeyPair> ids, boolean supportedOnly) {
-        if (GenericUtils.isEmpty(ids)) {
-            return null;
-        }
-
-        Map<String, KeyPair> pairsMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
-        ids.forEach((type, kp) -> {
-            BuiltinIdentities id = BuiltinIdentities.fromName(type);
-            if (id == null) {
-                id = BuiltinIdentities.fromKeyPair(kp);
-            }
-
-            if (supportedOnly && ((id == null) || (!id.isSupported()))) {
-                return;
-            }
-
-            String keyType = KeyUtils.getKeyType(kp);
-            if (GenericUtils.isEmpty(keyType)) {
-                return;
-            }
-
-            KeyPair prev = pairsMap.put(keyType, kp);
-            if (prev != null) {
-                return;   // less of an offense if 2 pairs mapped to same key type
-            }
-        });
-
-        if (GenericUtils.isEmpty(pairsMap)) {
-            return null;
-        } else {
-            return new MappedKeyPairProvider(pairsMap);
-        }
-    }
-
-    /**
-     * @param paths    A {@link Map} of the identities where key=identity type (case
-     *                 <U>insensitive</U>), value=the {@link Path} of file with the identity key
-     * @param provider A {@link FilePasswordProvider} - may be {@code null}
-     *                 if the loaded keys are <U>guaranteed</U> not to be encrypted. The argument
-     *                 to {@link FilePasswordProvider#getPassword(String)} is the path of the
-     *                 file whose key is to be loaded
-     * @param options  The {@link OpenOption}s to use when reading the key data
-     * @return A {@link Map} of the identities where key=identity type (case
-     * <U>insensitive</U>), value=the {@link KeyPair} of the identity
-     * @throws IOException              If failed to access the file system
-     * @throws GeneralSecurityException If failed to load the keys
-     * @see SecurityUtils#loadKeyPairIdentity(String, InputStream, FilePasswordProvider)
-     */
-    public static Map<String, KeyPair> loadIdentities(Map<String, ? extends Path> paths, FilePasswordProvider provider, OpenOption... options)
-            throws IOException, GeneralSecurityException {
-        if (GenericUtils.isEmpty(paths)) {
-            return Collections.emptyMap();
-        }
-
-        Map<String, KeyPair> ids = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
-        // Cannot use forEach because the potential for IOExceptions being thrown
-        for (Map.Entry<String, ? extends Path> pe : paths.entrySet()) {
-            String type = pe.getKey();
-            Path path = pe.getValue();
-            try (InputStream inputStream = Files.newInputStream(path, options)) {
-                KeyPair kp = SecurityUtils.loadKeyPairIdentity(path.toString(), inputStream, provider);
-                KeyPair prev = ids.put(type, kp);
-                ValidateUtils.checkTrue(prev == null, "Multiple keys for type=%s", type);
-            }
-        }
-
-        return ids;
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/KeyEntryResolver.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/KeyEntryResolver.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/KeyEntryResolver.java
deleted file mode 100644
index 4bfbea0..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/KeyEntryResolver.java
+++ /dev/null
@@ -1,190 +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.config.keys;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.math.BigInteger;
-import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
-import java.security.GeneralSecurityException;
-import java.security.InvalidKeyException;
-import java.security.KeyFactory;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-
-import org.apache.sshd.common.util.io.IoUtils;
-
-/**
- * @param <PUB> Type of {@link PublicKey}
- * @param <PRV> Type of {@link PrivateKey}
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface KeyEntryResolver<PUB extends PublicKey, PRV extends PrivateKey> extends IdentityResourceLoader<PUB, PRV> {
-    /**
-     * @param keySize Key size in bits
-     * @return A {@link KeyPair} with the specified key size
-     * @throws GeneralSecurityException if unable to generate the pair
-     */
-    default KeyPair generateKeyPair(int keySize) throws GeneralSecurityException {
-        KeyPairGenerator gen = getKeyPairGenerator();
-        gen.initialize(keySize);
-        return gen.generateKeyPair();
-    }
-
-    /**
-     * @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()
-     */
-    default KeyPair cloneKeyPair(KeyPair kp) throws GeneralSecurityException {
-        if (kp == null) {
-            return null;
-        }
-
-        PUB pubCloned = null;
-        PublicKey pubOriginal = kp.getPublic();
-        Class<PUB> pubExpected = getPublicKeyType();
-        if (pubOriginal != null) {
-            Class<?> orgType = pubOriginal.getClass();
-            if (!pubExpected.isAssignableFrom(orgType)) {
-                throw new InvalidKeyException("Mismatched public key types: expected=" + pubExpected.getSimpleName() + ", actual=" + orgType.getSimpleName());
-            }
-
-            pubCloned = clonePublicKey(pubExpected.cast(pubOriginal));
-        }
-
-        PRV prvCloned = null;
-        PrivateKey prvOriginal = kp.getPrivate();
-        Class<PRV> prvExpected = getPrivateKeyType();
-        if (prvOriginal != null) {
-            Class<?> orgType = prvOriginal.getClass();
-            if (!prvExpected.isAssignableFrom(orgType)) {
-                throw new InvalidKeyException("Mismatched private key types: expected=" + prvExpected.getSimpleName() + ", actual=" + orgType.getSimpleName());
-            }
-
-            prvCloned = clonePrivateKey(prvExpected.cast(prvOriginal));
-        }
-
-        return new KeyPair(pubCloned, prvCloned);
-    }
-
-    /**
-     * @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;
-
-    /**
-     * @return A {@link KeyFactory} suitable for the specific decoder type
-     * @throws GeneralSecurityException If failed to create one
-     */
-    KeyFactory getKeyFactoryInstance() throws GeneralSecurityException;
-
-    static int encodeString(OutputStream s, String v) throws IOException {
-        return encodeString(s, v, StandardCharsets.UTF_8);
-    }
-
-    static int encodeString(OutputStream s, String v, String charset) throws IOException {
-        return encodeString(s, v, Charset.forName(charset));
-    }
-
-    static int encodeString(OutputStream s, String v, Charset cs) throws IOException {
-        return writeRLEBytes(s, v.getBytes(cs));
-    }
-
-    static int encodeBigInt(OutputStream s, BigInteger v) throws IOException {
-        return writeRLEBytes(s, v.toByteArray());
-    }
-
-    static int writeRLEBytes(OutputStream s, byte... bytes) throws IOException {
-        return writeRLEBytes(s, bytes, 0, bytes.length);
-    }
-
-    static int writeRLEBytes(OutputStream s, byte[] bytes, int off, int len) throws IOException {
-        byte[] lenBytes = encodeInt(s, len);
-        s.write(bytes, off, len);
-        return lenBytes.length + len;
-    }
-
-    static byte[] encodeInt(OutputStream s, int v) throws IOException {
-        byte[] bytes = {
-            (byte) ((v >> 24) & 0xFF),
-            (byte) ((v >> 16) & 0xFF),
-            (byte) ((v >> 8) & 0xFF),
-            (byte) (v & 0xFF)
-        };
-        s.write(bytes);
-        return bytes;
-    }
-
-    static String decodeString(InputStream s) throws IOException {
-        return decodeString(s, StandardCharsets.UTF_8);
-    }
-
-    static String decodeString(InputStream s, String charset) throws IOException {
-        return decodeString(s, Charset.forName(charset));
-    }
-
-    static String decodeString(InputStream s, Charset cs) throws IOException {
-        byte[] bytes = readRLEBytes(s);
-        return new String(bytes, cs);
-    }
-
-    static BigInteger decodeBigInt(InputStream s) throws IOException {
-        return new BigInteger(readRLEBytes(s));
-    }
-
-    static byte[] readRLEBytes(InputStream s) throws IOException {
-        int len = decodeInt(s);
-        byte[] bytes = new byte[len];
-        IoUtils.readFully(s, bytes);
-        return bytes;
-    }
-
-    static int decodeInt(InputStream s) throws IOException {
-        byte[] bytes = {0, 0, 0, 0};
-        IoUtils.readFully(s, bytes);
-        return ((bytes[0] & 0xFF) << 24)
-                | ((bytes[1] & 0xFF) << 16)
-                | ((bytes[2] & 0xFF) << 8)
-                | (bytes[3] & 0xFF);
-    }
-}


[12/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common utilities code from sshd-core into sshd-common (new artifact)

Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/security/AbstractSecurityProviderRegistrar.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/security/AbstractSecurityProviderRegistrar.java b/sshd-core/src/main/java/org/apache/sshd/common/util/security/AbstractSecurityProviderRegistrar.java
deleted file mode 100644
index b665166..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/security/AbstractSecurityProviderRegistrar.java
+++ /dev/null
@@ -1,129 +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.security;
-
-import java.security.Provider;
-import java.security.Security;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.TreeMap;
-import java.util.concurrent.atomic.AtomicReference;
-
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.logging.AbstractLoggingBean;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class AbstractSecurityProviderRegistrar
-                extends AbstractLoggingBean
-                implements SecurityProviderRegistrar {
-    protected final Map<String, Object> props = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
-    protected final Map<Class<?>, Map<String, Boolean>> supportedEntities = new HashMap<>();
-    protected final AtomicReference<Provider> providerHolder = new AtomicReference<>(null);
-
-    private final String name;
-
-    protected AbstractSecurityProviderRegistrar(String name) {
-        this.name = ValidateUtils.checkNotNullAndNotEmpty(name, "No name provided");
-    }
-
-    @Override
-    public final String getName() {
-        return name;
-    }
-
-    @Override
-    public Map<String, Object> getProperties() {
-        return props;
-    }
-
-    @Override
-    public boolean isSecurityEntitySupported(Class<?> entityType, String name) {
-        Map<String, Boolean> supportMap;
-        synchronized (supportedEntities) {
-            supportMap = supportedEntities.computeIfAbsent(
-                    entityType, k -> new TreeMap<>(String.CASE_INSENSITIVE_ORDER));
-        }
-
-        Boolean supportFlag;
-        synchronized (supportMap) {
-            supportFlag = supportMap.computeIfAbsent(
-                    name, k -> SecurityProviderRegistrar.super.isSecurityEntitySupported(entityType, name));
-        }
-
-        return supportFlag;
-    }
-
-    /**
-     * Attempts to see if a provider with this name already registered. If not,
-     * then uses reflection API in order to load and instantiate the specified
-     * <tt>providerClassName</tt>
-     *
-     * @param providerClassName The fully-qualified class name to instantiate
-     * if a provider not already registered
-     * @return The resolved {@link Provider} instance - <B>Note:</B> the result
-     * is <U>cached</U> - i.e., successful resolution result will not cause
-     * the code to re-resolve the provider
-     * @throws ReflectiveOperationException If failed to instantiate the provider
-     * @throws UnsupportedOperationException If registrar not supported
-     * @see #isSupported()
-     * @see Security#getProvider(String)
-     * @see #createProviderInstance(String)
-     */
-    protected Provider getOrCreateProvider(String providerClassName) throws ReflectiveOperationException {
-        if (!isSupported()) {
-            throw new UnsupportedOperationException("Provider not supported");
-        }
-
-        Provider provider;
-        boolean created = false;
-        synchronized (providerHolder) {
-            provider = providerHolder.get();
-            if (provider != null) {
-                return provider;
-            }
-
-            provider = Security.getProvider(getName());
-            if (provider == null) {
-                provider = createProviderInstance(providerClassName);
-                created = true;
-            }
-            providerHolder.set(provider);
-        }
-
-        if (created) {
-            log.info("getOrCreateProvider({}) created instance of {}", getName(), providerClassName);
-        } else {
-            log.info("getOrCreateProvider({}) resolved instance of {}", getName(), provider.getClass().getName());
-        }
-
-        return provider;
-    }
-
-    protected Provider createProviderInstance(String providerClassName) throws ReflectiveOperationException {
-        return SecurityProviderChoice.createProviderInstance(getClass(), providerClassName);
-    }
-
-    @Override
-    public String toString() {
-        return getClass().getSimpleName() + "[" + getName() + "]";
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/security/SecurityEntityFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/security/SecurityEntityFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/util/security/SecurityEntityFactory.java
deleted file mode 100644
index 94b9454..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/security/SecurityEntityFactory.java
+++ /dev/null
@@ -1,194 +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.security;
-
-import java.lang.reflect.Method;
-import java.security.GeneralSecurityException;
-import java.security.Provider;
-import java.util.Objects;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-
-/**
- * @param <T> Type of security entity being generated by this factory
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface SecurityEntityFactory<T> {
-    Class<T> getEntityType();
-
-    T getInstance(String algorithm) throws GeneralSecurityException;
-
-    /**
-     * Uses reflection in order to wrap the {@code getInstance} method(s)
-     * as a security entity factory.
-     *
-     * @param <F> Type of entity being generated by the factor
-     * @param entityType The entity type class
-     * @param registrar The {@code SecurityProviderRegistrar} to use - if
-     * {@code null} then default provider is used (if specified).
-     * @param defaultProvider Default provider choice to use if no registrar
-     * provided. If {@code null}/empty then JCE default is used
-     * @return The {@link SecurityEntityFactory} for the entity
-     * @throws ReflectiveOperationException If failed to create the factory
-     * @see #toDefaultFactory(Class)
-     * @see #toNamedProviderFactory(Class, String)
-     * @see #toProviderInstanceFactory(Class, Provider)
-     * @see SecurityProviderChoice#isNamedProviderUsed()
-     * @see SecurityProviderChoice#getSecurityProvider()
-     */
-    static <F> SecurityEntityFactory<F> toFactory(
-            Class<F> entityType, SecurityProviderChoice registrar, SecurityProviderChoice defaultProvider)
-            throws ReflectiveOperationException {
-        if (registrar == null) {
-            if ((defaultProvider == null) || (defaultProvider == SecurityProviderChoice.EMPTY)) {
-                return toDefaultFactory(entityType);
-            } else if (defaultProvider.isNamedProviderUsed()) {
-                return toNamedProviderFactory(entityType, defaultProvider.getName());
-            } else {
-                return toProviderInstanceFactory(entityType, defaultProvider.getSecurityProvider());
-            }
-        } else if (registrar.isNamedProviderUsed()) {
-            return toNamedProviderFactory(entityType, registrar.getName());
-        } else {
-            return toProviderInstanceFactory(entityType, registrar.getSecurityProvider());
-        }
-    }
-
-    static <F> SecurityEntityFactory<F> toDefaultFactory(Class<F> entityType)
-            throws ReflectiveOperationException {
-        Method m = entityType.getDeclaredMethod("getInstance", String.class);
-        return new SecurityEntityFactory<F>() {
-            private final String s = SecurityEntityFactory.class.getSimpleName()
-                    + "[" + entityType.getSimpleName() + "]"
-                    + "[default]";
-
-            @Override
-            public Class<F> getEntityType() {
-                return entityType;
-            }
-
-            @Override
-            public F getInstance(String algorithm) throws GeneralSecurityException {
-                try {
-                    Object value = m.invoke(null, algorithm);
-                    return entityType.cast(value);
-                } catch (ReflectiveOperationException t) {
-                    Throwable e = GenericUtils.peelException(t);
-                    if (e instanceof GeneralSecurityException) {
-                        throw (GeneralSecurityException) e;
-                    } else if (e instanceof RuntimeException) {
-                        throw (RuntimeException) e;
-                    } else if (e instanceof Error) {
-                        throw (Error) e;
-                    } else {
-                        throw new GeneralSecurityException(e);
-                    }
-                }
-            }
-
-            @Override
-            public String toString() {
-                return s;
-            }
-        };
-    }
-
-    static <F> SecurityEntityFactory<F> toNamedProviderFactory(Class<F> entityType, String name)
-            throws ReflectiveOperationException {
-        ValidateUtils.checkNotNullAndNotEmpty(name, "No provider name specified");
-        Method m = entityType.getDeclaredMethod("getInstance", String.class, String.class);
-        return new SecurityEntityFactory<F>() {
-            private final String s = SecurityEntityFactory.class.getSimpleName()
-                    + "[" + entityType.getSimpleName() + "]"
-                    + "[" + name + "]";
-
-            @Override
-            public Class<F> getEntityType() {
-                return entityType;
-            }
-
-            @Override
-            public F getInstance(String algorithm) throws GeneralSecurityException {
-                try {
-                    Object value = m.invoke(null, algorithm, name);
-                    return entityType.cast(value);
-                } catch (ReflectiveOperationException t) {
-                    Throwable e = GenericUtils.peelException(t);
-                    if (e instanceof GeneralSecurityException) {
-                        throw (GeneralSecurityException) e;
-                    } else if (e instanceof RuntimeException) {
-                        throw (RuntimeException) e;
-                    } else if (e instanceof Error) {
-                        throw (Error) e;
-                    } else {
-                        throw new GeneralSecurityException(e);
-                    }
-                }
-            }
-
-            @Override
-            public String toString() {
-                return s;
-            }
-        };
-    }
-
-    static <F> SecurityEntityFactory<F> toProviderInstanceFactory(Class<F> entityType, Provider provider)
-            throws ReflectiveOperationException {
-        Objects.requireNonNull(provider, "No provider instance");
-        Method m = entityType.getDeclaredMethod("getInstance", String.class, Provider.class);
-        return new SecurityEntityFactory<F>() {
-            private final String s = SecurityEntityFactory.class.getSimpleName()
-                    + "[" + entityType.getSimpleName() + "]"
-                    + "[" + Provider.class.getSimpleName() + "]"
-                    + "[" + provider.getName() + "]";
-
-            @Override
-            public Class<F> getEntityType() {
-                return entityType;
-            }
-
-            @Override
-            public F getInstance(String algorithm) throws GeneralSecurityException {
-                try {
-                    Object value = m.invoke(null, algorithm, provider);
-                    return entityType.cast(value);
-                } catch (ReflectiveOperationException t) {
-                    Throwable e = GenericUtils.peelException(t);
-                    if (e instanceof GeneralSecurityException) {
-                        throw (GeneralSecurityException) e;
-                    } else if (e instanceof RuntimeException) {
-                        throw (RuntimeException) e;
-                    } else if (e instanceof Error) {
-                        throw (Error) e;
-                    } else {
-                        throw new GeneralSecurityException(e);
-                    }
-                }
-            }
-
-            @Override
-            public String toString() {
-                return s;
-            }
-        };
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/security/SecurityProviderChoice.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/security/SecurityProviderChoice.java b/sshd-core/src/main/java/org/apache/sshd/common/util/security/SecurityProviderChoice.java
deleted file mode 100644
index c12e747..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/security/SecurityProviderChoice.java
+++ /dev/null
@@ -1,130 +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.security;
-
-import java.security.Provider;
-import java.util.Objects;
-
-import org.apache.sshd.common.NamedResource;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.threads.ThreadUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface SecurityProviderChoice extends NamedResource {
-    SecurityProviderChoice EMPTY = new SecurityProviderChoice() {
-        @Override
-        public String getName() {
-            return null;
-        }
-
-        @Override
-        public boolean isNamedProviderUsed() {
-            return false;
-        }
-
-        @Override
-        public Provider getSecurityProvider() {
-            return null;
-        }
-
-        @Override
-        public String toString() {
-            return "EMPTY";
-        }
-    };
-
-    /**
-     * @return {@code true} if to use the provider's name rather than its
-     * {@link Provider} instance - default={@code true}.
-     */
-    default boolean isNamedProviderUsed() {
-        return true;
-    }
-
-    /**
-     * @return The security {@link Provider} to use in case {@link #isNamedProviderUsed()}
-     * is {@code false}. Can be {@code null} if {@link #isNamedProviderUsed()} is {@code true},
-     * but not recommended.
-     */
-    Provider getSecurityProvider();
-
-    static SecurityProviderChoice toSecurityProviderChoice(String name) {
-        ValidateUtils.checkNotNullAndNotEmpty(name, "No name provided");
-        return new SecurityProviderChoice() {
-            private final String s = SecurityProviderChoice.class.getSimpleName() + "[" + name + "]";
-
-            @Override
-            public String getName() {
-                return name;
-            }
-
-            @Override
-            public boolean isNamedProviderUsed() {
-                return true;
-            }
-
-            @Override
-            public Provider getSecurityProvider() {
-                return null;
-            }
-
-            @Override
-            public String toString() {
-                return s;
-            }
-        };
-    }
-
-    static SecurityProviderChoice toSecurityProviderChoice(Provider provider) {
-        Objects.requireNonNull(provider, "No provider instance");
-        return new SecurityProviderChoice() {
-            private final String s = SecurityProviderChoice.class.getSimpleName()
-                    + "[" + Provider.class.getSimpleName() + "]"
-                    + "[" + provider.getName() + "]";
-
-            @Override
-            public String getName() {
-                return provider.getName();
-            }
-
-            @Override
-            public boolean isNamedProviderUsed() {
-                return false;
-            }
-
-            @Override
-            public Provider getSecurityProvider() {
-                return provider;
-            }
-
-            @Override
-            public String toString() {
-                return s;
-            }
-        };
-    }
-
-    static Provider createProviderInstance(Class<?> anchor, String providerClassName)
-            throws ReflectiveOperationException {
-        return ThreadUtils.createDefaultInstance(anchor, Provider.class, providerClassName);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/security/SecurityProviderRegistrar.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/security/SecurityProviderRegistrar.java b/sshd-core/src/main/java/org/apache/sshd/common/util/security/SecurityProviderRegistrar.java
deleted file mode 100644
index 31e428b..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/security/SecurityProviderRegistrar.java
+++ /dev/null
@@ -1,337 +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.security;
-
-import java.security.KeyFactory;
-import java.security.KeyPairGenerator;
-import java.security.MessageDigest;
-import java.security.Provider;
-import java.security.Security;
-import java.security.Signature;
-import java.security.cert.CertificateFactory;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.function.Predicate;
-
-import javax.crypto.Cipher;
-import javax.crypto.KeyAgreement;
-import javax.crypto.Mac;
-
-import org.apache.sshd.common.OptionalFeature;
-import org.apache.sshd.common.PropertyResolver;
-import org.apache.sshd.common.PropertyResolverUtils;
-import org.apache.sshd.common.SyspropsMapWrapper;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.IgnoringEmptyMap;
-import org.apache.sshd.common.util.ValidateUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface SecurityProviderRegistrar extends SecurityProviderChoice, OptionalFeature, PropertyResolver {
-    /**
-     * Base name for configuration properties related to security providers
-     */
-    String CONFIG_PROP_BASE = "org.apache.sshd.security.provider";
-
-    /**
-     * Property used to configure whether the provider is enabled regardless of
-     * whether it is supported.
-     *
-     * @see #isEnabled()
-     */
-    String ENABLED_PROPERTY = "enabled";
-
-    /**
-     * Property used to configure whether to use the provider's name rather than its
-     * {@link Provider} instance
-     *
-     * @see #isNamedProviderUsed()
-     */
-    String NAMED_PROVIDER_PROPERTY = "useNamed";
-
-    String ALL_OPTIONS_VALUE = "all";
-    String ALL_OPTIONS_WILDCARD = "*";
-
-    String NO_OPTIONS_VALUE = "none";
-
-    /**
-     * All the entities that are used in calls to {@link #isSecurityEntitySupported(Class, String)}
-     */
-    List<Class<?>> SECURITY_ENTITIES =
-            Collections.unmodifiableList(
-                    Arrays.asList(
-                            Cipher.class, KeyFactory.class, MessageDigest.class,
-                            KeyPairGenerator.class, KeyAgreement.class, Mac.class,
-                            Signature.class, CertificateFactory.class));
-
-    default String getBasePropertyName() {
-        return CONFIG_PROP_BASE + "." + getName();
-    }
-
-    default String getConfigurationPropertyName(String name) {
-        return getBasePropertyName() + "." + name;
-    }
-
-    /**
-     * @return {@code true} if the provider is enabled regardless of
-     * whether it is supported - default={@code true}. <B>Note:</B>
-     * checks if the provider has been <U>programmatically</U> disabled
-     * via {@link SecurityUtils#setAPrioriDisabledProvider(String, boolean)}
-     * @see #ENABLED_PROPERTY
-     */
-    default boolean isEnabled() {
-        if (SecurityUtils.isAPrioriDisabledProvider(getName())) {
-            return false;
-        }
-
-        return this.getBooleanProperty(getConfigurationPropertyName(ENABLED_PROPERTY), true);
-    }
-
-    @Override
-    default PropertyResolver getParentPropertyResolver() {
-        return SyspropsMapWrapper.SYSPROPS_RESOLVER;
-    }
-
-    @Override
-    default Map<String, Object> getProperties() {
-        return IgnoringEmptyMap.getInstance();
-    }
-
-    /**
-     * @param transformation The requested {@link Cipher} transformation
-     * @return {@code true} if this security provider supports the transformation
-     * @see #isSecurityEntitySupported(Class, String)
-     */
-    default boolean isCipherSupported(String transformation) {
-        return isSecurityEntitySupported(Cipher.class, transformation);
-    }
-
-    /**
-     * @param algorithm The {@link KeyFactory} algorithm
-     * @return {@code true} if this security provider supports the algorithm
-     * @see #isSecurityEntitySupported(Class, String)
-     */
-    default boolean isKeyFactorySupported(String algorithm) {
-        return isSecurityEntitySupported(KeyFactory.class, algorithm);
-    }
-
-    /**
-     * @param algorithm The {@link MessageDigest} algorithm
-     * @return {@code true} if this security provider supports the algorithm
-     * @see #isSecurityEntitySupported(Class, String)
-     */
-    default boolean isMessageDigestSupported(String algorithm) {
-        return isSecurityEntitySupported(MessageDigest.class, algorithm);
-    }
-
-    /**
-     * @param algorithm The {@link KeyPairGenerator} algorithm
-     * @return {@code true} if this security provider supports the algorithm
-     * @see #isSecurityEntitySupported(Class, String)
-     */
-    default boolean isKeyPairGeneratorSupported(String algorithm) {
-        return isSecurityEntitySupported(KeyPairGenerator.class, algorithm);
-    }
-
-    /**
-     * @param algorithm The {@link KeyAgreement} algorithm
-     * @return {@code true} if this security provider supports the algorithm
-     * @see #isSecurityEntitySupported(Class, String)
-     */
-    default boolean isKeyAgreementSupported(String algorithm) {
-        return isSecurityEntitySupported(KeyAgreement.class, algorithm);
-    }
-
-    /**
-     * @param algorithm The {@link Mac} algorithm
-     * @return {@code true} if this security provider supports the algorithm
-     * @see #isSecurityEntitySupported(Class, String)
-     */
-    default boolean isMacSupported(String algorithm) {
-        return isSecurityEntitySupported(Mac.class, algorithm);
-    }
-
-    /**
-     * @param algorithm The {@link Signature} algorithm
-     * @return {@code true} if this security provider supports the algorithm
-     * @see #isSecurityEntitySupported(Class, String)
-     */
-    default boolean isSignatureSupported(String algorithm) {
-        return isSecurityEntitySupported(Signature.class, algorithm);
-    }
-
-    /**
-     * @param type The {@link CertificateFactory} type
-     * @return {@code true} if this security provider supports the algorithm
-     * @see #isSecurityEntitySupported(Class, String)
-     */
-    default boolean isCertificateFactorySupported(String type) {
-        return isSecurityEntitySupported(CertificateFactory.class, type);
-    }
-
-    /**
-     * @param entityType The requested entity type - its simple name serves to
-     * build the configuration property name.
-     * @return Configuration value to use if no specific configuration provided
-     * - default=empty
-     * @see #isSecurityEntitySupported(Class, String)
-     */
-    default String getDefaultSecurityEntitySupportValue(Class<?> entityType) {
-        return "";
-    }
-
-    default boolean isSecurityEntitySupported(Class<?> entityType, String name) {
-        String defaultValue = getDefaultSecurityEntitySupportValue(entityType);
-        return isSecurityEntitySupported(this, entityType, name, defaultValue);
-    }
-
-    /**
-     * @return {@code true} if to use the provider's name rather than its
-     * {@link Provider} instance - default={@code true}
-     * @see #NAMED_PROVIDER_PROPERTY
-     * @see #getSecurityProvider()
-     * @see #registerSecurityProvider(SecurityProviderRegistrar)
-     */
-    @Override
-    default boolean isNamedProviderUsed() {
-        return PropertyResolverUtils.getBooleanProperty(this,
-                getConfigurationPropertyName(NAMED_PROVIDER_PROPERTY),
-                SecurityProviderChoice.super.isNamedProviderUsed());
-    }
-
-    /**
-     * @param v Value to be examined
-     * @return {@code true} if the value equals (case insensitive) to
-     * either {@link #ALL_OPTIONS_VALUE} or {@link #ALL_OPTIONS_WILDCARD}
-     */
-    static boolean isAllOptionsValue(String v) {
-        return ALL_OPTIONS_VALUE.equalsIgnoreCase(v)
-            || ALL_OPTIONS_WILDCARD.equalsIgnoreCase(v);
-    }
-
-    /**
-     * Checks whether the requested entity type algorithm/name is listed
-     * as supported by the registrar's configuration
-     *
-     * @param registrar The {@link SecurityProviderRegistrar}
-     * @param entityType The requested entity type - its simple name serves to
-     * build the configuration property name.
-     * @param name The requested algorithm/name - <B>Note:</B> if the requested
-     * entity is a {@link Cipher} then the argument is assumed to be a possible
-     * &quot;/&quot; separated transformation and parsed as such in order to
-     * retrieve the pure cipher name
-     * @param defaultValue Configuration value to use if no specific configuration provided
-     * @return {@code true} registrar is supported and the value is listed
-     * (case <U>insensitive</U>) or * the property is one of the &quot;all&quot; markers
-     * @see SecurityProviderRegistrar#isSupported()
-     * @see #isAllOptionsValue(String)
-     */
-    static boolean isSecurityEntitySupported(SecurityProviderRegistrar registrar, Class<?> entityType, String name, String defaultValue) {
-        return Objects.requireNonNull(registrar, "No registrar instance").isSupported()
-            && isSecurityEntitySupported(registrar, registrar.getConfigurationPropertyName(entityType.getSimpleName()), entityType, name, defaultValue);
-    }
-
-    static boolean isSecurityEntitySupported(PropertyResolver resolver, String propName, Class<?> entityType, String name, String defaultValue) {
-        if (GenericUtils.isEmpty(name)) {
-            return false;
-        }
-
-        String propValue = resolver.getString(propName);
-        if (GenericUtils.isEmpty(propValue)) {
-            propValue = defaultValue;
-        }
-
-        if (NO_OPTIONS_VALUE.equalsIgnoreCase(propValue)) {
-            return false;
-        }
-
-        String[] values = GenericUtils.split(propValue, ',');
-        if (GenericUtils.isEmpty(values)) {
-            return false;
-        }
-
-        if ((values.length == 1) && isAllOptionsValue(values[0])) {
-            return true;
-        }
-
-        String effectiveName = getEffectiveSecurityEntityName(entityType, name);
-        int index = Arrays.binarySearch(values, effectiveName, String.CASE_INSENSITIVE_ORDER);
-        return index >= 0;
-    }
-
-    /**
-     * Determines the &quot;pure&quot; security entity name - e.g., for {@link Cipher}s
-     * it strips the trailing transformation specification in order to extract the
-     * base cipher name - e.g., &quot;AES/CBC/NoPadding&quot; =&gt; &quot;AES&quot;
-     *
-     * @param entityType The security entity type - ignored if {@code null}
-     * @param name The effective name - ignored if {@code null}/empty
-     * @return The resolved name
-     */
-    static String getEffectiveSecurityEntityName(Class<?> entityType, String name) {
-        if ((entityType == null) || GenericUtils.isEmpty(name) || (!Cipher.class.isAssignableFrom(entityType))) {
-            return name;
-        }
-
-        int pos = name.indexOf('/');
-        return (pos > 0) ? name.substring(0, pos) : name;
-    }
-
-    /**
-     * Attempts to register the security provider represented by the registrar
-     * if not already registered. <B>Note:</B> if {@link SecurityProviderRegistrar#isNamedProviderUsed()}
-     * is {@code true} then the generated provider will be added to the system's
-     * list of known providers.
-     *
-     * @param registrar The {@link SecurityProviderRegistrar}
-     * @return {@code true} if no provider was previously registered
-     * @see Security#getProvider(String)
-     * @see SecurityProviderRegistrar#getSecurityProvider()
-     * @see Security#addProvider(Provider)
-     */
-    static boolean registerSecurityProvider(SecurityProviderRegistrar registrar) {
-        String name = ValidateUtils.checkNotNullAndNotEmpty(
-                (registrar == null) ? null : registrar.getName(), "No name for registrar=%s", registrar);
-        Provider p = Security.getProvider(name);
-        if (p != null) {
-            return false;
-        }
-
-        p = ValidateUtils.checkNotNull(
-                registrar.getSecurityProvider(), "No provider created for registrar of %s", name);
-        if (registrar.isNamedProviderUsed()) {
-            Security.addProvider(p);
-        }
-
-        return true;
-    }
-
-    static SecurityProviderRegistrar findSecurityProviderRegistrarBySecurityEntity(
-            Predicate<? super SecurityProviderRegistrar> entitySelector,
-            Collection<? extends SecurityProviderRegistrar> registrars) {
-        return GenericUtils.findFirstMatchingMember(
-            r -> r.isEnabled() && r.isSupported() && entitySelector.test(r), registrars);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java
deleted file mode 100644
index ee755e6..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java
+++ /dev/null
@@ -1,759 +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.security;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.math.BigInteger;
-import java.nio.file.Path;
-import java.security.GeneralSecurityException;
-import java.security.InvalidKeyException;
-import java.security.Key;
-import java.security.KeyFactory;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.Signature;
-import java.security.cert.CertificateFactory;
-import java.security.spec.InvalidKeySpecException;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.TreeMap;
-import java.util.TreeSet;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.function.Predicate;
-
-import javax.crypto.Cipher;
-import javax.crypto.KeyAgreement;
-import javax.crypto.Mac;
-import javax.crypto.spec.DHParameterSpec;
-
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.config.keys.PrivateKeyEntryDecoder;
-import org.apache.sshd.common.config.keys.PublicKeyEntryDecoder;
-import org.apache.sshd.common.config.keys.loader.KeyPairResourceParser;
-import org.apache.sshd.common.config.keys.loader.openssh.OpenSSHKeyPairResourceParser;
-import org.apache.sshd.common.config.keys.loader.pem.PEMResourceParserUtils;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.random.JceRandomFactory;
-import org.apache.sshd.common.random.RandomFactory;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.buffer.Buffer;
-import org.apache.sshd.common.util.security.bouncycastle.BouncyCastleGeneratorHostKeyProvider;
-import org.apache.sshd.common.util.security.bouncycastle.BouncyCastleKeyPairResourceParser;
-import org.apache.sshd.common.util.security.bouncycastle.BouncyCastleRandomFactory;
-import org.apache.sshd.common.util.security.eddsa.EdDSASecurityProviderUtils;
-import org.apache.sshd.common.util.threads.ThreadUtils;
-import org.apache.sshd.server.keyprovider.AbstractGeneratorHostKeyProvider;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Specific security providers related code
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public final class SecurityUtils {
-    /**
-     * Bouncycastle JCE provider name
-     */
-    public static final String BOUNCY_CASTLE = "BC";
-
-    /**
-     * EDDSA support - should match {@code EdDSAKey.KEY_ALGORITHM}
-     */
-    public static final String EDDSA = "EdDSA";
-
-    // A copy-paste from the original, but we don't want to drag the classes into the classpath
-    // See EdDSAEngine.SIGNATURE_ALGORITHM
-    public static final String CURVE_ED25519_SHA512 = "NONEwithEdDSA";
-
-    /**
-     * System property used to configure the value for the maximum supported Diffie-Hellman
-     * Group Exchange key size. If not set, then an internal auto-discovery mechanism is employed.
-     * If set to negative value then Diffie-Hellman Group Exchange is disabled. If set to a
-     * negative value then Diffie-Hellman Group Exchange is disabled
-     */
-    public static final String MAX_DHGEX_KEY_SIZE_PROP = "org.apache.sshd.maxDHGexKeySize";
-
-    /**
-     * The min. key size value used for testing whether Diffie-Hellman Group Exchange
-     * is supported or not. According to <A HREF="https://tools.ietf.org/html/rfc4419">RFC 4419</A>
-     * section 3: &quot;Servers and clients SHOULD support groups with a modulus length of k
-     * bits, where 1024 <= k <= 8192&quot;.
-     * </code>
-     */
-    public static final int MIN_DHGEX_KEY_SIZE = 1024;
-    // Keys of size > 1024 are not support by default with JCE
-    public static final int DEFAULT_DHGEX_KEY_SIZE = MIN_DHGEX_KEY_SIZE;
-    public static final int PREFERRED_DHGEX_KEY_SIZE = 4096;
-    public static final int MAX_DHGEX_KEY_SIZE = 8192;
-
-    /**
-     * Comma separated list of fully qualified {@link SecurityProviderRegistrar}s
-     * to automatically register
-     */
-    public static final String SECURITY_PROVIDER_REGISTRARS = "org.apache.sshd.security.registrars";
-    public static final List<String> DEFAULT_SECURITY_PROVIDER_REGISTRARS =
-            Collections.unmodifiableList(
-                    Arrays.asList(
-                            "org.apache.sshd.common.util.security.bouncycastle.BouncyCastleSecurityProviderRegistrar",
-                            "org.apache.sshd.common.util.security.eddsa.EdDSASecurityProviderRegistrar"));
-
-
-    /**
-     * System property used to control whether to automatically register the
-     * {@code Bouncyastle} JCE provider
-     * @deprecated Please use &quot;org.apache.sshd.security.provider.BC.enabled&quot;
-     */
-    @Deprecated
-    public static final String REGISTER_BOUNCY_CASTLE_PROP = "org.apache.sshd.registerBouncyCastle";
-
-    /**
-     * System property used to control whether Elliptic Curves are supported or not.
-     * If not set then the support is auto-detected. <B>Note:</B> if set to {@code true}
-     * it is up to the user to make sure that indeed there is a provider for them
-     */
-    public static final String ECC_SUPPORTED_PROP = "org.apache.sshd.eccSupport";
-
-    /**
-     * System property used to decide whether EDDSA curves are supported or not
-     * (in addition or even in spite of {@link #isEDDSACurveSupported()}). If not
-     * set or set to {@code true}, then the existence of the optional support classes
-     * determines the support.
-     * @deprecated Please use &quot;org.apache.sshd.security.provider.EdDSA.enabled&qupt;
-     */
-    @Deprecated
-    public static final String EDDSA_SUPPORTED_PROP = "org.apache.sshd.eddsaSupport";
-
-    public static final String PROP_DEFAULT_SECURITY_PROVIDER = "org.apache.sshd.security.defaultProvider";
-
-    private static final AtomicInteger MAX_DHG_KEY_SIZE_HOLDER = new AtomicInteger(0);
-
-    /*
-     * NOTE: we use a LinkedHashMap in order to preserve registration order
-     * in case several providers support the same security entity
-     */
-    private static final Map<String, SecurityProviderRegistrar> REGISTERED_PROVIDERS = new LinkedHashMap<>();
-    private static final AtomicReference<KeyPairResourceParser> KEYPAIRS_PARSER_HODLER = new AtomicReference<>();
-    // If an entry already exists for the named provider, then it overrides its SecurityProviderRegistrar#isEnabled()
-    private static final Set<String> APRIORI_DISABLED_PROVIDERS = new TreeSet<>();
-    private static final AtomicBoolean REGISTRATION_STATE_HOLDER = new AtomicBoolean(false);
-    private static final Map<Class<?>, Map<String, SecurityEntityFactory<?>>> SECURITY_ENTITY_FACTORIES = new HashMap<>();
-
-    private static final AtomicReference<SecurityProviderChoice> DEFAULT_PROVIDER_HOLDER = new AtomicReference<>();
-
-    private static Boolean hasEcc;
-
-    private SecurityUtils() {
-        throw new UnsupportedOperationException("No instance");
-    }
-
-    /**
-     * @param name The provider's name - never {@code null}/empty
-     * @return {@code true} if the provider is marked as disabled a-priori
-     * @see #setAPrioriDisabledProvider(String, boolean)
-     */
-    public static boolean isAPrioriDisabledProvider(String name) {
-        ValidateUtils.checkNotNullAndNotEmpty(name, "No provider name specified");
-        synchronized (APRIORI_DISABLED_PROVIDERS) {
-            return APRIORI_DISABLED_PROVIDERS.contains(name);
-        }
-    }
-
-    /**
-     * Marks a provider's registrar as &quot;a-priori&quot; <U>programatically</U>
-     * so that when its {@link SecurityProviderRegistrar#isEnabled()} is eventually
-     * consulted it will return {@code false} regardless of the configured value for
-     * the specific provider registrar instance. <B>Note:</B> has no effect if the
-     * provider has already been registered.
-     *
-     * @param name The provider's name - never {@code null}/empty
-     * @param disabled {@code true} whether to disable it a-priori
-     * @see #isAPrioriDisabledProvider(String)
-     */
-    public static void setAPrioriDisabledProvider(String name, boolean disabled) {
-        ValidateUtils.checkNotNullAndNotEmpty(name, "No provider name specified");
-        synchronized (APRIORI_DISABLED_PROVIDERS) {
-            if (disabled) {
-                APRIORI_DISABLED_PROVIDERS.add(name);
-            } else {
-                APRIORI_DISABLED_PROVIDERS.remove(name);
-            }
-        }
-    }
-
-    /**
-     * @return A <U>copy</U> if the current a-priori disabled providers names
-     */
-    public static Set<String> getAPrioriDisabledProviders() {
-        synchronized (APRIORI_DISABLED_PROVIDERS) {
-            return new TreeSet<>(APRIORI_DISABLED_PROVIDERS);
-        }
-    }
-
-    /**
-     * @return {@code true} if Elliptic Curve Cryptography is supported
-     * @see #ECC_SUPPORTED_PROP
-     */
-    public static boolean isECCSupported() {
-        if (hasEcc == null) {
-            String propValue = System.getProperty(ECC_SUPPORTED_PROP);
-            if (GenericUtils.isEmpty(propValue)) {
-                try {
-                    getKeyPairGenerator(KeyUtils.EC_ALGORITHM);
-                    hasEcc = Boolean.TRUE;
-                } catch (Throwable t) {
-                    hasEcc = Boolean.FALSE;
-                }
-            } else {
-                Logger logger = LoggerFactory.getLogger(SecurityUtils.class);
-                logger.info("Override ECC support value: " + propValue);
-                hasEcc = Boolean.valueOf(propValue);
-            }
-        }
-
-        return hasEcc;
-    }
-
-    /**
-     * @return {@code true} if Diffie-Hellman Group Exchange is supported
-     * @see #getMaxDHGroupExchangeKeySize()
-     */
-    public static boolean isDHGroupExchangeSupported() {
-        return getMaxDHGroupExchangeKeySize() > 0;
-    }
-
-    /**
-     * @param keySize The expected key size
-     * @return {@code true} if Oakely Diffie-Hellman Group Exchange is supported
-     * for the specified key size
-     * @see #getMaxDHGroupExchangeKeySize()
-     */
-    public static boolean isDHOakelyGroupSupported(int keySize) {
-        return getMaxDHGroupExchangeKeySize() >= keySize;
-    }
-
-    /**
-     * @return The maximum supported Diffie-Hellman Group Exchange key size,
-     * or non-positive if not supported
-     */
-    public static int getMaxDHGroupExchangeKeySize() {
-        int maxSupportedKeySize;
-        synchronized (MAX_DHG_KEY_SIZE_HOLDER) {
-            maxSupportedKeySize = MAX_DHG_KEY_SIZE_HOLDER.get();
-            if (maxSupportedKeySize != 0) { // 1st time we are called ?
-                return maxSupportedKeySize;
-            }
-
-            String propValue = System.getProperty(MAX_DHGEX_KEY_SIZE_PROP);
-            if (GenericUtils.isEmpty(propValue)) {
-                maxSupportedKeySize = -1;
-                // Go down from max. to min. to ensure we stop at 1st maximum value success
-                for (int testKeySize = MAX_DHGEX_KEY_SIZE; testKeySize >= MIN_DHGEX_KEY_SIZE; testKeySize -= 1024) {
-                    if (isDHGroupExchangeSupported(testKeySize)) {
-                        maxSupportedKeySize = testKeySize;
-                        break;
-                    }
-                }
-            } else {
-                Logger logger = LoggerFactory.getLogger(SecurityUtils.class);
-                logger.info("Override max. DH group exchange key size: " + propValue);
-                maxSupportedKeySize = Integer.parseInt(propValue);
-                // negative is OK - means user wants to disable DH group exchange
-                ValidateUtils.checkTrue(maxSupportedKeySize != 0,
-                        "Configured " + MAX_DHGEX_KEY_SIZE_PROP + " value must be non-zero: %d", maxSupportedKeySize);
-            }
-
-            MAX_DHG_KEY_SIZE_HOLDER.set(maxSupportedKeySize);
-        }
-
-        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);
-
-        try {
-            BigInteger r = new BigInteger("0").setBit(maxKeySize - 1);
-            DHParameterSpec dhSkipParamSpec = new DHParameterSpec(r, r);
-            KeyPairGenerator kpg = getKeyPairGenerator("DH");
-            kpg.initialize(dhSkipParamSpec);
-            return true;
-        } catch (GeneralSecurityException t) {
-            return false;
-        }
-    }
-
-    public static SecurityProviderChoice getDefaultProviderChoice() {
-        SecurityProviderChoice choice;
-        synchronized (DEFAULT_PROVIDER_HOLDER) {
-            choice = DEFAULT_PROVIDER_HOLDER.get();
-            if (choice != null) {
-                return choice;
-            }
-
-            String name = System.getProperty(PROP_DEFAULT_SECURITY_PROVIDER);
-            choice = (GenericUtils.isEmpty(name) || "none".equalsIgnoreCase(name))
-                    ? SecurityProviderChoice.EMPTY
-                    : SecurityProviderChoice.toSecurityProviderChoice(name);
-            DEFAULT_PROVIDER_HOLDER.set(choice);
-        }
-
-        return choice;
-    }
-
-    public static void setDefaultProviderChoice(SecurityProviderChoice choice) {
-        DEFAULT_PROVIDER_HOLDER.set(choice);
-    }
-
-    /**
-     * @return A <U>copy</U> of the currently registered security providers
-     */
-    public static Set<String> getRegisteredProviders() {
-        // returns a COPY of the providers in order to avoid modifications
-        synchronized (REGISTERED_PROVIDERS) {
-            return new TreeSet<>(REGISTERED_PROVIDERS.keySet());
-        }
-    }
-
-    public static boolean isBouncyCastleRegistered() {
-        register();
-        return isProviderRegistered(BOUNCY_CASTLE);
-    }
-
-    public static boolean isProviderRegistered(String provider) {
-        return getRegisteredProvider(provider) != null;
-    }
-
-    public static SecurityProviderRegistrar getRegisteredProvider(String provider) {
-        ValidateUtils.checkNotNullAndNotEmpty(provider, "No provider name specified");
-        synchronized (REGISTERED_PROVIDERS) {
-            return REGISTERED_PROVIDERS.get(provider);
-        }
-    }
-
-    public static boolean isRegistrationCompleted() {
-        return REGISTRATION_STATE_HOLDER.get();
-    }
-
-    private static void register() {
-        synchronized (REGISTRATION_STATE_HOLDER) {
-            if (REGISTRATION_STATE_HOLDER.get()) {
-                return;
-            }
-
-            String regsList = System.getProperty(SECURITY_PROVIDER_REGISTRARS,
-                    GenericUtils.join(DEFAULT_SECURITY_PROVIDER_REGISTRARS, ','));
-            boolean bouncyCastleRegistered = false;
-            if ((GenericUtils.length(regsList) > 0) && (!"none".equalsIgnoreCase(regsList))) {
-                String[] classes = GenericUtils.split(regsList, ',');
-                Logger logger = LoggerFactory.getLogger(SecurityUtils.class);
-                boolean debugEnabled = logger.isDebugEnabled();
-                for (String registrarClass : classes) {
-                    SecurityProviderRegistrar r;
-                    try {
-                        r = ThreadUtils.createDefaultInstance(SecurityUtils.class, SecurityProviderRegistrar.class, registrarClass);
-                    } catch (ReflectiveOperationException t) {
-                        Throwable e = GenericUtils.peelException(t);
-                        logger.error("Failed ({}) to create default {} registrar instance: {}",
-                                     e.getClass().getSimpleName(), registrarClass, e.getMessage());
-                        if (e instanceof RuntimeException) {
-                            throw (RuntimeException) e;
-                        } else if (e instanceof Error) {
-                            throw (Error) e;
-                        } else {
-                            throw new RuntimeException(e);
-                        }
-                    }
-
-                    String name = r.getName();
-                    SecurityProviderRegistrar registeredInstance = registerSecurityProvider(r);
-                    if (registeredInstance == null) {
-                        if (debugEnabled) {
-                            logger.debug("register({}) not registered - enabled={}, supported={}",
-                                         name, r.isEnabled(), r.isSupported());
-                        }
-                        continue;   // provider not registered - e.g., disabled, not supported
-                    }
-
-                    if (BOUNCY_CASTLE.equalsIgnoreCase(name)) {
-                        bouncyCastleRegistered = true;
-                    }
-                }
-            }
-
-            SecurityProviderChoice choice = getDefaultProviderChoice();
-            if (((choice == null) || (choice == SecurityProviderChoice.EMPTY)) && bouncyCastleRegistered) {
-                setDefaultProviderChoice(SecurityProviderChoice.toSecurityProviderChoice(BOUNCY_CASTLE));
-            }
-
-            REGISTRATION_STATE_HOLDER.set(true);
-        }
-    }
-
-    /**
-     * @param registrar The registrar instance to register
-     * @return The registered instance - may be different than required
-     * if already registered. Returns {@code null} if not already registered
-     * and not enabled or not supported registrar.
-     */
-    public static SecurityProviderRegistrar registerSecurityProvider(SecurityProviderRegistrar registrar) {
-        Objects.requireNonNull(registrar, "No registrar instance to register");
-        String name = registrar.getName();
-        SecurityProviderRegistrar registeredInstance = getRegisteredProvider(name);
-        if ((registeredInstance == null) && registrar.isEnabled() && registrar.isSupported()) {
-            try {
-                SecurityProviderRegistrar.registerSecurityProvider(registrar);
-                synchronized (REGISTERED_PROVIDERS) {
-                    REGISTERED_PROVIDERS.put(name, registrar);
-                }
-
-                return registrar;
-            } catch (Throwable t) {
-                Logger logger = LoggerFactory.getLogger(SecurityUtils.class);
-                logger.error("Failed {} to register {} as a JCE provider: {}",
-                             t.getClass().getSimpleName(), name, t.getMessage());
-                throw new RuntimeException("Failed to register " + name + " as a JCE provider", t);
-            }
-        }
-
-        return registeredInstance;
-    }
-
-    ///////////////// Bouncycastle specific implementations //////////////////
-
-    /* -------------------------------------------------------------------- */
-
-    /**
-     * @param resourceKey An identifier of the key being loaded - used as
-     *                    argument to the {@link FilePasswordProvider#getPassword(String)}
-     *                    invocation
-     * @param inputStream The {@link InputStream} for the <U>private</U> key
-     * @param provider    A {@link FilePasswordProvider} - may be {@code null}
-     *                    if the loaded key is <U>guaranteed</U> not to be encrypted
-     * @return The loaded {@link KeyPair}
-     * @throws IOException              If failed to read/parse the input stream
-     * @throws GeneralSecurityException If failed to generate the keys
-     */
-    public static KeyPair loadKeyPairIdentity(String resourceKey, InputStream inputStream, FilePasswordProvider provider)
-            throws IOException, GeneralSecurityException {
-        KeyPairResourceParser parser = getKeyPairResourceParser();
-        if (parser == null) {
-            throw new NoSuchProviderException("No registered key-pair resource parser");
-        }
-
-        Collection<KeyPair> ids = parser.loadKeyPairs(resourceKey, provider, inputStream);
-        int numLoaded = GenericUtils.size(ids);
-        if (numLoaded <= 0) {
-            throw new InvalidKeyException("Unsupported private key file format: " + resourceKey);
-        }
-        if (numLoaded != 1) {
-            throw new InvalidKeySpecException("Multiple private key pairs N/A: " + resourceKey);
-        }
-
-        return ids.iterator().next();
-    }
-
-    /* -------------------------------------------------------------------- */
-
-    public static AbstractGeneratorHostKeyProvider createGeneratorHostKeyProvider(Path path) {
-        ValidateUtils.checkTrue(isBouncyCastleRegistered(), "BouncyCastle not registered");
-        return new BouncyCastleGeneratorHostKeyProvider(path);
-    }
-
-    public static KeyPairResourceParser getBouncycastleKeyPairResourceParser() {
-        ValidateUtils.checkTrue(isBouncyCastleRegistered(), "BouncyCastle not registered");
-        return BouncyCastleKeyPairResourceParser.INSTANCE;
-    }
-
-    /**
-     * @return If {@link #isBouncyCastleRegistered()} then a {@link BouncyCastleRandomFactory}
-     * instance, otherwise a {@link JceRandomFactory} one
-     */
-    public static RandomFactory getRandomFactory() {
-        if (isBouncyCastleRegistered()) {
-            return BouncyCastleRandomFactory.INSTANCE;
-        } else {
-            return JceRandomFactory.INSTANCE;
-        }
-    }
-
-    ///////////////////////////// ED25519 support ///////////////////////////////
-
-    /**
-     * @return {@code true} if EDDSA curves (e.g., {@code ed25519}) are supported
-     */
-    public static boolean isEDDSACurveSupported() {
-        register();
-
-        SecurityProviderRegistrar r = getRegisteredProvider(EDDSA);
-        return (r != null) && r.isEnabled() && r.isSupported();
-    }
-
-    /* -------------------------------------------------------------------- */
-
-    public static PublicKeyEntryDecoder<? extends PublicKey, ? extends PrivateKey> getEDDSAPublicKeyEntryDecoder() {
-        if (!isEDDSACurveSupported()) {
-            throw new UnsupportedOperationException(EDDSA + " provider N/A");
-        }
-
-        return EdDSASecurityProviderUtils.getEDDSAPublicKeyEntryDecoder();
-    }
-
-    public static PrivateKeyEntryDecoder<? extends PublicKey, ? extends PrivateKey> getOpenSSHEDDSAPrivateKeyEntryDecoder() {
-        if (!isEDDSACurveSupported()) {
-            throw new UnsupportedOperationException(EDDSA + " provider N/A");
-        }
-
-        return EdDSASecurityProviderUtils.getOpenSSHEDDSAPrivateKeyEntryDecoder();
-    }
-
-    public static org.apache.sshd.common.signature.Signature getEDDSASigner() {
-        if (isEDDSACurveSupported()) {
-            return EdDSASecurityProviderUtils.getEDDSASignature();
-        }
-
-        throw new UnsupportedOperationException(EDDSA + " Signer not available");
-    }
-
-    public static int getEDDSAKeySize(Key key) {
-        return EdDSASecurityProviderUtils.getEDDSAKeySize(key);
-    }
-
-    public static Class<? extends PublicKey> getEDDSAPublicKeyType() {
-        return isEDDSACurveSupported() ? EdDSASecurityProviderUtils.getEDDSAPublicKeyType() : PublicKey.class;
-    }
-
-    public static Class<? extends PrivateKey> getEDDSAPrivateKeyType() {
-        return isEDDSACurveSupported() ? EdDSASecurityProviderUtils.getEDDSAPrivateKeyType() : PrivateKey.class;
-    }
-
-    public static boolean compareEDDSAPPublicKeys(PublicKey k1, PublicKey k2) {
-        return isEDDSACurveSupported() ? EdDSASecurityProviderUtils.compareEDDSAPPublicKeys(k1, k2) : false;
-    }
-
-    public static boolean compareEDDSAPrivateKeys(PrivateKey k1, PrivateKey k2) {
-        return isEDDSACurveSupported() ? EdDSASecurityProviderUtils.compareEDDSAPrivateKeys(k1, k2) : false;
-    }
-
-    public static PublicKey recoverEDDSAPublicKey(PrivateKey key) throws GeneralSecurityException {
-        if (!isEDDSACurveSupported()) {
-            throw new NoSuchAlgorithmException(EDDSA + " provider not supported");
-        }
-
-        return EdDSASecurityProviderUtils.recoverEDDSAPublicKey(key);
-    }
-
-    public static PublicKey generateEDDSAPublicKey(String keyType, byte[] seed) throws GeneralSecurityException {
-        if (!KeyPairProvider.SSH_ED25519.equals(keyType)) {
-            throw new InvalidKeyException("Unsupported key type: " + keyType);
-        }
-
-        if (!isEDDSACurveSupported()) {
-            throw new NoSuchAlgorithmException(EDDSA + " provider not supported");
-        }
-
-        return EdDSASecurityProviderUtils.generateEDDSAPublicKey(seed);
-    }
-
-    public static <B extends Buffer> B putRawEDDSAPublicKey(B buffer, PublicKey key) {
-        if (!isEDDSACurveSupported()) {
-            throw new UnsupportedOperationException(EDDSA + " provider not supported");
-        }
-
-        return EdDSASecurityProviderUtils.putRawEDDSAPublicKey(buffer, key);
-    }
-
-    public static <B extends Buffer> B putEDDSAKeyPair(B buffer, KeyPair kp) {
-        return putEDDSAKeyPair(buffer, Objects.requireNonNull(kp, "No key pair").getPublic(), kp.getPrivate());
-    }
-
-    public static <B extends Buffer> B putEDDSAKeyPair(B buffer, PublicKey pubKey, PrivateKey prvKey) {
-        if (!isEDDSACurveSupported()) {
-            throw new UnsupportedOperationException(EDDSA + " provider not supported");
-        }
-
-        return EdDSASecurityProviderUtils.putEDDSAKeyPair(buffer, pubKey, prvKey);
-    }
-
-    public static KeyPair extractEDDSAKeyPair(Buffer buffer, String keyType) throws GeneralSecurityException {
-        if (!KeyPairProvider.SSH_ED25519.equals(keyType)) {
-            throw new InvalidKeyException("Unsupported key type: " + keyType);
-        }
-
-        if (!isEDDSACurveSupported()) {
-            throw new NoSuchAlgorithmException(EDDSA + " provider not supported");
-        }
-
-        throw new GeneralSecurityException("Full SSHD-440 implementation N/A");
-    }
-
-    //////////////////////////////////////////////////////////////////////////
-
-    public static KeyPairResourceParser getKeyPairResourceParser() {
-        KeyPairResourceParser parser;
-        synchronized (KEYPAIRS_PARSER_HODLER) {
-            parser = KEYPAIRS_PARSER_HODLER.get();
-            if (parser != null) {
-                return parser;
-            }
-
-            parser = KeyPairResourceParser.aggregate(
-                    PEMResourceParserUtils.PROXY,
-                    OpenSSHKeyPairResourceParser.INSTANCE);
-            KEYPAIRS_PARSER_HODLER.set(parser);
-        }
-
-        return parser;
-    }
-
-    /**
-     * @param parser The system-wide {@code KeyPairResourceParser} to use.
-     * If set to {@code null}, then the default parser will be re-constructed
-     * on next call to {@link #getKeyPairResourceParser()}
-     */
-    public static void setKeyPairResourceParser(KeyPairResourceParser parser) {
-        synchronized (KEYPAIRS_PARSER_HODLER) {
-            KEYPAIRS_PARSER_HODLER.set(parser);
-        }
-    }
-
-    //////////////////////////// Security entities factories /////////////////////////////
-
-    @SuppressWarnings("unchecked")
-    public static <T> SecurityEntityFactory<T> resolveSecurityEntityFactory(
-            Class<T> entityType, String algorithm, Predicate<? super SecurityProviderRegistrar> entitySelector) {
-        Map<String, SecurityEntityFactory<?>> factoriesMap;
-        synchronized (SECURITY_ENTITY_FACTORIES) {
-            factoriesMap =
-                    SECURITY_ENTITY_FACTORIES.computeIfAbsent(
-                            entityType, k -> new TreeMap<>(String.CASE_INSENSITIVE_ORDER));
-        }
-
-        String effectiveName = SecurityProviderRegistrar.getEffectiveSecurityEntityName(entityType, algorithm);
-        SecurityEntityFactory<?> factoryEntry;
-        synchronized (factoriesMap) {
-            factoryEntry =
-                    factoriesMap.computeIfAbsent(
-                            effectiveName, k -> createSecurityEntityFactory(entityType, entitySelector));
-        }
-
-        return (SecurityEntityFactory<T>) factoryEntry;
-    }
-
-    public static <T> SecurityEntityFactory<T> createSecurityEntityFactory(
-            Class<T> entityType, Predicate<? super SecurityProviderRegistrar> entitySelector) {
-        register();
-
-        SecurityProviderRegistrar registrar;
-        synchronized (REGISTERED_PROVIDERS) {
-            registrar =
-                 SecurityProviderRegistrar.findSecurityProviderRegistrarBySecurityEntity(
-                         entitySelector, REGISTERED_PROVIDERS.values());
-        }
-
-        try {
-            return SecurityEntityFactory.toFactory(entityType, registrar, getDefaultProviderChoice());
-        } catch (ReflectiveOperationException t) {
-            Throwable e = GenericUtils.peelException(t);
-            if (e instanceof RuntimeException) {
-                throw (RuntimeException) e;
-            } else if (e instanceof Error) {
-                throw (Error) e;
-            } else {
-                throw new RuntimeException(e);
-            }
-        }
-    }
-
-    public static KeyFactory getKeyFactory(String algorithm) throws GeneralSecurityException {
-        SecurityEntityFactory<KeyFactory> factory =
-                resolveSecurityEntityFactory(KeyFactory.class, algorithm, r -> r.isKeyFactorySupported(algorithm));
-        return factory.getInstance(algorithm);
-    }
-
-    public static Cipher getCipher(String transformation) throws GeneralSecurityException {
-        SecurityEntityFactory<Cipher> factory =
-                resolveSecurityEntityFactory(Cipher.class, transformation, r -> r.isCipherSupported(transformation));
-        return factory.getInstance(transformation);
-    }
-
-    public static MessageDigest getMessageDigest(String algorithm) throws GeneralSecurityException {
-        SecurityEntityFactory<MessageDigest> factory =
-                resolveSecurityEntityFactory(MessageDigest.class, algorithm, r -> r.isMessageDigestSupported(algorithm));
-        return factory.getInstance(algorithm);
-    }
-
-    public static KeyPairGenerator getKeyPairGenerator(String algorithm) throws GeneralSecurityException {
-        SecurityEntityFactory<KeyPairGenerator> factory =
-                resolveSecurityEntityFactory(KeyPairGenerator.class, algorithm, r -> r.isKeyPairGeneratorSupported(algorithm));
-        return factory.getInstance(algorithm);
-    }
-
-    public static KeyAgreement getKeyAgreement(String algorithm) throws GeneralSecurityException {
-        SecurityEntityFactory<KeyAgreement> factory =
-                resolveSecurityEntityFactory(KeyAgreement.class, algorithm, r -> r.isKeyAgreementSupported(algorithm));
-        return factory.getInstance(algorithm);
-    }
-
-    public static Mac getMac(String algorithm) throws GeneralSecurityException {
-        SecurityEntityFactory<Mac> factory =
-                resolveSecurityEntityFactory(Mac.class, algorithm, r -> r.isMacSupported(algorithm));
-        return factory.getInstance(algorithm);
-    }
-
-    public static Signature getSignature(String algorithm) throws GeneralSecurityException {
-        SecurityEntityFactory<Signature> factory =
-                resolveSecurityEntityFactory(Signature.class, algorithm, r -> r.isSignatureSupported(algorithm));
-        return factory.getInstance(algorithm);
-    }
-
-    public static CertificateFactory getCertificateFactory(String type) throws GeneralSecurityException {
-        SecurityEntityFactory<CertificateFactory> factory =
-                resolveSecurityEntityFactory(CertificateFactory.class, type, r -> r.isCertificateFactorySupported(type));
-        return factory.getInstance(type);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleGeneratorHostKeyProvider.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleGeneratorHostKeyProvider.java b/sshd-core/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleGeneratorHostKeyProvider.java
deleted file mode 100644
index 3716719..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleGeneratorHostKeyProvider.java
+++ /dev/null
@@ -1,48 +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.security.bouncycastle;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Path;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-
-import org.apache.sshd.server.keyprovider.AbstractGeneratorHostKeyProvider;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class BouncyCastleGeneratorHostKeyProvider extends AbstractGeneratorHostKeyProvider {
-    public BouncyCastleGeneratorHostKeyProvider(Path path) {
-        setPath(path);
-    }
-
-    @SuppressWarnings("deprecation")
-    @Override
-    protected void doWriteKeyPair(String resourceKey, KeyPair kp, OutputStream outputStream) throws IOException, GeneralSecurityException {
-        try (org.bouncycastle.openssl.PEMWriter w =
-                     new org.bouncycastle.openssl.PEMWriter(new OutputStreamWriter(outputStream, StandardCharsets.UTF_8))) {
-            w.writeObject(kp);
-            w.flush();
-        }
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleKeyPairResourceParser.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleKeyPairResourceParser.java b/sshd-core/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleKeyPairResourceParser.java
deleted file mode 100644
index 4c8722a..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleKeyPairResourceParser.java
+++ /dev/null
@@ -1,130 +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.security.bouncycastle;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.nio.charset.StandardCharsets;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.security.NoSuchProviderException;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.config.keys.loader.AbstractKeyPairResourceParser;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.common.util.security.SecurityProviderRegistrar;
-import org.apache.sshd.common.util.security.SecurityUtils;
-import org.bouncycastle.openssl.PEMDecryptorProvider;
-import org.bouncycastle.openssl.PEMEncryptedKeyPair;
-import org.bouncycastle.openssl.PEMKeyPair;
-import org.bouncycastle.openssl.PEMParser;
-import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
-import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class BouncyCastleKeyPairResourceParser extends AbstractKeyPairResourceParser {
-    public static final List<String> BEGINNERS =
-            Collections.unmodifiableList(
-                    Arrays.asList(
-                            "BEGIN RSA PRIVATE KEY",
-                            "BEGIN DSA PRIVATE KEY",
-                            "BEGIN EC PRIVATE KEY"));
-
-    public static final List<String> ENDERS =
-            Collections.unmodifiableList(
-                    Arrays.asList(
-                            "END RSA PRIVATE KEY",
-                            "END DSA PRIVATE KEY",
-                            "END EC PRIVATE KEY"));
-
-    public static final BouncyCastleKeyPairResourceParser INSTANCE = new BouncyCastleKeyPairResourceParser();
-
-    public BouncyCastleKeyPairResourceParser() {
-        super(BEGINNERS, ENDERS);
-    }
-
-    @Override
-    public Collection<KeyPair> extractKeyPairs(
-            String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, List<String> lines)
-                    throws IOException, GeneralSecurityException {
-        StringBuilder writer = new StringBuilder(beginMarker.length() + endMarker.length() + lines.size() * 80);
-        writer.append(beginMarker).append(IoUtils.EOL);
-        lines.forEach(l -> writer.append(l).append(IoUtils.EOL));
-        writer.append(endMarker).append(IoUtils.EOL);
-
-        String data = writer.toString();
-        byte[] dataBytes = data.getBytes(StandardCharsets.UTF_8);
-        try (InputStream bais = new ByteArrayInputStream(dataBytes)) {
-            return extractKeyPairs(resourceKey, beginMarker, endMarker, passwordProvider, bais);
-        }
-    }
-
-    @Override
-    public Collection<KeyPair> extractKeyPairs(
-            String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, InputStream stream)
-                    throws IOException, GeneralSecurityException {
-        KeyPair kp = loadKeyPair(resourceKey, stream, passwordProvider);
-        return (kp == null) ? Collections.emptyList() : Collections.singletonList(kp);
-    }
-
-    public static KeyPair loadKeyPair(String resourceKey, InputStream inputStream, FilePasswordProvider provider)
-            throws IOException, GeneralSecurityException {
-        try (PEMParser r = new PEMParser(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
-            Object o = r.readObject();
-
-            SecurityProviderRegistrar registrar = SecurityUtils.getRegisteredProvider(SecurityUtils.BOUNCY_CASTLE);
-            if (registrar == null) {
-                throw new NoSuchProviderException(SecurityUtils.BOUNCY_CASTLE + " registrar not available");
-            }
-
-            JcaPEMKeyConverter pemConverter = new JcaPEMKeyConverter();
-            if (registrar.isNamedProviderUsed()) {
-                pemConverter.setProvider(registrar.getName());
-            } else {
-                pemConverter.setProvider(registrar.getSecurityProvider());
-            }
-            if (o instanceof PEMEncryptedKeyPair) {
-                ValidateUtils.checkNotNull(provider, "No password provider for resource=%s", resourceKey);
-
-                String password = ValidateUtils.checkNotNullAndNotEmpty(provider.getPassword(resourceKey), "No password provided for resource=%s", resourceKey);
-                JcePEMDecryptorProviderBuilder decryptorBuilder = new JcePEMDecryptorProviderBuilder();
-                PEMDecryptorProvider pemDecryptor = decryptorBuilder.build(password.toCharArray());
-                o = ((PEMEncryptedKeyPair) o).decryptKeyPair(pemDecryptor);
-            }
-
-            if (o instanceof PEMKeyPair) {
-                return pemConverter.getKeyPair((PEMKeyPair) o);
-            } else if (o instanceof KeyPair) {
-                return (KeyPair) o;
-            } else {
-                throw new IOException("Failed to read " + resourceKey + " - unknown result object: " + o);
-            }
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleRandom.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleRandom.java b/sshd-core/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleRandom.java
deleted file mode 100644
index 36c23e7..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleRandom.java
+++ /dev/null
@@ -1,88 +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.security.bouncycastle;
-
-import java.security.SecureRandom;
-
-import org.apache.sshd.common.random.AbstractRandom;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-import org.bouncycastle.crypto.prng.RandomGenerator;
-import org.bouncycastle.crypto.prng.VMPCRandomGenerator;
-
-/**
- * BouncyCastle <code>Random</code>.
- * This pseudo random number generator uses the a very fast PRNG from BouncyCastle.
- * The JRE random will be used when creating a new generator to add some random
- * data to the seed.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public final class BouncyCastleRandom extends AbstractRandom {
-    public static final String NAME = SecurityUtils.BOUNCY_CASTLE;
-    private final RandomGenerator random;
-
-    public BouncyCastleRandom() {
-        ValidateUtils.checkTrue(SecurityUtils.isBouncyCastleRegistered(), "BouncyCastle not registered");
-        this.random = new VMPCRandomGenerator();
-        byte[] seed = new SecureRandom().generateSeed(8);
-        this.random.addSeedMaterial(seed);
-    }
-
-    @Override
-    public String getName() {
-        return NAME;
-    }
-
-    @Override
-    public void fill(byte[] bytes, int start, int len) {
-        this.random.nextBytes(bytes, start, len);
-    }
-
-    /**
-     * Returns a pseudo-random uniformly distributed {@code int}
-     * in the half-open range [0, n).
-     */
-    @Override
-    public int random(int n) {
-        ValidateUtils.checkTrue(n > 0, "Limit must be positive: %d", n);
-        if ((n & -n) == n) {
-            return (int) ((n * (long) next(31)) >> 31);
-        }
-
-        int bits;
-        int val;
-        do {
-            bits = next(31);
-            val = bits % n;
-        } while (bits - val + (n - 1) < 0);
-        return val;
-    }
-
-    private int next(int numBits) {
-        int bytes = (numBits + 7) / 8;
-        byte next[] = new byte[bytes];
-        int ret = 0;
-        random.nextBytes(next);
-        for (int i = 0; i < bytes; i++) {
-            ret = (next[i] & 0xFF) | (ret << 8);
-        }
-        return ret >>> (bytes * 8 - numBits);
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleRandomFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleRandomFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleRandomFactory.java
deleted file mode 100644
index 720c7a5..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleRandomFactory.java
+++ /dev/null
@@ -1,45 +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.security.bouncycastle;
-
-import org.apache.sshd.common.random.AbstractRandomFactory;
-import org.apache.sshd.common.random.Random;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * Named factory for the BouncyCastle <code>Random</code>
- */
-public final class BouncyCastleRandomFactory extends AbstractRandomFactory {
-    public static final String NAME = "bouncycastle";
-    public static final BouncyCastleRandomFactory INSTANCE = new BouncyCastleRandomFactory();
-
-    public BouncyCastleRandomFactory() {
-        super(NAME);
-    }
-
-    @Override
-    public boolean isSupported() {
-        return SecurityUtils.isBouncyCastleRegistered();
-    }
-
-    @Override
-    public Random create() {
-        return new BouncyCastleRandom();
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleSecurityProviderRegistrar.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleSecurityProviderRegistrar.java b/sshd-core/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleSecurityProviderRegistrar.java
deleted file mode 100644
index b47ca80..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleSecurityProviderRegistrar.java
+++ /dev/null
@@ -1,128 +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.security.bouncycastle;
-
-import java.security.KeyFactory;
-import java.security.KeyPairGenerator;
-import java.security.Provider;
-import java.security.Signature;
-import java.util.Objects;
-import java.util.concurrent.atomic.AtomicReference;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ReflectionUtils;
-import org.apache.sshd.common.util.security.AbstractSecurityProviderRegistrar;
-import org.apache.sshd.common.util.security.SecurityUtils;
-import org.apache.sshd.common.util.threads.ThreadUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class BouncyCastleSecurityProviderRegistrar extends AbstractSecurityProviderRegistrar {
-    // We want to use reflection API so as not to require BouncyCastle to be present in the classpath
-    public static final String PROVIDER_CLASS = "org.bouncycastle.jce.provider.BouncyCastleProvider";
-    // Do not define a static registrar instance to minimize class loading issues
-    private final AtomicReference<Boolean> supportHolder = new AtomicReference<>(null);
-    private final AtomicReference<String> allSupportHolder = new AtomicReference<>();
-
-    public BouncyCastleSecurityProviderRegistrar() {
-        super(SecurityUtils.BOUNCY_CASTLE);
-    }
-
-    @Override
-    public boolean isEnabled() {
-        if (!super.isEnabled()) {
-            return false;
-        }
-
-        // For backward compatibility
-        return this.getBooleanProperty(SecurityUtils.REGISTER_BOUNCY_CASTLE_PROP, true);
-    }
-
-    @Override
-    public Provider getSecurityProvider() {
-        try {
-            return getOrCreateProvider(PROVIDER_CLASS);
-        } catch (ReflectiveOperationException t) {
-            Throwable e = GenericUtils.peelException(t);
-            log.error("getSecurityProvider({}) failed ({}) to instantiate {}: {}",
-                      getName(), e.getClass().getSimpleName(), PROVIDER_CLASS, e.getMessage());
-            if (e instanceof RuntimeException) {
-                throw (RuntimeException) e;
-            }
-
-            throw new RuntimeException(e);
-        }
-    }
-
-    @Override
-    public String getDefaultSecurityEntitySupportValue(Class<?> entityType) {
-        String allValue = allSupportHolder.get();
-        if (GenericUtils.length(allValue) > 0) {
-            return allValue;
-        }
-
-        String propName = getConfigurationPropertyName("supportAll");
-        allValue = this.getStringProperty(propName, ALL_OPTIONS_VALUE);
-        if (GenericUtils.isEmpty(allValue)) {
-            allValue = NO_OPTIONS_VALUE;
-        }
-
-        allSupportHolder.set(allValue);
-        return allValue;
-    }
-
-    @Override
-    public boolean isSecurityEntitySupported(Class<?> entityType, String name) {
-        if (!isSupported()) {
-            return false;
-        }
-
-        // Some known values it does not support
-        if (KeyPairGenerator.class.isAssignableFrom(entityType)
-                || KeyFactory.class.isAssignableFrom(entityType)) {
-            if (Objects.compare(name, SecurityUtils.EDDSA, String.CASE_INSENSITIVE_ORDER) == 0) {
-                return false;
-            }
-        } else if (Signature.class.isAssignableFrom(entityType)) {
-            if (Objects.compare(name, SecurityUtils.CURVE_ED25519_SHA512, String.CASE_INSENSITIVE_ORDER) == 0) {
-                return false;
-            }
-        }
-
-        return super.isSecurityEntitySupported(entityType, name);
-    }
-
-    @Override
-    public boolean isSupported() {
-        Boolean supported;
-        synchronized (supportHolder) {
-            supported = supportHolder.get();
-            if (supported != null) {
-                return supported.booleanValue();
-            }
-
-            ClassLoader cl = ThreadUtils.resolveDefaultClassLoader(getClass());
-            supported = ReflectionUtils.isClassAvailable(cl, PROVIDER_CLASS);
-            supportHolder.set(supported);
-        }
-
-        return supported.booleanValue();
-    }
-}


[25/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common utilities code from sshd-core into sshd-common (new artifact)

Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/PropertyResolver.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/PropertyResolver.java b/sshd-core/src/main/java/org/apache/sshd/common/PropertyResolver.java
deleted file mode 100644
index c333c7f..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/PropertyResolver.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.common;
-
-import java.util.Collections;
-import java.util.Map;
-
-/**
- * Indicates an entity that can be configured using properties. The properties
- * are simple name-value pairs where the actual value type depends on the
- * property. Some automatic conversions may be available - e.g., from a string
- * to a numeric or {@code boolean} value, or from {@code int} to {@code long},
- * etc.. <B>Note:</B> implementations may decide to use case <U>insensitive</U>
- * property names, therefore it is <U><B>highly discouraged</B></U> to use names
- * that differ from each other only in case sensitivity. Also, implementations
- * may choose to trim whitespaces, thus such are also highly discouraged.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface PropertyResolver {
-    /**
-     * An &quot;empty&quot; resolver with no properties and no parent
-     */
-    PropertyResolver EMPTY = new PropertyResolver() {
-        @Override
-        public PropertyResolver getParentPropertyResolver() {
-            return null;
-        }
-
-        @Override
-        public Map<String, Object> getProperties() {
-            return Collections.emptyMap();
-        }
-
-        @Override
-        public String toString() {
-            return "EMPTY";
-        }
-    };
-
-    /**
-     * @return The parent resolver that can be used to query for missing
-     *         properties - {@code null} if no parent
-     */
-    PropertyResolver getParentPropertyResolver();
-
-    /**
-     * <P>
-     * A map of properties that can be used to configure the SSH server or
-     * client. This map will never be changed by either the server or client and
-     * is not supposed to be changed at runtime (changes are not bound to have
-     * any effect on a running client or server), though it may affect the
-     * creation of sessions later as these values are usually not cached.
-     * </P>
-     *
-     * <P>
-     * <B>Note:</B> the <U>type</U> of the mapped property should match the
-     * expected configuration value type - {@code Long, Integer, Boolean,
-     * String}, etc.... If it doesn't, the {@code toString()} result of the
-     * mapped value is used to convert it to the required type. E.g., if the
-     * mapped value is the <U>string</U> &quot;1234&quot; and the expected value
-     * is a {@code long} then it will be parsed into one. Also, if the mapped
-     * value is an {@code Integer} but a {@code long} is expected, then it will
-     * be converted into one.
-     * </P>
-     *
-     * @return a valid <code>Map</code> containing configuration values, never
-     *         {@code null}
-     */
-    Map<String, Object> getProperties();
-
-    default long getLongProperty(String name, long def) {
-        return PropertyResolverUtils.getLongProperty(this, name, def);
-    }
-
-    default Long getLong(String name) {
-        return PropertyResolverUtils.getLong(this, name);
-    }
-
-    default int getIntProperty(String name, int def) {
-        return PropertyResolverUtils.getIntProperty(this, name, def);
-    }
-
-    default Integer getInteger(String name) {
-        return PropertyResolverUtils.getInteger(this, name);
-    }
-
-    default boolean getBooleanProperty(String name, boolean def) {
-        return PropertyResolverUtils.getBooleanProperty(this, name, def);
-    }
-
-    default Boolean getBoolean(String name) {
-        return PropertyResolverUtils.getBoolean(this, name);
-    }
-
-    default String getStringProperty(String name, String def) {
-        return PropertyResolverUtils.getStringProperty(this, name, def);
-    }
-
-    default String getString(String name) {
-        return PropertyResolverUtils.getString(this, name);
-    }
-
-    default Object getObject(String name) {
-        return PropertyResolverUtils.getObject(this, name);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/PropertyResolverUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/PropertyResolverUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/PropertyResolverUtils.java
deleted file mode 100644
index 6677685..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/PropertyResolverUtils.java
+++ /dev/null
@@ -1,482 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.common;
-
-import java.nio.charset.Charset;
-import java.util.Collection;
-import java.util.Comparator;
-import java.util.Map;
-import java.util.NoSuchElementException;
-import java.util.Objects;
-import java.util.Properties;
-import java.util.TreeMap;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public final class PropertyResolverUtils {
-    private PropertyResolverUtils() {
-        throw new UnsupportedOperationException("No instance allowed");
-    }
-
-    /**
-     * @param resolver     The {@link PropertyResolver} instance - ignored if {@code null}
-     * @param name         The property name
-     * @param defaultValue The default value to return if the specified property
-     *                     does not exist in the properties map
-     * @return The resolved property
-     * @throws NumberFormatException if malformed value
-     * @see #toLong(Object, long)
-     */
-    public static long getLongProperty(PropertyResolver resolver, String name, long defaultValue) {
-        return toLong(resolvePropertyValue(resolver, name), defaultValue);
-    }
-
-    public static long getLongProperty(Map<String, ?> props, String name, long defaultValue) {
-        return toLong(resolvePropertyValue(props, name), defaultValue);
-    }
-
-    /**
-     * Converts a generic object value to a {@code long} if possible:
-     * <UL>
-     *      <LI>
-     *      If value is {@code null} the default is returned
-     *      </LI>
-     *
-     *      <LI>
-     *      If value is a {@link Number} then its {@link Number#longValue()} is returned
-     *      </LI>
-     *
-     *      <LI>
-     *      Otherwise, the value's {@link #toString()} is parsed as a {@code long}
-     *      </LI>
-     * </UL>
-     *
-     * @param value         The resolved value - may be {@code null}
-     * @param defaultValue  The default to use if {@code null} resolved value
-     * @return The resolved value
-     * @throws NumberFormatException if malformed value
-     * @see Long#parseLong(String)
-     */
-    public static long toLong(Object value, long defaultValue) {
-        if (value == null) {
-            return defaultValue;
-        } else if (value instanceof Number) {
-            return ((Number) value).longValue();
-        } else {    // we parse the string in case it is not a valid long value
-            return Long.parseLong(value.toString());
-        }
-    }
-
-    /**
-     * @param resolver The {@link PropertyResolver} instance - ignored if {@code null}
-     * @param name     The property name
-     * @return The {@link Long} value or {@code null} if property not found
-     * @throws NumberFormatException if malformed value
-     * @see #toLong(Object)
-     */
-    public static Long getLong(PropertyResolver resolver, String name) {
-        return toLong(resolvePropertyValue(resolver, name));
-    }
-
-    public static Long getLong(Map<String, ?> props, String name) {
-        return toLong(resolvePropertyValue(props, name));
-    }
-
-    /**
-     * Converts a generic object into a {@link Long}:
-     * <UL>
-     *      <LI>
-     *      If the value is {@code null} then returns {@code null}.
-     *      </LI>
-     *
-     *      <LI>
-     *      If the value is already a {@link Long} then it is returned as such.
-     *      </LI>
-
-     *      <LI>
-     *      If value is a {@link Number} then its {@link Number#longValue()} is
-     *      wrapped as a {@link Long}
-     *      </LI>
-     *
-     *      <LI>
-     *      Otherwise, the value's {@link #toString()} is parsed as a {@link Long}
-     *      </LI>
-     * </UL>
-     *
-     * @param value The resolved value - may be {@code null}
-     * @return The {@link Long} value or {@code null} if property not found
-     * @throws NumberFormatException if malformed value
-     * @see Long#valueOf(long)
-     * @see Long#valueOf(String)
-     */
-    public static Long toLong(Object value) {
-        if (value == null) {
-            return null;
-        } else if (value instanceof Long) {
-            return (Long) value;
-        } else if (value instanceof Number) {
-            return ((Number) value).longValue();
-        } else {    // we parse the string in case it is not a valid long value
-            return Long.valueOf(value.toString());
-        }
-    }
-
-    /**
-     * Converts an enumerated configuration value:
-     * <UL>
-     *      <P><LI>
-     *      If value is {@code null} then return {@code null}
-     *      </LI></P>
-     *
-     *      <P><LI>
-     *      If value already of the expected type then simply
-     *      cast and return it.
-     *      </LI></P>
-     *
-     *      <P><LI>
-     *      If value is a {@link CharSequence} then convert it
-     *      to a string and look for a matching enumerated value
-     *      name - case <U>insensitive</U>.
-     *      </LI></P>>
-     * </UL>
-     *
-     * @param <E> Type of enumerated value
-     * @param enumType The enumerated class type
-     * @param value The configured value - ignored if {@code null}
-     * @param failIfNoMatch Whether to fail if no matching name found
-     * @param available The available values to compare the name
-     * @return The matching enumerated value - {@code null} if no match found
-     * @throws IllegalArgumentException If value is neither {@code null},
-     * nor the enumerated type nor a {@link CharSequence}
-     * @throws NoSuchElementException If no matching string name found and
-     * <tt>failIfNoMatch</tt> is {@code true}
-     */
-    public static <E extends Enum<E>> E toEnum(Class<E> enumType, Object value, boolean failIfNoMatch, Collection<E> available) {
-        if (value == null) {
-            return null;
-        } else if (enumType.isInstance(value)) {
-            return enumType.cast(value);
-        } else if (value instanceof CharSequence) {
-            String name = value.toString();
-            if (GenericUtils.size(available) > 0) {
-                for (E v : available) {
-                    if (name.equalsIgnoreCase(v.name())) {
-                        return v;
-                    }
-                }
-            }
-
-            if (failIfNoMatch) {
-                throw new NoSuchElementException("No match found for " + enumType.getSimpleName() + "[" + name + "]");
-            }
-
-            return null;
-        } else {
-            throw new IllegalArgumentException("Bad value type for enum conversion: " + value.getClass().getSimpleName());
-        }
-    }
-
-    public static Object updateProperty(PropertyResolver resolver, String name, long value) {
-        return updateProperty(resolver.getProperties(), name, value);
-    }
-
-    public static Object updateProperty(Map<String, Object> props, String name, long value) {
-        return updateProperty(props, name, Long.valueOf(value));
-    }
-
-    public static int getIntProperty(PropertyResolver resolver, String name, int defaultValue) {
-        return toInteger(resolvePropertyValue(resolver, name), defaultValue);
-    }
-
-    public static int getIntProperty(Map<String, ?> props, String name, int defaultValue) {
-        return toInteger(resolvePropertyValue(props, name), defaultValue);
-    }
-
-    public static int toInteger(Object value, int defaultValue) {
-        if (value == null) {
-            return defaultValue;
-        } else if (value instanceof Number) {
-            return ((Number) value).intValue();
-        } else {    // we parse the string in case this is NOT an integer
-            return Integer.parseInt(value.toString());
-        }
-    }
-
-    public static Integer getInteger(PropertyResolver resolver, String name) {
-        return toInteger(resolvePropertyValue(resolver, name));
-    }
-
-    public static Integer getInteger(Map<String, ?> props, String name) {
-        return toInteger(resolvePropertyValue(props, name));
-    }
-
-    public static Integer toInteger(Object value) {
-        if (value == null) {
-            return null;
-        } else if (value instanceof Integer) {
-            return (Integer) value;
-        } else if (value instanceof Number) {
-            return ((Number) value).intValue();
-        } else {    // we parse the string in case this is NOT an integer
-            return Integer.valueOf(value.toString());
-        }
-    }
-
-    public static Object updateProperty(PropertyResolver resolver, String name, int value) {
-        return updateProperty(resolver.getProperties(), name, value);
-    }
-
-    public static Object updateProperty(Map<String, Object> props, String name, int value) {
-        return updateProperty(props, name, Integer.valueOf(value));
-    }
-
-    public static boolean getBooleanProperty(PropertyResolver resolver, String name, boolean defaultValue) {
-        return toBoolean(getObject(resolver, name), defaultValue);
-    }
-
-    public static boolean getBooleanProperty(Map<String, ?> props, String name, boolean defaultValue) {
-        return toBoolean(getObject(props, name), defaultValue);
-    }
-
-    public static boolean toBoolean(Object value, boolean defaultValue) {
-        if (value == null) {
-            return defaultValue;
-        } else {
-            return toBoolean(value);
-        }
-    }
-
-    public static Boolean getBoolean(PropertyResolver resolver, String name) {
-        return toBoolean(resolvePropertyValue(resolver, name));
-    }
-
-    public static Boolean getBoolean(Map<String, ?> props, String name) {
-        return toBoolean(resolvePropertyValue(props, name));
-    }
-
-    public static Boolean toBoolean(Object value) {
-        if (value == null) {
-            return null;
-        } else if (value instanceof Boolean) {
-            return (Boolean) value;
-        } else {
-            return Boolean.valueOf(value.toString());
-        }
-    }
-
-    public static Object updateProperty(PropertyResolver resolver, String name, boolean value) {
-        return updateProperty(resolver.getProperties(), name, value);
-    }
-
-    public static Object updateProperty(Map<String, Object> props, String name, boolean value) {
-        return updateProperty(props, name, Boolean.valueOf(value));
-    }
-
-    /**
-     * @param resolver     The {@link PropertyResolver} to use - ignored if {@code null}
-     * @param name         The property name
-     * @param defaultValue The default value to return if property not set or empty
-     * @return The set value (if not {@code null}/empty) or default one
-     */
-    public static String getStringProperty(PropertyResolver resolver, String name, String defaultValue) {
-        String value = getString(resolver, name);
-        if (GenericUtils.isEmpty(value)) {
-            return defaultValue;
-        } else {
-            return value;
-        }
-    }
-
-    public static String getStringProperty(Map<String, ?> props, String name, String defaultValue) {
-        Object value = resolvePropertyValue(props, name);
-        if (value == null) {
-            return defaultValue;
-        } else {
-            return Objects.toString(value);
-        }
-    }
-
-    public static Charset getCharset(PropertyResolver resolver, String name, Charset defaultValue) {
-        Object value = getObject(resolver, name);
-        return (value == null) ? defaultValue : toCharset(value);
-    }
-
-    public static Charset getCharset(Map<String, ?> props, String name, Charset defaultValue) {
-        Object value = getObject(props, name);
-        return (value == null) ? defaultValue : toCharset(value);
-    }
-
-    public static Charset toCharset(Object value) {
-        if (value == null) {
-            return null;
-        } else if (value instanceof Charset) {
-            return (Charset) value;
-        } else if (value instanceof CharSequence) {
-            return Charset.forName(value.toString());
-        } else {
-            throw new IllegalArgumentException("Invalid charset conversion value: " + value);
-        }
-    }
-
-    public static String getString(PropertyResolver resolver, String name) {
-        Object value = getObject(resolver, name);
-        return Objects.toString(value, null);
-    }
-
-    public static String getString(Map<String, ?> props, String name) {
-        Object value = getObject(props, name);
-        return Objects.toString(value, null);
-    }
-
-    public static Object getObject(PropertyResolver resolver, String name) {
-        return resolvePropertyValue(resolver, name);
-    }
-
-    // for symmetrical reasons...
-    public static Object getObject(Map<String, ?> props, String name) {
-        return resolvePropertyValue(props, name);
-    }
-
-    public static Object resolvePropertyValue(Map<String, ?> props, String name) {
-        String key = ValidateUtils.checkNotNullAndNotEmpty(name, "No property name");
-        return props != null ? props.get(key) : null;
-    }
-
-    /**
-     * @param resolver The {@link PropertyResolver} instance
-     * @param name     The property name
-     * @param value    The new value - if {@code null} or an empty {@link CharSequence}
-     *                 the property is <U>removed</U>
-     * @return The previous value - {@code null} if none
-     */
-    public static Object updateProperty(PropertyResolver resolver, String name, Object value) {
-        return updateProperty(resolver.getProperties(), name, value);
-    }
-
-    public static Object updateProperty(Map<String, Object> props, String name, Object value) {
-        String key = ValidateUtils.checkNotNullAndNotEmpty(name, "No property name");
-        if ((value == null) || ((value instanceof CharSequence) && GenericUtils.isEmpty((CharSequence) value))) {
-            return props.remove(key);
-        } else {
-            return props.put(key, value);
-        }
-    }
-
-    /**
-     * Unwinds the resolvers hierarchy until found one with a non-{@code null} value
-     * for the requested property or reached top. If still no value found and the key
-     * starts with &quot;org.apache.sshd&quot; then the system properties are also
-     * consulted
-     *
-     * @param resolver The {@link PropertyResolver} to start from - ignored if {@code null}
-     * @param name     The requested property name
-     * @return The found value or {@code null}
-     */
-    public static Object resolvePropertyValue(PropertyResolver resolver, String name) {
-        String key = ValidateUtils.checkNotNullAndNotEmpty(name, "No property name");
-        for (PropertyResolver r = resolver; r != null; r = r.getParentPropertyResolver()) {
-            Map<String, ?> props = r.getProperties();
-            if (props != null) {
-                Object value = props.get(key);
-                if (value != null) {
-                    return value;
-                }
-            }
-        }
-
-        return null;
-    }
-
-    /**
-     * Unwinds the resolvers hierarchy until found one with a non-{@code null} value
-     * for the requested property or reached top.
-     *
-     * @param resolver The {@link PropertyResolver} to start from - ignored if {@code null}
-     * @param name     The requested property name
-     * @return The found properties {@link Map} or {@code null}
-     */
-    public static Map<String, Object> resolvePropertiesSource(PropertyResolver resolver, String name) {
-        String key = ValidateUtils.checkNotNullAndNotEmpty(name, "No property name");
-        for (PropertyResolver r = resolver; r != null; r = r.getParentPropertyResolver()) {
-            Map<String, Object> props = r.getProperties();
-            if (props != null) {
-                Object value = props.get(key);
-                if (value != null) {
-                    return props;
-                }
-            }
-        }
-
-        return null;
-    }
-
-    public static PropertyResolver toPropertyResolver(Properties props) {
-        if (GenericUtils.isEmpty(props)) {
-            return PropertyResolver.EMPTY;
-        }
-
-        Map<String, Object> propsMap = new TreeMap<>(Comparator.naturalOrder());
-        Collection<String> names = props.stringPropertyNames();
-        for (String key : names) {
-            String value = props.getProperty(key);
-            if (value == null) {
-                continue;
-            }
-            propsMap.put(key, value);
-        }
-
-        return toPropertyResolver(propsMap);
-    }
-
-    /**
-     * Wraps a {@link Map} into a {@link PropertyResolver} so it can be used
-     * with these utilities
-     *
-     * @param props The properties map - may be {@code null}/empty if no properties
-     * are updated
-     * @return The resolver wrapper
-     */
-    public static PropertyResolver toPropertyResolver(Map<String, Object> props) {
-        return toPropertyResolver(props, null);
-    }
-
-    public static PropertyResolver toPropertyResolver(Map<String, Object> props, PropertyResolver parent) {
-        return new PropertyResolver() {
-            @Override
-            public PropertyResolver getParentPropertyResolver() {
-                return parent;
-            }
-
-            @Override
-            public Map<String, Object> getProperties() {
-                return props;
-            }
-
-            @Override
-            public String toString() {
-                return Objects.toString(props);
-            }
-        };
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/RuntimeSshException.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/RuntimeSshException.java b/sshd-core/src/main/java/org/apache/sshd/common/RuntimeSshException.java
deleted file mode 100644
index 8c9164f..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/RuntimeSshException.java
+++ /dev/null
@@ -1,48 +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;
-
-/**
- * Exception used in the SSH client or server.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class RuntimeSshException extends RuntimeException {
-    private static final long serialVersionUID = -2423550196146939503L;
-
-    public RuntimeSshException() {
-        this(null, null);
-    }
-
-    public RuntimeSshException(String message) {
-        this(message, null);
-    }
-
-    public RuntimeSshException(Throwable cause) {
-        this(null, cause);
-    }
-
-    public RuntimeSshException(String message, Throwable cause) {
-        super(message);
-        if (cause != null) {
-            initCause(cause);
-        }
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/SshConstants.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/SshConstants.java b/sshd-core/src/main/java/org/apache/sshd/common/SshConstants.java
deleted file mode 100644
index 01e8ae7..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/SshConstants.java
+++ /dev/null
@@ -1,245 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sshd.common;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.logging.LoggingUtils;
-
-/**
- * This interface defines constants for the SSH protocol.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public final class SshConstants {
-    //
-    // SSH message identifiers
-    //
-
-    public static final byte SSH_MSG_DISCONNECT = 1;
-    public static final byte SSH_MSG_IGNORE = 2;
-    public static final byte SSH_MSG_UNIMPLEMENTED = 3;
-    public static final byte SSH_MSG_DEBUG = 4;
-    public static final byte SSH_MSG_SERVICE_REQUEST = 5;
-    public static final byte SSH_MSG_SERVICE_ACCEPT = 6;
-    public static final byte SSH_MSG_KEXINIT = 20;
-    public static final int MSG_KEX_COOKIE_SIZE = 16;
-    public static final byte SSH_MSG_NEWKEYS = 21;
-
-    public static final byte SSH_MSG_KEX_FIRST = 30;
-    public static final byte SSH_MSG_KEX_LAST = 49;
-
-    public static final byte SSH_MSG_KEXDH_INIT = 30;
-    public static final byte SSH_MSG_KEXDH_REPLY = 31;
-
-    public static final byte SSH_MSG_KEX_DH_GEX_REQUEST_OLD = 30;
-    public static final byte SSH_MSG_KEX_DH_GEX_GROUP = 31;
-    public static final byte SSH_MSG_KEX_DH_GEX_INIT = 32;
-    public static final byte SSH_MSG_KEX_DH_GEX_REPLY = 33;
-    public static final byte SSH_MSG_KEX_DH_GEX_REQUEST = 34;
-
-    public static final byte SSH_MSG_USERAUTH_REQUEST = 50;
-    public static final byte SSH_MSG_USERAUTH_FAILURE = 51;
-    public static final byte SSH_MSG_USERAUTH_SUCCESS = 52;
-    public static final byte SSH_MSG_USERAUTH_BANNER = 53;
-
-    public static final byte SSH_MSG_USERAUTH_INFO_REQUEST = 60;
-    public static final byte SSH_MSG_USERAUTH_INFO_RESPONSE = 61;
-
-    public static final byte SSH_MSG_USERAUTH_PK_OK = 60;
-
-    public static final byte SSH_MSG_USERAUTH_PASSWD_CHANGEREQ = 60;
-
-    public static final byte SSH_MSG_USERAUTH_GSSAPI_MIC = 66;
-
-    public static final byte SSH_MSG_GLOBAL_REQUEST = 80;
-    public static final byte SSH_MSG_REQUEST_SUCCESS = 81;
-    public static final byte SSH_MSG_REQUEST_FAILURE = 82;
-    public static final byte SSH_MSG_CHANNEL_OPEN = 90;
-    public static final byte SSH_MSG_CHANNEL_OPEN_CONFIRMATION = 91;
-    public static final byte SSH_MSG_CHANNEL_OPEN_FAILURE = 92;
-    public static final byte SSH_MSG_CHANNEL_WINDOW_ADJUST = 93;
-    public static final byte SSH_MSG_CHANNEL_DATA = 94;
-    public static final byte SSH_MSG_CHANNEL_EXTENDED_DATA = 95;
-    public static final byte SSH_MSG_CHANNEL_EOF = 96;
-    public static final byte SSH_MSG_CHANNEL_CLOSE = 97;
-    public static final byte SSH_MSG_CHANNEL_REQUEST = 98;
-    public static final byte SSH_MSG_CHANNEL_SUCCESS = 99;
-    public static final byte SSH_MSG_CHANNEL_FAILURE = 100;
-
-    //
-    // Disconnect error codes
-    //
-    public static final int SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT = 1;
-    public static final int SSH2_DISCONNECT_PROTOCOL_ERROR = 2;
-    public static final int SSH2_DISCONNECT_KEY_EXCHANGE_FAILED = 3;
-    public static final int SSH2_DISCONNECT_HOST_AUTHENTICATION_FAILED = 4;
-    public static final int SSH2_DISCONNECT_RESERVED = 4;
-    public static final int SSH2_DISCONNECT_MAC_ERROR = 5;
-    public static final int SSH2_DISCONNECT_COMPRESSION_ERROR = 6;
-    public static final int SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE = 7;
-    public static final int SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED = 8;
-    public static final int SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE = 9;
-    public static final int SSH2_DISCONNECT_CONNECTION_LOST = 10;
-    public static final int SSH2_DISCONNECT_BY_APPLICATION = 11;
-    public static final int SSH2_DISCONNECT_TOO_MANY_CONNECTIONS = 12;
-    public static final int SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER = 13;
-    public static final int SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE = 14;
-    public static final int SSH2_DISCONNECT_ILLEGAL_USER_NAME = 15;
-
-    //
-    // Open error codes
-    //
-
-    public static final int SSH_OPEN_ADMINISTRATIVELY_PROHIBITED = 1;
-    public static final int SSH_OPEN_CONNECT_FAILED = 2;
-    public static final int SSH_OPEN_UNKNOWN_CHANNEL_TYPE = 3;
-    public static final int SSH_OPEN_RESOURCE_SHORTAGE = 4;
-
-    // Some more constants
-    public static final int SSH_EXTENDED_DATA_STDERR = 1;   // see RFC4254 section 5.2
-    public static final int SSH_PACKET_HEADER_LEN = 5;  // 32-bit length + 8-bit pad length
-
-    private SshConstants() {
-        throw new UnsupportedOperationException("No instance allowed");
-    }
-
-    private static final class LazyAmbiguousOpcodesHolder {
-        private static final Set<Integer> AMBIGUOUS_OPCODES =
-            Collections.unmodifiableSet(
-                new HashSet<>(
-                    LoggingUtils.getAmbiguousMenmonics(SshConstants.class, "SSH_MSG_").values()));
-
-        private LazyAmbiguousOpcodesHolder() {
-            throw new UnsupportedOperationException("No instance allowed");
-        }
-    }
-
-    /**
-     * @param cmd The command value
-     * @return {@code true} if this value is used by several <U>different</U> messages
-     * @see #getAmbiguousOpcodes()
-     */
-    public static boolean isAmbiguousOpcode(int cmd) {
-        Collection<Integer> ambiguousOpcodes = getAmbiguousOpcodes();
-        return ambiguousOpcodes.contains(cmd);
-    }
-
-    /**
-     * @return A {@link Set} of opcodes that are used by several <U>different</U> messages
-     */
-    @SuppressWarnings("synthetic-access")
-    public static Set<Integer> getAmbiguousOpcodes() {
-        return LazyAmbiguousOpcodesHolder.AMBIGUOUS_OPCODES;
-    }
-
-    private static final class LazyMessagesMapHolder {
-        private static final Map<Integer, String> MESSAGES_MAP =
-            LoggingUtils.generateMnemonicMap(SshConstants.class, f -> {
-                String name = f.getName();
-                if (!name.startsWith("SSH_MSG_")) {
-                    return false;
-                }
-
-                try {
-                    return !isAmbiguousOpcode(f.getByte(null));
-                } catch (Exception e) {
-                    return false;
-                }
-            });
-
-        private LazyMessagesMapHolder() {
-            throw new UnsupportedOperationException("No instance allowed");
-        }
-    }
-
-    /**
-     * Converts a command value to a user-friendly name
-     *
-     * @param cmd The command value
-     * @return The user-friendly name - if not one of the defined {@code SSH_MSG_XXX}
-     * values then returns the string representation of the command's value
-     */
-    public static String getCommandMessageName(int cmd) {
-        @SuppressWarnings("synthetic-access")
-        String name = LazyMessagesMapHolder.MESSAGES_MAP.get(cmd);
-        if (GenericUtils.isEmpty(name)) {
-            return Integer.toString(cmd);
-        } else {
-            return name;
-        }
-    }
-
-    private static final class LazyReasonsMapHolder {
-        private static final Map<Integer, String> REASONS_MAP =
-            LoggingUtils.generateMnemonicMap(SshConstants.class, "SSH2_DISCONNECT_");
-
-        private LazyReasonsMapHolder() {
-            throw new UnsupportedOperationException("No instance allowed");
-        }
-    }
-
-    /**
-     * Converts a disconnect reason value to a user-friendly name
-     *
-     * @param reason The disconnect reason value
-     * @return The user-friendly name - if not one of the defined {@code SSH2_DISCONNECT_}
-     * values then returns the string representation of the reason's value
-     */
-    public static String getDisconnectReasonName(int reason) {
-        @SuppressWarnings("synthetic-access")
-        String name = LazyReasonsMapHolder.REASONS_MAP.get(reason);
-        if (GenericUtils.isEmpty(name)) {
-            return Integer.toString(reason);
-        } else {
-            return name;
-        }
-    }
-
-    private static final class LazyOpenCodesMapHolder {
-        private static final Map<Integer, String> OPEN_CODES_MAP =
-            LoggingUtils.generateMnemonicMap(SshConstants.class, "SSH_OPEN_");
-
-        private LazyOpenCodesMapHolder() {
-            throw new UnsupportedOperationException("No instance allowed");
-        }
-    }
-
-    /**
-     * Converts an open error value to a user-friendly name
-     *
-     * @param code The open error value
-     * @return The user-friendly name - if not one of the defined {@code SSH_OPEN_}
-     * values then returns the string representation of the reason's value
-     */
-    public static String getOpenErrorCodeName(int code) {
-        @SuppressWarnings("synthetic-access")
-        String name = LazyOpenCodesMapHolder.OPEN_CODES_MAP.get(code);
-        if (GenericUtils.isEmpty(name)) {
-            return Integer.toString(code);
-        } else {
-            return name;
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/SshException.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/SshException.java b/sshd-core/src/main/java/org/apache/sshd/common/SshException.java
deleted file mode 100644
index 4280d08..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/SshException.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sshd.common;
-
-import java.io.IOException;
-import java.util.Objects;
-
-import org.apache.sshd.common.util.GenericUtils;
-
-/**
- * Represents an SSH related exception
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class SshException extends IOException {
-
-    private static final long serialVersionUID = -7349477687125144606L;
-
-    private final int disconnectCode;
-
-    public SshException(String message) {
-        this(message, null);
-    }
-
-    public SshException(Throwable cause) {
-        this(Objects.requireNonNull(cause, "No cause").getMessage(), cause);
-    }
-
-    public SshException(String message, Throwable cause) {
-        this(0, message, cause);
-    }
-
-    public SshException(int disconnectCode) {
-        this(disconnectCode, SshConstants.getDisconnectReasonName(disconnectCode));
-    }
-
-    public SshException(int disconnectCode, String message) {
-        this(disconnectCode, message, null);
-    }
-
-    public SshException(int disconnectCode, Throwable cause) {
-        this(disconnectCode, SshConstants.getDisconnectReasonName(disconnectCode), cause);
-    }
-
-    public SshException(int disconnectCode, String message, Throwable cause) {
-        super(GenericUtils.isEmpty(message) ? SshConstants.getDisconnectReasonName(disconnectCode) : message);
-        this.disconnectCode = disconnectCode;
-        if (cause != null) {
-            initCause(cause);
-        }
-    }
-
-    public int getDisconnectCode() {
-        return disconnectCode;
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/SyspropsMapWrapper.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/SyspropsMapWrapper.java b/sshd-core/src/main/java/org/apache/sshd/common/SyspropsMapWrapper.java
deleted file mode 100644
index 4408be2..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/SyspropsMapWrapper.java
+++ /dev/null
@@ -1,209 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.common;
-
-import java.util.AbstractMap.SimpleImmutableEntry;
-import java.util.Collection;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Properties;
-import java.util.Set;
-import java.util.TreeSet;
-import java.util.stream.Collectors;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.MapEntryUtils;
-
-/**
- * A wrapper that exposes a read-only {@link Map} access to the system
- * properties. Any attempt to modify it will throw {@link UnsupportedOperationException}.
- * The mapper uses the {@link #SYSPROPS_MAPPED_PREFIX} to filter and access'
- * only these properties, ignoring all others
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public final class SyspropsMapWrapper implements Map<String, Object> {
-    /**
-     * Prefix of properties used by the mapper to identify SSHD related settings
-     */
-    public static final String SYSPROPS_MAPPED_PREFIX = "org.apache.sshd.config";
-
-    /**
-     * The one and only wrapper instance
-     */
-    public static final SyspropsMapWrapper INSTANCE = new SyspropsMapWrapper();
-
-    /**
-     * A {@link PropertyResolver} with no parent that exposes the system properties
-     */
-    public static final PropertyResolver SYSPROPS_RESOLVER = new PropertyResolver() {
-        @Override
-        public Map<String, Object> getProperties() {
-            return SyspropsMapWrapper.INSTANCE;
-        }
-
-        @Override
-        public PropertyResolver getParentPropertyResolver() {
-            return null;
-        }
-
-        @Override
-        public String toString() {
-            return "SYSPROPS";
-        }
-    };
-
-    private SyspropsMapWrapper() {
-        super();
-    }
-
-    @Override
-    public void clear() {
-        throw new UnsupportedOperationException("sysprops#clear() N/A");
-    }
-
-    @Override
-    public boolean containsKey(Object key) {
-        return get(key) != null;
-    }
-
-    @Override
-    public boolean containsValue(Object value) {
-        // not the most efficient implementation, but we do not expect it to be called much
-        Properties props = System.getProperties();
-        for (String key : props.stringPropertyNames()) {
-            if (!isMappedSyspropKey(key)) {
-                continue;
-            }
-
-            Object v = props.getProperty(key);
-            if (Objects.equals(v, value)) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-    @Override
-    public Set<Entry<String, Object>> entrySet() {
-        Properties props = System.getProperties();
-        // return a copy in order to avoid concurrent modifications
-        Set<Entry<String, Object>> entries = new TreeSet<>(MapEntryUtils.byKeyEntryComparator());
-        for (String key : props.stringPropertyNames()) {
-            if (!isMappedSyspropKey(key)) {
-                continue;
-            }
-
-            Object v = props.getProperty(key);
-            if (v != null) {
-                entries.add(new SimpleImmutableEntry<>(getUnmappedSyspropKey(key), v));
-            }
-        }
-
-        return entries;
-    }
-
-    @Override
-    public Object get(Object key) {
-        return (key instanceof String) ? System.getProperty(getMappedSyspropKey(key)) : null;
-    }
-
-    @Override
-    public boolean isEmpty() {
-        return GenericUtils.isEmpty(keySet());
-    }
-
-    @Override
-    public Set<String> keySet() {
-        return System.getProperties()
-                .stringPropertyNames().stream()
-                // filter out any non-SSHD properties
-                .filter(SyspropsMapWrapper::isMappedSyspropKey)
-                .map(SyspropsMapWrapper::getUnmappedSyspropKey)
-                .collect(Collectors.toSet());
-    }
-
-    @Override
-    public Object put(String key, Object value) {
-        throw new UnsupportedOperationException("sysprops#put(" + key + ")[" + value + "] N/A");
-    }
-
-    @Override
-    public void putAll(Map<? extends String, ?> m) {
-        throw new UnsupportedOperationException("sysprops#putAll(" + m + ") N/A");
-    }
-
-    @Override
-    public Object remove(Object key) {
-        throw new UnsupportedOperationException("sysprops#remove(" + key + ") N/A");
-    }
-
-    @Override
-    public int size() {
-        return GenericUtils.size(keySet());
-    }
-
-    @Override
-    public Collection<Object> values() {
-        Properties props = System.getProperties();
-        // return a copy in order to avoid concurrent modifications
-        return props
-                .stringPropertyNames().stream()
-                .filter(SyspropsMapWrapper::isMappedSyspropKey)
-                .map(props::get)
-                .collect(Collectors.toList());
-    }
-
-    @Override
-    public String toString() {
-        return Objects.toString(System.getProperties(), null);
-    }
-
-    /**
-     * @param key Key to be tested
-     * @return {@code true} if key starts with {@link #SYSPROPS_MAPPED_PREFIX}
-     * and continues with a dot followed by some characters
-     */
-    public static boolean isMappedSyspropKey(String key) {
-        return (GenericUtils.length(key) > (SYSPROPS_MAPPED_PREFIX.length() + 1))
-            && key.startsWith(SYSPROPS_MAPPED_PREFIX)
-            && (key.charAt(SYSPROPS_MAPPED_PREFIX.length()) == '.');
-    }
-
-    /**
-     * @param key Key to be transformed
-     * @return The &quot;pure&quot; key name if a mapped one, same as input otherwise
-     * @see #isMappedSyspropKey(String)
-     */
-    public static String getUnmappedSyspropKey(Object key) {
-        String s = Objects.toString(key);
-        return isMappedSyspropKey(s) ? s.substring(SYSPROPS_MAPPED_PREFIX.length() + 1 /* skip dot */) : s;
-    }
-
-    /**
-     * @param key The original key
-     * @return A key prefixed by {@link #SYSPROPS_MAPPED_PREFIX}
-     * @see #isMappedSyspropKey(String)
-     */
-    public static String getMappedSyspropKey(Object key) {
-        return SYSPROPS_MAPPED_PREFIX + "." + key;
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/auth/MutableUserHolder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/auth/MutableUserHolder.java b/sshd-core/src/main/java/org/apache/sshd/common/auth/MutableUserHolder.java
deleted file mode 100644
index 485f2f2..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/auth/MutableUserHolder.java
+++ /dev/null
@@ -1,27 +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.auth;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface MutableUserHolder extends UsernameHolder {
-    void setUsername(String username);
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/auth/UsernameHolder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/auth/UsernameHolder.java b/sshd-core/src/main/java/org/apache/sshd/common/auth/UsernameHolder.java
deleted file mode 100644
index 7ee76ad..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/auth/UsernameHolder.java
+++ /dev/null
@@ -1,32 +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.auth;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FunctionalInterface
-public interface UsernameHolder {
-    /**
-     * @return The attached username - may be {@code null}/empty if holder
-     * not yet initialized
-     */
-    String getUsername();
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/channel/AbstractChannel.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/channel/AbstractChannel.java b/sshd-core/src/main/java/org/apache/sshd/common/channel/AbstractChannel.java
index 085bd41..a6c11ca 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/channel/AbstractChannel.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/channel/AbstractChannel.java
@@ -34,7 +34,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.IntUnaryOperator;
 
-import org.apache.sshd.common.AttributeStore;
 import org.apache.sshd.common.Closeable;
 import org.apache.sshd.common.FactoryManager;
 import org.apache.sshd.common.PropertyResolver;
@@ -964,11 +963,6 @@ public abstract class AbstractChannel
         return (T) attributes.remove(Objects.requireNonNull(key, "No key"));
     }
 
-    @Override
-    public <T> T resolveAttribute(AttributeKey<T> key) {
-        return AttributeStore.resolveAttribute(this, key);
-    }
-
     protected void configureWindow() {
         localWindow.init(this);
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/channel/Channel.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/channel/Channel.java b/sshd-core/src/main/java/org/apache/sshd/common/channel/Channel.java
index 9aa0b0f..1ef143a 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/channel/Channel.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/channel/Channel.java
@@ -21,6 +21,7 @@ package org.apache.sshd.common.channel;
 import java.io.IOException;
 import java.util.Collection;
 import java.util.List;
+import java.util.Objects;
 
 import org.apache.sshd.client.future.OpenFuture;
 import org.apache.sshd.common.AttributeStore;
@@ -211,4 +212,29 @@ public interface Channel
      * @throws IOException If failed to handle the success
      */
     void handleOpenFailure(Buffer buffer) throws IOException;
+
+    @Override
+    default <T> T resolveAttribute(AttributeKey<T> key) {
+        return resolveAttribute(this, key);
+    }
+
+    /**
+     * Attempts to use the channel attribute, if not found then tries the session
+     *
+     * @param <T> The generic attribute type
+     * @param channel The {@link Channel} - ignored if {@code null}
+     * @param key The attribute key - never {@code null}
+     * @return Associated value - {@code null} if not found
+     * @see Session#getFactoryManager()
+     * @see #resolveAttribute(Session, AttributeKey)
+     */
+    static <T> T resolveAttribute(Channel channel, AttributeKey<T> key) {
+        Objects.requireNonNull(key, "No key");
+        if (channel == null) {
+            return null;
+        }
+
+        T value = channel.getAttribute(key);
+        return (value != null) ? value : Session.resolveAttribute(channel.getSession(), key);
+    }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/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
deleted file mode 100644
index c3d9426..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/cipher/BaseCipher.java
+++ /dev/null
@@ -1,113 +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.cipher;
-
-import javax.crypto.spec.IvParameterSpec;
-import javax.crypto.spec.SecretKeySpec;
-
-import org.apache.sshd.common.SshException;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * Base class for all Cipher implementations delegating to the JCE provider.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class BaseCipher implements Cipher {
-
-    protected javax.crypto.Cipher cipher;
-    private final int ivsize;
-    private final int bsize;
-    private final String algorithm;
-    private final String transformation;
-    private String s;
-
-    public BaseCipher(int ivsize, int bsize, String algorithm, String transformation) {
-        this.ivsize = ivsize;
-        this.bsize = bsize;
-        this.algorithm = ValidateUtils.checkNotNullAndNotEmpty(algorithm, "No algorithm");
-        this.transformation = ValidateUtils.checkNotNullAndNotEmpty(transformation, "No transformation");
-    }
-
-    @Override
-    public String getAlgorithm() {
-        return algorithm;
-    }
-
-    @Override
-    public String getTransformation() {
-        return transformation;
-    }
-
-    @Override
-    public int getIVSize() {
-        return ivsize;
-    }
-
-    @Override
-    public int getBlockSize() {
-        return bsize;
-    }
-
-    @Override
-    public void init(Mode mode, byte[] key, byte[] iv) throws Exception {
-        key = resize(key, getBlockSize());
-        iv = resize(iv, getIVSize());
-        try {
-            cipher = SecurityUtils.getCipher(getTransformation());
-            cipher.init(Mode.Encrypt.equals(mode) ? javax.crypto.Cipher.ENCRYPT_MODE : javax.crypto.Cipher.DECRYPT_MODE,
-                    new SecretKeySpec(key, getAlgorithm()),
-                    new IvParameterSpec(iv));
-        } catch (Exception e) {
-            cipher = null;
-            throw new SshException("Unable to initialize cipher " + this, e);
-        }
-    }
-
-    @Override
-    public void update(byte[] input, int inputOffset, int inputLen) throws Exception {
-        cipher.update(input, inputOffset, inputLen, input, inputOffset);
-    }
-
-    protected static byte[] resize(byte[] data, int size) {
-        if (data.length > size) {
-            byte[] tmp = new byte[size];
-            System.arraycopy(data, 0, tmp, 0, size);
-            data = tmp;
-        }
-        return data;
-    }
-
-    @Override
-    public String toString() {
-        synchronized (this) {
-            if (s == null) {
-                s = getClass().getSimpleName()
-                    + "[" + getAlgorithm()
-                    + "," + getIVSize()
-                    + "," + getBlockSize()
-                    + "," + getTransformation()
-                    + "]";
-            }
-        }
-
-        return s;
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/cipher/BaseRC4Cipher.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/cipher/BaseRC4Cipher.java b/sshd-core/src/main/java/org/apache/sshd/common/cipher/BaseRC4Cipher.java
deleted file mode 100644
index 2e3fc1e..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/cipher/BaseRC4Cipher.java
+++ /dev/null
@@ -1,54 +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.cipher;
-
-import javax.crypto.spec.SecretKeySpec;
-
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class BaseRC4Cipher extends BaseCipher {
-
-    public static final int SKIP_SIZE = 1536;
-
-    public BaseRC4Cipher(int ivsize, int bsize) {
-        super(ivsize, bsize, "ARCFOUR", "RC4");
-    }
-
-    @Override
-    public void init(Mode mode, byte[] key, byte[] iv) throws Exception {
-        key = resize(key, getBlockSize());
-        try {
-            cipher = SecurityUtils.getCipher(getTransformation());
-            cipher.init(Mode.Encrypt.equals(mode)  ? javax.crypto.Cipher.ENCRYPT_MODE : javax.crypto.Cipher.DECRYPT_MODE,
-                    new SecretKeySpec(key, getAlgorithm()));
-
-            byte[] foo = new byte[1];
-            for (int i = 0; i < SKIP_SIZE; i++) {
-                cipher.update(foo, 0, 1, foo, 0);
-            }
-        } catch (Exception e) {
-            cipher = null;
-            throw e;
-        }
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/cipher/BuiltinCiphers.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/cipher/BuiltinCiphers.java b/sshd-core/src/main/java/org/apache/sshd/common/cipher/BuiltinCiphers.java
deleted file mode 100644
index 8609d50..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/cipher/BuiltinCiphers.java
+++ /dev/null
@@ -1,348 +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.cipher;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.List;
-import java.util.Map;
-import java.util.NavigableSet;
-import java.util.Objects;
-import java.util.Set;
-import java.util.SortedSet;
-import java.util.TreeMap;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.NamedResource;
-import org.apache.sshd.common.config.NamedFactoriesListParseResult;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-
-/**
- * Provides easy access to the currently implemented ciphers
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public enum BuiltinCiphers implements CipherFactory {
-    none(Constants.NONE, 0, 0, "None", "None") {
-        @Override
-        public Cipher create() {
-            return new CipherNone();
-        }
-    },
-    aes128cbc(Constants.AES128_CBC, 16, 16, "AES", "AES/CBC/NoPadding"),
-    aes128ctr(Constants.AES128_CTR, 16, 16, "AES", "AES/CTR/NoPadding"),
-    aes192cbc(Constants.AES192_CBC, 16, 24, "AES", "AES/CBC/NoPadding"),
-    aes192ctr(Constants.AES192_CTR, 16, 24, "AES", "AES/CTR/NoPadding"),
-    aes256cbc(Constants.AES256_CBC, 16, 32, "AES", "AES/CBC/NoPadding"),
-    aes256ctr(Constants.AES256_CTR, 16, 32, "AES", "AES/CTR/NoPadding"),
-    arcfour128(Constants.ARCFOUR128, 8, 16, "ARCFOUR", "RC4") {
-        @Override
-        public Cipher create() {
-            return new BaseRC4Cipher(getIVSize(), getBlockSize());
-        }
-    },
-    arcfour256(Constants.ARCFOUR256, 8, 32, "ARCFOUR", "RC4") {
-        @Override
-        public Cipher create() {
-            return new BaseRC4Cipher(getIVSize(), getBlockSize());
-        }
-    },
-    blowfishcbc(Constants.BLOWFISH_CBC, 8, 16, "Blowfish", "Blowfish/CBC/NoPadding"),
-    tripledescbc(Constants.TRIPLE_DES_CBC, 8, 24, "DESede", "DESede/CBC/NoPadding");
-
-    public static final Set<BuiltinCiphers> VALUES =
-            Collections.unmodifiableSet(EnumSet.allOf(BuiltinCiphers.class));
-
-    private static final Map<String, CipherFactory> EXTENSIONS =
-            new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
-
-    private final String factoryName;
-    private final int ivsize;
-    private final int blocksize;
-    private final int keysize;
-    private final String algorithm;
-    private final String transformation;
-    private final boolean supported;
-
-    BuiltinCiphers(String factoryName, int ivsize, int blocksize, String algorithm, String transformation) {
-        this.factoryName = factoryName;
-        this.ivsize = ivsize;
-        this.blocksize = blocksize;
-        this.keysize = blocksize * Byte.SIZE;
-        this.algorithm = algorithm;
-        this.transformation = transformation;
-        /*
-         * This can be done once since in order to change the support the JVM
-         * needs to be stopped, some unlimited-strength files need be installed
-         * and then the JVM re-started. Therefore, the answer is not going to
-         * change while the JVM is running
-         */
-        this.supported = Constants.NONE.equals(factoryName) || Cipher.checkSupported(this.transformation, this.keysize);
-    }
-
-    @Override
-    public final String getName() {
-        return factoryName;
-    }
-
-    @Override
-    public final String toString() {
-        return getName();
-    }
-
-    /**
-     * @return {@code true} if the current JVM configuration supports this
-     * cipher - e.g., AES-256 requires the <A HREF="http://www.oracle.com/technetwork/java/javase/downloads/">
-     * Java Cryptography Extension (JCE)</A>
-     */
-    @Override
-    public boolean isSupported() {
-        return supported;
-    }
-
-    /**
-     * @return The key size (in bits) for the cipher
-     */
-    public int getKeySize() {
-        return keysize;
-    }
-
-    @Override
-    public int getIVSize() {
-        return ivsize;
-    }
-
-    @Override
-    public int getBlockSize() {
-        return blocksize;
-    }
-
-    @Override
-    public String getAlgorithm() {
-        return algorithm;
-    }
-
-    @Override
-    public String getTransformation() {
-        return transformation;
-    }
-
-    @Override
-    public Cipher create() {
-        return new BaseCipher(getIVSize(), getBlockSize(), getAlgorithm(), getTransformation());
-    }
-
-    /**
-     * Registered a {@link NamedFactory} to be available besides the built-in
-     * ones when parsing configuration
-     *
-     * @param extension The factory to register
-     * @throws IllegalArgumentException if factory instance is {@code null},
-     * or overrides a built-in one or overrides another registered factory
-     * with the same name (case <U>insensitive</U>).
-     */
-    public static void registerExtension(CipherFactory extension) {
-        String name = Objects.requireNonNull(extension, "No extension provided").getName();
-        ValidateUtils.checkTrue(fromFactoryName(name) == null, "Extension overrides built-in: %s", name);
-
-        synchronized (EXTENSIONS) {
-            ValidateUtils.checkTrue(!EXTENSIONS.containsKey(name), "Extension overrides existing: %s", name);
-            EXTENSIONS.put(name, extension);
-        }
-    }
-
-    /**
-     * @return A {@link SortedSet} of the currently registered extensions, sorted
-     * according to the factory name (case <U>insensitive</U>)
-     */
-    public static NavigableSet<CipherFactory> getRegisteredExtensions() {
-        synchronized (EXTENSIONS) {
-            return GenericUtils.asSortedSet(NamedResource.BY_NAME_COMPARATOR, EXTENSIONS.values());
-        }
-    }
-
-    /**
-     * Unregisters specified extension
-     *
-     * @param name The factory name - ignored if {@code null}/empty
-     * @return The registered extension - {@code null} if not found
-     */
-    public static NamedFactory<Cipher> unregisterExtension(String name) {
-        if (GenericUtils.isEmpty(name)) {
-            return null;
-        }
-
-        synchronized (EXTENSIONS) {
-            return EXTENSIONS.remove(name);
-        }
-    }
-
-    /**
-     * @param s The {@link Enum}'s name - ignored if {@code null}/empty
-     * @return The matching {@link BuiltinCiphers} whose {@link Enum#name()} matches
-     * (case <U>insensitive</U>) the provided argument - {@code null} if no match
-     */
-    public static BuiltinCiphers fromString(String s) {
-        if (GenericUtils.isEmpty(s)) {
-            return null;
-        }
-
-        for (BuiltinCiphers c : VALUES) {
-            if (s.equalsIgnoreCase(c.name())) {
-                return c;
-            }
-        }
-
-        return null;
-    }
-
-    /**
-     * @param factory The {@link NamedFactory} for the cipher - ignored if {@code null}
-     * @return The matching {@link BuiltinCiphers} whose factory name matches
-     * (case <U>insensitive</U>) the cipher factory name
-     * @see #fromFactoryName(String)
-     */
-    public static BuiltinCiphers fromFactory(NamedFactory<Cipher> factory) {
-        if (factory == null) {
-            return null;
-        } else {
-            return fromFactoryName(factory.getName());
-        }
-    }
-
-    /**
-     * @param name The factory name - ignored if {@code null}/empty
-     * @return The matching {@link BuiltinCiphers} whose factory name matches
-     * (case <U>insensitive</U>) the provided name - {@code null} if no match
-     */
-    public static BuiltinCiphers fromFactoryName(String name) {
-        return NamedResource.findByName(name, String.CASE_INSENSITIVE_ORDER, VALUES);
-    }
-
-    /**
-     * @param ciphers A comma-separated list of ciphers' names - ignored if {@code null}/empty
-     * @return A {@link ParseResult} containing the successfully parsed
-     * factories and the unknown ones. <B>Note:</B> it is up to caller to
-     * ensure that the lists do not contain duplicates
-     */
-    public static ParseResult parseCiphersList(String ciphers) {
-        return parseCiphersList(GenericUtils.split(ciphers, ','));
-    }
-
-    public static ParseResult parseCiphersList(String... ciphers) {
-        return parseCiphersList(GenericUtils.isEmpty((Object[]) ciphers) ? Collections.emptyList() : Arrays.asList(ciphers));
-    }
-
-    public static ParseResult parseCiphersList(Collection<String> ciphers) {
-        if (GenericUtils.isEmpty(ciphers)) {
-            return ParseResult.EMPTY;
-        }
-
-        List<CipherFactory> factories = new ArrayList<>(ciphers.size());
-        List<String> unknown = Collections.emptyList();
-        for (String name : ciphers) {
-            CipherFactory c = resolveFactory(name);
-            if (c != null) {
-                factories.add(c);
-            } else {
-                // replace the (unmodifiable) empty list with a real one
-                if (unknown.isEmpty()) {
-                    unknown = new ArrayList<>();
-                }
-                unknown.add(name);
-            }
-        }
-
-        return new ParseResult(factories, unknown);
-    }
-
-    /**
-     * @param name The factory name
-     * @return The factory or {@code null} if it is neither a built-in one
-     * or a registered extension
-     */
-    public static CipherFactory resolveFactory(String name) {
-        if (GenericUtils.isEmpty(name)) {
-            return null;
-        }
-
-        CipherFactory c = fromFactoryName(name);
-        if (c != null) {
-            return c;
-        }
-
-        synchronized (EXTENSIONS) {
-            return EXTENSIONS.get(name);
-        }
-    }
-
-    /**
-     * Holds the result of {@link BuiltinCiphers#parseCiphersList(String)}
-     *
-     * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
-     */
-    public static class ParseResult extends NamedFactoriesListParseResult<Cipher, CipherFactory> {
-        public static final ParseResult EMPTY = new ParseResult(Collections.emptyList(), Collections.emptyList());
-
-        public ParseResult(List<CipherFactory> parsed, List<String> unsupported) {
-            super(parsed, unsupported);
-        }
-    }
-
-    public static final class Constants {
-        public static final String NONE = "none";
-        public static final Pattern NONE_CIPHER_PATTERN =
-                Pattern.compile("(^|.*,)" + NONE + "($|,.*)");
-
-        public static final String AES128_CBC = "aes128-cbc";
-        public static final String AES128_CTR = "aes128-ctr";
-        public static final String AES192_CBC = "aes192-cbc";
-        public static final String AES192_CTR = "aes192-ctr";
-        public static final String AES256_CBC = "aes256-cbc";
-        public static final String AES256_CTR = "aes256-ctr";
-        public static final String ARCFOUR128 = "arcfour128";
-        public static final String ARCFOUR256 = "arcfour256";
-        public static final String BLOWFISH_CBC = "blowfish-cbc";
-        public static final String TRIPLE_DES_CBC = "3des-cbc";
-
-        private Constants() {
-            throw new UnsupportedOperationException("No instance allowed");
-        }
-
-        /**
-         * @param s A comma-separated list of ciphers - ignored if {@code null}/empty
-         * @return {@code true} if the {@link #NONE} cipher name appears in it
-         */
-        public static boolean isNoneCipherIncluded(String s) {
-            if (GenericUtils.isEmpty(s)) {
-                return false;
-            }
-            Matcher m = NONE_CIPHER_PATTERN.matcher(s);
-            return m.matches();
-        }
-
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/cipher/Cipher.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/cipher/Cipher.java b/sshd-core/src/main/java/org/apache/sshd/common/cipher/Cipher.java
deleted file mode 100644
index 868e983..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/cipher/Cipher.java
+++ /dev/null
@@ -1,89 +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.cipher;
-
-import org.apache.sshd.common.util.NumberUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-
-/**
- * Wrapper for a cryptographic cipher, used either for encryption
- * or decryption.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface Cipher extends CipherInformation {
-
-    enum Mode {
-        Encrypt, Decrypt
-    }
-
-    /**
-     * Initialize the cipher for encryption or decryption with
-     * the given key and initialization vector
-     *
-     * @param mode Encrypt/Decrypt initialization
-     * @param key  Key bytes
-     * @param iv   Initialization vector bytes
-     * @throws Exception If failed to initialize
-     */
-    void init(Mode mode, byte[] key, byte[] iv) throws Exception;
-
-    /**
-     * Performs in-place encryption or decryption on the given data.
-     *
-     * @param input The input/output bytes
-     * @throws Exception If failed to execute
-     * @see #update(byte[], int, int)
-     */
-    default void update(byte[] input) throws Exception {
-        update(input, 0, NumberUtils.length(input));
-    }
-
-    /**
-     * Performs in-place encryption or decryption on the given data.
-     *
-     * @param input       The input/output bytes
-     * @param inputOffset The offset of the data in the data buffer
-     * @param inputLen    The number of bytes to update - starting at the given offset
-     * @throws Exception If failed to execute
-     */
-    void update(byte[] input, int inputOffset, int inputLen) throws Exception;
-
-    /**
-     * @param xform The full cipher transformation - e.g., AES/CBC/NoPadding -
-     * never {@code null}/empty
-     * @param keyLength The required key length in bits - always positive
-     * @return {@code true} if the cipher transformation <U>and</U> required
-     * key length are supported
-     * @see javax.crypto.Cipher#getMaxAllowedKeyLength(String)
-     */
-    static boolean checkSupported(String xform, int keyLength) {
-        ValidateUtils.checkNotNullAndNotEmpty(xform, "No transformation");
-        if (keyLength <= 0) {
-            throw new IllegalArgumentException("Bad key length (" + keyLength + ") for cipher=" + xform);
-        }
-
-        try {
-            int maxKeyLength = javax.crypto.Cipher.getMaxAllowedKeyLength(xform);
-            return maxKeyLength >= keyLength;
-        } catch (Exception e) {
-            return false;
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherFactory.java
deleted file mode 100644
index 36909f3..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherFactory.java
+++ /dev/null
@@ -1,31 +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.cipher;
-
-import org.apache.sshd.common.BuiltinFactory;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-// CHECKSTYLE:OFF
-public interface CipherFactory extends BuiltinFactory<Cipher>, CipherInformation {
-    // nothing extra
-}
-//CHECKSTYLE:ON

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherInformation.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherInformation.java b/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherInformation.java
deleted file mode 100644
index f17fd16..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherInformation.java
+++ /dev/null
@@ -1,45 +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.cipher;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface CipherInformation {
-    /**
-     * @return The cipher's algorithm
-     */
-    String getAlgorithm();
-
-    /**
-     * @return The actual transformation used - e.g., AES/CBC/NoPadding
-     */
-    String getTransformation();
-
-    /**
-     * @return Size of the initialization vector (in bytes)
-     */
-    int getIVSize();
-
-    /**
-     * @return The block size (in bytes) for this cipher
-     */
-    int getBlockSize();
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/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
deleted file mode 100644
index 15b6e9f..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherNone.java
+++ /dev/null
@@ -1,63 +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.cipher;
-
-/**
- * Represents a no-op cipher.
- * This cipher can not really be used during authentication and should only
- * be used after, so that authentication remains secured, but not the remaining
- * of the exchanges.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class CipherNone implements Cipher {
-    public CipherNone() {
-        super();
-    }
-
-    @Override
-    public String getAlgorithm() {
-        return "none";
-    }
-
-    @Override
-    public String getTransformation() {
-        return "none";
-    }
-
-    @Override
-    public int getIVSize() {
-        return 8;   // dummy
-    }
-
-    @Override
-    public int getBlockSize() {
-        return 16;  // dummy
-    }
-
-    @Override
-    public void init(Mode mode, byte[] bytes, byte[] bytes1) throws Exception {
-        // ignored - always succeeds
-    }
-
-    @Override
-    public void update(byte[] input, int inputOffset, int inputLen) throws Exception {
-        // ignored - always succeeds
-    }
-}


[47/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common utilities code from sshd-core into sshd-common (new artifact)

Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/cipher/ECCurves.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/cipher/ECCurves.java b/sshd-common/src/main/java/org/apache/sshd/common/cipher/ECCurves.java
new file mode 100644
index 0000000..ae39dd3
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/cipher/ECCurves.java
@@ -0,0 +1,580 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.cipher;
+
+import java.io.ByteArrayOutputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.StreamCorruptedException;
+import java.math.BigInteger;
+import java.security.interfaces.ECKey;
+import java.security.spec.ECField;
+import java.security.spec.ECFieldFp;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPoint;
+import java.security.spec.EllipticCurve;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.NavigableSet;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.OptionalFeature;
+import org.apache.sshd.common.config.keys.KeyEntryResolver;
+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.NumberUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * Utilities for working with elliptic curves.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public enum ECCurves implements NamedResource, OptionalFeature {
+    nistp256(Constants.NISTP256, new int[]{1, 2, 840, 10045, 3, 1, 7},
+            new ECParameterSpec(
+                    new EllipticCurve(
+                            new ECFieldFp(new BigInteger("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF", 16)),
+                            new BigInteger("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC", 16),
+                            new BigInteger("5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", 16)),
+                    new ECPoint(
+                            new BigInteger("6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296", 16),
+                            new BigInteger("4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5", 16)),
+                    new BigInteger("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551", 16),
+                    1),
+            32,
+            BuiltinDigests.sha256),
+    nistp384(Constants.NISTP384, new int[]{1, 3, 132, 0, 34},
+            new ECParameterSpec(
+                    new EllipticCurve(
+                            new ECFieldFp(new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF", 16)),
+                            new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC", 16),
+                            new BigInteger("B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF", 16)),
+                    new ECPoint(
+                            new BigInteger("AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7", 16),
+                            new BigInteger("3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F", 16)),
+                    new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973", 16),
+                    1),
+            48,
+            BuiltinDigests.sha384),
+    nistp521(Constants.NISTP521, new int[]{1, 3, 132, 0, 35},
+            new ECParameterSpec(
+                    new EllipticCurve(
+                            new ECFieldFp(new BigInteger("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+                                                          + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 16)),
+                            new BigInteger("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+                                                + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC", 16),
+                            new BigInteger("0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951"
+                                            + "EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00", 16)),
+                    new ECPoint(
+                            new BigInteger("00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77"
+                                            + "EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66", 16),
+                            new BigInteger("011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE7299"
+                                            + "5EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650", 16)),
+                    new BigInteger("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B"
+                                    + "7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409", 16),
+                    1),
+            66,
+            BuiltinDigests.sha512);
+
+    /**
+     * A {@link Set} of all the known curves
+     */
+    public static final Set<ECCurves> VALUES =
+            Collections.unmodifiableSet(EnumSet.allOf(ECCurves.class));
+
+    /**
+     * A {@link Set} of all the known curves names
+     */
+    public static final NavigableSet<String> NAMES =
+            Collections.unmodifiableNavigableSet(GenericUtils.mapSort(
+                    VALUES,
+                    ECCurves::getName,
+                    String.CASE_INSENSITIVE_ORDER));
+
+    /**
+     * A {@link Set} of all the known curves key types
+     */
+    public static final NavigableSet<String> KEY_TYPES =
+            Collections.unmodifiableNavigableSet(GenericUtils.mapSort(
+                    VALUES,
+                    ECCurves::getKeyType,
+                    String.CASE_INSENSITIVE_ORDER));
+
+    public static final Comparator<ECCurves> BY_KEY_SIZE = (o1, o2) -> {
+        int k1 = (o1 == null) ? Integer.MAX_VALUE : o1.getKeySize();
+        int k2 = (o2 == null) ? Integer.MAX_VALUE : o2.getKeySize();
+        return Integer.compare(k1, k2);
+    };
+
+    public static final List<ECCurves> SORTED_KEY_SIZE =
+            Collections.unmodifiableList(VALUES.stream()
+                    .sorted(BY_KEY_SIZE)
+                    .collect(Collectors.toList()));
+
+    private final String name;
+    private final String keyType;
+    private final String oidString;
+    private final List<Integer> oidValue;
+    private final ECParameterSpec params;
+    private final int keySize;
+    private final int numOctets;
+    private final DigestFactory digestFactory;
+
+    ECCurves(String name, int[] oid, ECParameterSpec params, int numOctets, DigestFactory digestFactory) {
+        this.name = ValidateUtils.checkNotNullAndNotEmpty(name, "No curve name");
+        this.oidString = NumberUtils.join('.', ValidateUtils.checkNotNullAndNotEmpty(oid, "No OID"));
+        this.oidValue = Collections.unmodifiableList(NumberUtils.asList(oid));
+        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 = Objects.requireNonNull(digestFactory, "No digestFactory");
+    }
+
+    @Override   // The curve name
+    public final String getName() {
+        return name;
+    }
+
+    public final String getOID() {
+        return oidString;
+    }
+
+    public final List<Integer> getOIDValue() {
+        return oidValue;
+    }
+
+    /**
+     * @return The standard key type used to represent this curve
+     */
+    public final String getKeyType() {
+        return keyType;
+    }
+
+    @Override
+    public final boolean isSupported() {
+        return SecurityUtils.isECCSupported() && digestFactory.isSupported();
+    }
+
+    public final ECParameterSpec getParameters() {
+        return params;
+    }
+
+    /**
+     * @return The size (in bits) of the key
+     */
+    public final int getKeySize() {
+        return keySize;
+    }
+
+    /**
+     * @return The number of octets used to represent the point(s) for the curve
+     */
+    public final int getNumPointOctets() {
+        return numOctets;
+    }
+
+    /**
+     * @return The {@link Digest} to use when hashing the curve's parameters
+     */
+    public final Digest getDigestForParams() {
+        return digestFactory.create();
+    }
+
+    /**
+     * @param type The key type value - ignored if {@code null}/empty
+     * @return The matching {@link ECCurves} constant - {@code null} if
+     * no match found case <U>insensitive</U>
+     */
+    public static ECCurves fromKeyType(String type) {
+        if (GenericUtils.isEmpty(type)) {
+            return null;
+        }
+
+        for (ECCurves c : VALUES) {
+            if (type.equalsIgnoreCase(c.getKeyType())) {
+                return c;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * @param name The curve name (case <U>insensitive</U> - ignored if
+     *             {@code null}/empty
+     * @return The matching {@link ECCurves} instance - {@code null} if no
+     * match found
+     */
+    public static ECCurves fromCurveName(String name) {
+        return NamedResource.findByName(name, String.CASE_INSENSITIVE_ORDER, VALUES);
+    }
+
+    /**
+     * @param key The {@link ECKey} - ignored if {@code null}
+     * @return The matching {@link ECCurves} instance - {@code null} if no
+     * match found
+     */
+    public static ECCurves fromECKey(ECKey key) {
+        return fromCurveParameters((key == null) ? null : key.getParams());
+    }
+
+    /**
+     * @param params The curve's {@link ECParameterSpec} - ignored if {@code null}
+     * @return The matching {@link ECCurves} value - {@code null} if no match found
+     * @see #getCurveSize(ECParameterSpec)
+     * @see #fromCurveSize(int)
+     */
+    public static ECCurves fromCurveParameters(ECParameterSpec params) {
+        if (params == null) {
+            return null;
+        } else {
+            return fromCurveSize(getCurveSize(params));
+        }
+    }
+
+    /**
+     * @param keySize The key size (in bits)
+     * @return The matching {@link ECCurves} value - {@code null} if no
+     * match found
+     */
+    public static ECCurves fromCurveSize(int keySize) {
+        if (keySize <= 0) {
+            return null;
+        }
+
+        for (ECCurves c : VALUES) {
+            if (keySize == c.getKeySize()) {
+                return c;
+            }
+        }
+
+        return null;
+    }
+
+    public static ECCurves fromOIDValue(List<? extends Number> oid) {
+        if (GenericUtils.isEmpty(oid)) {
+            return null;
+        }
+
+        for (ECCurves c : VALUES) {
+            List<? extends Number> v = c.getOIDValue();
+            if (oid.size() != v.size()) {
+                continue;
+            }
+
+            boolean matches = true;
+            for (int index = 0; index < v.size(); index++) {
+                Number exp = v.get(index);
+                Number act = oid.get(index);
+                if (exp.intValue() != act.intValue()) {
+                    matches = false;
+                    break;
+                }
+            }
+
+            if (matches) {
+                return c;
+            }
+        }
+
+        return null;
+    }
+
+    public static ECCurves fromOID(String oid) {
+        if (GenericUtils.isEmpty(oid)) {
+            return null;
+        }
+
+        for (ECCurves c : VALUES) {
+            if (oid.equalsIgnoreCase(c.getOID())) {
+                return c;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * @param params The curve's {@link ECParameterSpec}
+     * @return The curve's key size in bits
+     * @throws IllegalArgumentException if invalid parameters provided
+     */
+    public static int getCurveSize(ECParameterSpec params) {
+        EllipticCurve curve = Objects.requireNonNull(params, "No EC params").getCurve();
+        ECField field = Objects.requireNonNull(curve, "No EC curve").getField();
+        return Objects.requireNonNull(field, "No EC field").getFieldSize();
+    }
+
+    public static byte[] encodeECPoint(ECPoint group, ECParameterSpec params) {
+        return encodeECPoint(group, params.getCurve());
+    }
+
+    public static byte[] encodeECPoint(ECPoint group, EllipticCurve curve) {
+        // M has len 2 ceil(log_2(q)/8) + 1 ?
+        int elementSize = (curve.getField().getFieldSize() + 7) / 8;
+        byte[] m = new byte[2 * elementSize + 1];
+
+        // Uncompressed format
+        m[0] = 0x04;
+
+        byte[] affineX = removeLeadingZeroes(group.getAffineX().toByteArray());
+        System.arraycopy(affineX, 0, m, 1 + elementSize - affineX.length, affineX.length);
+
+        byte[] affineY = removeLeadingZeroes(group.getAffineY().toByteArray());
+        System.arraycopy(affineY, 0, m, 1 + elementSize + elementSize - affineY.length, affineY.length);
+
+        return m;
+    }
+
+    private static byte[] removeLeadingZeroes(byte[] input) {
+        if (input[0] != 0x00) {
+            return input;
+        }
+
+        int pos = 1;
+        while (pos < input.length - 1 && input[pos] == 0x00) {
+            pos++;
+        }
+
+        byte[] output = new byte[input.length - pos];
+        System.arraycopy(input, pos, output, 0, output.length);
+        return output;
+    }
+
+    /**
+     * Converts the given octet string (defined by ASN.1 specifications) to a {@link BigInteger}
+     * As octet strings always represent positive integers, a zero-byte is prepended to
+     * the given array if necessary (if is MSB equal to 1), then this is converted to BigInteger
+     * The conversion is defined in the Section 2.3.8
+     *
+     * @param octets - octet string bytes to be converted
+     * @return The {@link BigInteger} representation of the octet string
+     */
+    public static BigInteger octetStringToInteger(byte... octets) {
+        if (octets == null) {
+            return null;
+        } else if (octets.length == 0) {
+            return BigInteger.ZERO;
+        } else {
+            return new BigInteger(1, octets);
+        }
+    }
+
+    public static ECPoint octetStringToEcPoint(byte... octets) {
+        if (NumberUtils.isEmpty(octets)) {
+            return null;
+        }
+
+        int startIndex = findFirstNonZeroIndex(octets);
+        if (startIndex < 0) {
+            throw new IllegalArgumentException("All zeroes ECPoint N/A");
+        }
+
+        byte indicator = octets[startIndex];
+        ECCurves.ECPointCompression compression = ECCurves.ECPointCompression.fromIndicatorValue(indicator);
+        if (compression == null) {
+            throw new UnsupportedOperationException("Unknown compression indicator value: 0x" + Integer.toHexString(indicator & 0xFF));
+        }
+
+        // The coordinates actually start after the compression indicator
+        return compression.octetStringToEcPoint(octets, startIndex + 1, octets.length - startIndex - 1);
+    }
+
+    private static int findFirstNonZeroIndex(byte... octets) {
+        if (NumberUtils.isEmpty(octets)) {
+            return -1;
+        }
+
+        for (int index = 0; index < octets.length; index++) {
+            if (octets[index] != 0) {
+                return index;
+            }
+        }
+
+        return -1;    // all zeroes
+    }
+
+    public static final class Constants {
+        /**
+         * Standard prefix of NISTP key types when encoded
+         */
+        public static final String ECDSA_SHA2_PREFIX = "ecdsa-sha2-";
+
+        public static final String NISTP256 = "nistp256";
+        public static final String NISTP384 = "nistp384";
+        public static final String NISTP521 = "nistp521";
+
+        private Constants() {
+            throw new UnsupportedOperationException("No instance allowed");
+        }
+    }
+
+    /**
+     * The various {@link ECPoint} representation compression indicators
+     *
+     * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+     * @see <A HREF="https://www.ietf.org/rfc/rfc5480.txt">RFC-5480 - section 2.2</A>
+     */
+    public enum ECPointCompression {
+        // see http://tools.ietf.org/html/draft-jivsov-ecc-compact-00
+        // see http://crypto.stackexchange.com/questions/8914/ecdsa-compressed-public-key-point-back-to-uncompressed-public-key-point
+        VARIANT2((byte) 0x02) {
+            @Override
+            public ECPoint octetStringToEcPoint(byte[] octets, int startIndex, int len) {
+                byte[] xp = new byte[len];
+                System.arraycopy(octets, startIndex, xp, 0, len);
+                BigInteger x = octetStringToInteger(xp);
+
+                // TODO derive even Y...
+                throw new UnsupportedOperationException("octetStringToEcPoint(" + name() + ")(X=" + x + ") compression support N/A");
+            }
+        },
+        VARIANT3((byte) 0x03) {
+            @Override
+            public ECPoint octetStringToEcPoint(byte[] octets, int startIndex, int len) {
+                byte[] xp = new byte[len];
+                System.arraycopy(octets, startIndex, xp, 0, len);
+                BigInteger x = octetStringToInteger(xp);
+
+                // TODO derive odd Y...
+                throw new UnsupportedOperationException("octetStringToEcPoint(" + name() + ")(X=" + x + ") compression support N/A");
+            }
+        },
+        UNCOMPRESSED((byte) 0x04) {
+            @Override
+            public ECPoint octetStringToEcPoint(byte[] octets, int startIndex, int len) {
+                int numElements = len / 2;    /* x, y */
+                if (len != (numElements * 2)) {    // make sure length is not odd
+                    throw new IllegalArgumentException("octetStringToEcPoint(" + name() + ") "
+                            + " invalid remainder octets representation: "
+                            + " expected=" + (2 * numElements) + ", actual=" + len);
+                }
+
+                byte[] xp = new byte[numElements];
+                byte[] yp = new byte[numElements];
+                System.arraycopy(octets, startIndex, xp, 0, numElements);
+                System.arraycopy(octets, startIndex + numElements, yp, 0, numElements);
+
+                BigInteger x = octetStringToInteger(xp);
+                BigInteger y = octetStringToInteger(yp);
+                return new ECPoint(x, y);
+            }
+
+            @Override
+            public void writeECPoint(OutputStream s, String curveName, ECPoint p) throws IOException {
+                ECCurves curve = fromCurveName(curveName);
+                if (curve == null) {
+                    throw new StreamCorruptedException("writeECPoint(" + name() + ")[" + curveName + "] cannot determine octets count");
+                }
+
+                int numElements = curve.getNumPointOctets();
+                KeyEntryResolver.encodeInt(s, 1 /* the indicator */ + 2 * numElements);
+                s.write(getIndicatorValue());
+                writeCoordinate(s, "X", p.getAffineX(), numElements);
+                writeCoordinate(s, "Y", p.getAffineY(), numElements);
+            }
+        };
+
+        public static final Set<ECPointCompression> VALUES =
+                Collections.unmodifiableSet(EnumSet.allOf(ECPointCompression.class));
+
+        private final byte indicatorValue;
+
+        ECPointCompression(byte indicator) {
+            indicatorValue = indicator;
+        }
+
+        public final byte getIndicatorValue() {
+            return indicatorValue;
+        }
+
+        public abstract ECPoint octetStringToEcPoint(byte[] octets, int startIndex, int len);
+
+        public byte[] ecPointToOctetString(String curveName, ECPoint p) {
+            try (ByteArrayOutputStream baos = new ByteArrayOutputStream((2 * 66) + Long.SIZE)) {
+                writeECPoint(baos, curveName, p);
+                return baos.toByteArray();
+            } catch (IOException e) {
+                throw new RuntimeException("ecPointToOctetString(" + curveName + ")"
+                        + " failed (" + e.getClass().getSimpleName() + ")"
+                        + " to write data: " + e.getMessage(),
+                        e);
+            }
+        }
+
+        public void writeECPoint(OutputStream s, String curveName, ECPoint p) throws IOException {
+            if (s == null) {
+                throw new EOFException("No output stream");
+            }
+
+            throw new StreamCorruptedException("writeECPoint(" + name() + ")[" + p + "] N/A");
+        }
+
+        protected void writeCoordinate(OutputStream s, String n, BigInteger v, int numElements) throws IOException {
+            byte[] vp = v.toByteArray();
+            int startIndex = 0;
+            int vLen = vp.length;
+            if (vLen > numElements) {
+                if (vp[0] == 0) {   // skip artificial positive sign
+                    startIndex++;
+                    vLen--;
+                }
+            }
+
+            if (vLen > numElements) {
+                throw new StreamCorruptedException("writeCoordinate(" + name() + ")[" + n + "]"
+                        + " value length (" + vLen + ") exceeds max. (" + numElements + ")"
+                        + " for " + v);
+            }
+
+            if (vLen < numElements) {
+                byte[] tmp = new byte[numElements];
+                System.arraycopy(vp, startIndex, tmp, numElements - vLen, vLen);
+                vp = tmp;
+            }
+
+            s.write(vp, startIndex, vLen);
+        }
+
+        public static ECPointCompression fromIndicatorValue(int value) {
+            if ((value < 0) || (value > 0xFF)) {
+                return null;    // must be a byte value
+            }
+
+            for (ECPointCompression c : VALUES) {
+                if (value == c.getIndicatorValue()) {
+                    return c;
+                }
+            }
+
+            return null;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/cipher/package.html
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/cipher/package.html b/sshd-common/src/main/java/org/apache/sshd/common/cipher/package.html
new file mode 100644
index 0000000..197a89d
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/cipher/package.html
@@ -0,0 +1,26 @@
+<!--
+    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.
+-->
+<html>
+<head>
+</head>
+<body>
+
+<a href="{@docRoot}/org/apache/sshd/common/cipher/Cipher.html"><code>Cipher</code></a>
+implementations.
+
+</body>
+</html>

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/compression/BaseCompression.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/compression/BaseCompression.java b/sshd-common/src/main/java/org/apache/sshd/common/compression/BaseCompression.java
new file mode 100644
index 0000000..ff31947
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/compression/BaseCompression.java
@@ -0,0 +1,48 @@
+/*
+ * 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.compression;
+
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class BaseCompression implements Compression {
+    private final String name;
+
+    protected BaseCompression(String name) {
+        this.name = ValidateUtils.checkNotNullAndNotEmpty(name, "No compression name");
+    }
+
+    @Override
+    public final String getName() {
+        return name;
+    }
+
+    @Override
+    public boolean isCompressionExecuted() {
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return getName();
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/compression/BuiltinCompressions.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/compression/BuiltinCompressions.java b/sshd-common/src/main/java/org/apache/sshd/common/compression/BuiltinCompressions.java
new file mode 100644
index 0000000..49ad0ab
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/compression/BuiltinCompressions.java
@@ -0,0 +1,239 @@
+/*
+ * 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.compression;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Map;
+import java.util.NavigableSet;
+import java.util.Objects;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeMap;
+
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.config.NamedFactoriesListParseResult;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public enum BuiltinCompressions implements CompressionFactory {
+    none(Constants.NONE) {
+        @Override
+        public Compression create() {
+            return new CompressionNone();
+        }
+
+        @Override
+        public boolean isCompressionExecuted() {
+            return false;
+        }
+    },
+    zlib(Constants.ZLIB) {
+        @Override
+        public Compression create() {
+            return new CompressionZlib();
+        }
+    },
+    delayedZlib(Constants.DELAYED_ZLIB) {
+        @Override
+        public Compression create() {
+            return new CompressionDelayedZlib();
+        }
+
+        @Override
+        public boolean isDelayed() {
+            return true;
+        }
+    };
+
+    public static final Set<BuiltinCompressions> VALUES =
+            Collections.unmodifiableSet(EnumSet.allOf(BuiltinCompressions.class));
+
+    private static final Map<String, CompressionFactory> EXTENSIONS =
+            new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+
+    private final String name;
+
+    BuiltinCompressions(String n) {
+        name = n;
+    }
+
+    @Override
+    public final String getName() {
+        return name;
+    }
+
+    @Override
+    public boolean isDelayed() {
+        return false;
+    }
+
+    @Override
+    public boolean isCompressionExecuted() {
+        return true;
+    }
+
+    @Override
+    public final String toString() {
+        return getName();
+    }
+
+    @Override
+    public final boolean isSupported() {
+        return true;
+    }
+
+    /**
+     * Registered a {@link org.apache.sshd.common.NamedFactory} to be available besides the built-in
+     * ones when parsing configuration
+     *
+     * @param extension The factory to register
+     * @throws IllegalArgumentException if factory instance is {@code null},
+     * or overrides a built-in one or overrides another registered factory
+     * with the same name (case <U>insensitive</U>).
+     */
+    public static void registerExtension(CompressionFactory extension) {
+        String name = Objects.requireNonNull(extension, "No extension provided").getName();
+        ValidateUtils.checkTrue(fromFactoryName(name) == null, "Extension overrides built-in: %s", name);
+
+        synchronized (EXTENSIONS) {
+            ValidateUtils.checkTrue(!EXTENSIONS.containsKey(name), "Extension overrides existing: %s", name);
+            EXTENSIONS.put(name, extension);
+        }
+    }
+
+    /**
+     * @return A {@link SortedSet} of the currently registered extensions, sorted
+     * according to the factory name (case <U>insensitive</U>)
+     */
+    public static NavigableSet<CompressionFactory> getRegisteredExtensions() {
+        synchronized (EXTENSIONS) {
+            return GenericUtils.asSortedSet(NamedResource.BY_NAME_COMPARATOR, EXTENSIONS.values());
+        }
+    }
+
+    /**
+     * Unregisters specified extension
+     *
+     * @param name The factory name - ignored if {@code null}/empty
+     * @return The registered extension - {@code null} if not found
+     */
+    public static CompressionFactory unregisterExtension(String name) {
+        if (GenericUtils.isEmpty(name)) {
+            return null;
+        }
+
+        synchronized (EXTENSIONS) {
+            return EXTENSIONS.remove(name);
+        }
+    }
+
+    public static BuiltinCompressions fromFactoryName(String name) {
+        return NamedResource.findByName(name, String.CASE_INSENSITIVE_ORDER, VALUES);
+    }
+
+    /**
+     * @param compressions A comma-separated list of Compressions' names - ignored
+     *                     if {@code null}/empty
+     * @return A {@link ParseResult} containing the successfully parsed
+     * factories and the unknown ones. <B>Note:</B> it is up to caller to
+     * ensure that the lists do not contain duplicates
+     */
+    public static ParseResult parseCompressionsList(String compressions) {
+        return parseCompressionsList(GenericUtils.split(compressions, ','));
+    }
+
+    public static ParseResult parseCompressionsList(String... compressions) {
+        return parseCompressionsList(GenericUtils.isEmpty((Object[]) compressions) ? Collections.emptyList() : Arrays.asList(compressions));
+    }
+
+    public static ParseResult parseCompressionsList(Collection<String> compressions) {
+        if (GenericUtils.isEmpty(compressions)) {
+            return ParseResult.EMPTY;
+        }
+
+        List<CompressionFactory> factories = new ArrayList<>(compressions.size());
+        List<String> unknown = Collections.emptyList();
+        for (String name : compressions) {
+            CompressionFactory c = resolveFactory(name);
+            if (c != null) {
+                factories.add(c);
+            } else {
+                // replace the (unmodifiable) empty list with a real one
+                if (unknown.isEmpty()) {
+                    unknown = new ArrayList<>();
+                }
+                unknown.add(name);
+            }
+        }
+
+        return new ParseResult(factories, unknown);
+    }
+
+    /**
+     * @param name The factory name
+     * @return The factory or {@code null} if it is neither a built-in one
+     * or a registered extension
+     */
+    public static CompressionFactory resolveFactory(String name) {
+        if (GenericUtils.isEmpty(name)) {
+            return null;
+        }
+
+        CompressionFactory c = fromFactoryName(name);
+        if (c != null) {
+            return c;
+        }
+
+        synchronized (EXTENSIONS) {
+            return EXTENSIONS.get(name);
+        }
+    }
+
+    /**
+     * Holds the result of {@link BuiltinCompressions#parseCompressionsList(String)}
+     *
+     * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+     */
+    public static class ParseResult extends NamedFactoriesListParseResult<Compression, CompressionFactory> {
+        public static final ParseResult EMPTY = new ParseResult(Collections.emptyList(), Collections.emptyList());
+
+        public ParseResult(List<CompressionFactory> parsed, List<String> unsupported) {
+            super(parsed, unsupported);
+        }
+    }
+
+    public static final class Constants {
+        public static final String NONE = "none";
+        public static final String ZLIB = "zlib";
+        public static final String DELAYED_ZLIB = "zlib@openssh.com";
+
+        private Constants() {
+            throw new UnsupportedOperationException("No instance allowed");
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/compression/Compression.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/compression/Compression.java b/sshd-common/src/main/java/org/apache/sshd/common/compression/Compression.java
new file mode 100644
index 0000000..91ac27e
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/compression/Compression.java
@@ -0,0 +1,71 @@
+/*
+ * 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.compression;
+
+import java.io.IOException;
+
+import org.apache.sshd.common.util.buffer.Buffer;
+
+/**
+ * Interface used to compress the stream of data between the
+ * SSH server and clients.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface Compression extends CompressionInformation {
+
+    /**
+     * Enum identifying if this object will be used to compress
+     * or uncompress data.
+     */
+    enum Type {
+        Inflater,
+        Deflater
+    }
+
+    /**
+     * Initialize this object to either compress or uncompress data.
+     * This method must be called prior to any calls to either
+     * <code>compress</code> or <code>uncompress</code>.
+     * Once the object has been initialized, only one of
+     * <code>compress</code> or <code>uncompress</code> methods can be
+     * called.
+     *
+     * @param type  compression type
+     * @param level compression level
+     */
+    void init(Type type, int level);
+
+    /**
+     * Compress the given buffer in place.
+     *
+     * @param buffer the buffer containing the data to compress
+     * @throws IOException if an error occurs
+     */
+    void compress(Buffer buffer) throws IOException;
+
+    /**
+     * Uncompress the data in a buffer into another buffer.
+     *
+     * @param from the buffer containing the data to uncompress
+     * @param to   the buffer receiving the uncompressed data
+     * @throws IOException if an error occurs
+     */
+    void uncompress(Buffer from, Buffer to) throws IOException;
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionDelayedZlib.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionDelayedZlib.java b/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionDelayedZlib.java
new file mode 100644
index 0000000..c062e38
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionDelayedZlib.java
@@ -0,0 +1,40 @@
+/*
+ * 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.compression;
+
+
+/**
+ * ZLib delayed compression.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ * @see Compression#isDelayed()
+ */
+public class CompressionDelayedZlib extends CompressionZlib {
+    /**
+     * Create a new instance of a delayed ZLib compression
+     */
+    public CompressionDelayedZlib() {
+        super(BuiltinCompressions.Constants.DELAYED_ZLIB);
+    }
+
+    @Override
+    public boolean isDelayed() {
+        return true;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionFactory.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionFactory.java b/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionFactory.java
new file mode 100644
index 0000000..355594a
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionFactory.java
@@ -0,0 +1,31 @@
+/*
+ * 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.compression;
+
+import org.apache.sshd.common.BuiltinFactory;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+// CHECKSTYLE:OFF
+public interface CompressionFactory extends BuiltinFactory<Compression>, CompressionInformation {
+    // nothing extra
+}
+//CHECKSTYLE:ON

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionInformation.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionInformation.java b/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionInformation.java
new file mode 100644
index 0000000..428689f
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionInformation.java
@@ -0,0 +1,42 @@
+/*
+ * 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.compression;
+
+import org.apache.sshd.common.NamedResource;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface CompressionInformation extends NamedResource {
+    /**
+     * Delayed compression is an Open-SSH specific feature which
+     * informs both the client and server to not compress data before
+     * the session has been authenticated.
+     *
+     * @return if the compression is delayed after authentication or not
+     */
+    boolean isDelayed();
+
+    /**
+     * @return {@code true} if there is any compression executed by
+     * this &quot;compressor&quot; - special case for 'none'
+     */
+    boolean isCompressionExecuted();
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionNone.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionNone.java b/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionNone.java
new file mode 100644
index 0000000..815e8ce
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionNone.java
@@ -0,0 +1,76 @@
+/*
+ * 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.compression;
+
+import java.io.IOException;
+import java.io.StreamCorruptedException;
+
+import org.apache.sshd.common.util.buffer.Buffer;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class CompressionNone extends BaseCompression {
+    private Type type;
+    private int level;
+
+    public CompressionNone() {
+        super(BuiltinCompressions.Constants.NONE);
+    }
+
+    @Override
+    public void init(Type type, int level) {
+        this.type = type;
+        this.level = level;
+    }
+
+    @Override
+    public boolean isCompressionExecuted() {
+        return false;
+    }
+
+    @Override
+    public void compress(Buffer buffer) throws IOException {
+        if (!Type.Deflater.equals(type)) {
+            throw new StreamCorruptedException("Not set up for compression: " + type);
+        }
+    }
+
+    @Override
+    public void uncompress(Buffer from, Buffer to) throws IOException {
+        if (!Type.Inflater.equals(type)) {
+            throw new StreamCorruptedException("Not set up for de-compression: " + type);
+        }
+
+        if (from != to) {
+            throw new StreamCorruptedException("Separate de-compression buffers provided");
+        }
+    }
+
+    @Override
+    public boolean isDelayed() {
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return super.toString() + "[" + type + "/" + level + "]";
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionZlib.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionZlib.java b/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionZlib.java
new file mode 100644
index 0000000..e365b91
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionZlib.java
@@ -0,0 +1,85 @@
+/*
+ * 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.compression;
+
+import java.io.IOException;
+import java.util.zip.DataFormatException;
+import java.util.zip.Deflater;
+import java.util.zip.Inflater;
+
+import org.apache.sshd.common.util.buffer.Buffer;
+
+/**
+ * ZLib based Compression.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class CompressionZlib extends BaseCompression {
+
+    private static final int BUF_SIZE = 4096;
+
+    private byte[] tmpbuf = new byte[BUF_SIZE];
+    private Deflater compresser;
+    private Inflater decompresser;
+
+    /**
+     * Create a new instance of a ZLib base compression
+     */
+    public CompressionZlib() {
+        this(BuiltinCompressions.Constants.ZLIB);
+    }
+
+    protected CompressionZlib(String name) {
+        super(name);
+    }
+
+    @Override
+    public boolean isDelayed() {
+        return false;
+    }
+
+    @Override
+    public void init(Type type, int level) {
+        compresser = new Deflater(level);
+        decompresser = new Inflater();
+    }
+
+    @Override
+    public void compress(Buffer buffer) throws IOException {
+        compresser.setInput(buffer.array(), buffer.rpos(), buffer.available());
+        buffer.wpos(buffer.rpos());
+        for (int len = compresser.deflate(tmpbuf, 0, tmpbuf.length, Deflater.SYNC_FLUSH);
+                len > 0;
+                len = compresser.deflate(tmpbuf, 0, tmpbuf.length, Deflater.SYNC_FLUSH)) {
+            buffer.putRawBytes(tmpbuf, 0, len);
+        }
+    }
+
+    @Override
+    public void uncompress(Buffer from, Buffer to) throws IOException {
+        decompresser.setInput(from.array(), from.rpos(), from.available());
+        try {
+            for (int len = decompresser.inflate(tmpbuf); len > 0; len = decompresser.inflate(tmpbuf)) {
+                to.putRawBytes(tmpbuf, 0, len);
+            }
+        } catch (DataFormatException e) {
+            throw new IOException("Error decompressing data", e);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/compression/package.html
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/compression/package.html b/sshd-common/src/main/java/org/apache/sshd/common/compression/package.html
new file mode 100644
index 0000000..9bd4652
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/compression/package.html
@@ -0,0 +1,25 @@
+<!--
+    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.
+-->
+<html>
+<head>
+</head>
+<body>
+
+<a href="{@docRoot}/org/apache/sshd/common/compression/Compression.html"><code>Compression</code></a> implementations.
+
+</body>
+</html>

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/CompressionConfigValue.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/CompressionConfigValue.java b/sshd-common/src/main/java/org/apache/sshd/common/config/CompressionConfigValue.java
new file mode 100644
index 0000000..e153adc
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/CompressionConfigValue.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.config;
+
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.Set;
+
+import org.apache.sshd.common.compression.BuiltinCompressions;
+import org.apache.sshd.common.compression.Compression;
+import org.apache.sshd.common.compression.CompressionFactory;
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * Provides a &quot;bridge&quot; between the configuration values and the
+ * actual {@link org.apache.sshd.common.NamedFactory} for the {@link Compression}.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public enum CompressionConfigValue implements CompressionFactory {
+    YES(BuiltinCompressions.zlib),
+    NO(BuiltinCompressions.none),
+    DELAYED(BuiltinCompressions.delayedZlib);
+
+    public static final Set<CompressionConfigValue> VALUES =
+            Collections.unmodifiableSet(EnumSet.allOf(CompressionConfigValue.class));
+
+    private final CompressionFactory factory;
+
+    CompressionConfigValue(CompressionFactory delegate) {
+        factory = delegate;
+    }
+
+    @Override
+    public final String getName() {
+        return factory.getName();
+    }
+
+    @Override
+    public final Compression create() {
+        return factory.create();
+    }
+
+    @Override
+    public boolean isSupported() {
+        return factory.isSupported();
+    }
+
+    @Override
+    public final String toString() {
+        return getName();
+    }
+
+    @Override
+    public boolean isDelayed() {
+        return factory.isDelayed();
+    }
+
+    @Override
+    public boolean isCompressionExecuted() {
+        return factory.isCompressionExecuted();
+    }
+
+    public static CompressionConfigValue fromName(String n) {
+        if (GenericUtils.isEmpty(n)) {
+            return null;
+        }
+
+        for (CompressionConfigValue v : VALUES) {
+            if (n.equalsIgnoreCase(v.name())) {
+                return v;
+            }
+        }
+
+        return null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/ConfigFileReaderSupport.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/ConfigFileReaderSupport.java b/sshd-common/src/main/java/org/apache/sshd/common/config/ConfigFileReaderSupport.java
new file mode 100644
index 0000000..10b131c
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/ConfigFileReaderSupport.java
@@ -0,0 +1,230 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StreamCorruptedException;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.OpenOption;
+import java.nio.file.Path;
+import java.util.Properties;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.common.util.io.NoCloseInputStream;
+import org.apache.sshd.common.util.io.NoCloseReader;
+import org.apache.sshd.common.util.net.SshdSocketAddress;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ * @see <a href="https://www.freebsd.org/cgi/man.cgi?query=ssh_config&sektion=5">ssh_config(5)</a>
+ */
+public final class ConfigFileReaderSupport {
+
+    public static final char COMMENT_CHAR = '#';
+
+    public static final String COMPRESSION_PROP = "Compression";
+    public static final String DEFAULT_COMPRESSION = CompressionConfigValue.NO.getName();
+    public static final String MAX_SESSIONS_CONFIG_PROP = "MaxSessions";
+    public static final int DEFAULT_MAX_SESSIONS = 10;
+    public static final String PASSWORD_AUTH_CONFIG_PROP = "PasswordAuthentication";
+    public static final String DEFAULT_PASSWORD_AUTH = "no";
+    public static final boolean DEFAULT_PASSWORD_AUTH_VALUE = parseBooleanValue(DEFAULT_PASSWORD_AUTH);
+    public static final String LISTEN_ADDRESS_CONFIG_PROP = "ListenAddress";
+    public static final String DEFAULT_BIND_ADDRESS = SshdSocketAddress.IPV4_ANYADDR;
+    public static final String PORT_CONFIG_PROP = "Port";
+    public static final int DEFAULT_PORT = 22;
+    public static final String KEEP_ALIVE_CONFIG_PROP = "TCPKeepAlive";
+    public static final boolean DEFAULT_KEEP_ALIVE = true;
+    public static final String USE_DNS_CONFIG_PROP = "UseDNS";
+    // NOTE: the usual default is TRUE
+    public static final boolean DEFAULT_USE_DNS = true;
+    public static final String PUBKEY_AUTH_CONFIG_PROP = "PubkeyAuthentication";
+    public static final String DEFAULT_PUBKEY_AUTH = "yes";
+    public static final boolean DEFAULT_PUBKEY_AUTH_VALUE = parseBooleanValue(DEFAULT_PUBKEY_AUTH);
+    public static final String AUTH_KEYS_FILE_CONFIG_PROP = "AuthorizedKeysFile";
+    public static final String MAX_AUTH_TRIES_CONFIG_PROP = "MaxAuthTries";
+    public static final int DEFAULT_MAX_AUTH_TRIES = 6;
+    public static final String MAX_STARTUPS_CONFIG_PROP = "MaxStartups";
+    public static final int DEFAULT_MAX_STARTUPS = 10;
+    public static final String LOGIN_GRACE_TIME_CONFIG_PROP = "LoginGraceTime";
+    public static final long DEFAULT_LOGIN_GRACE_TIME = TimeUnit.SECONDS.toMillis(120);
+    public static final String KEY_REGENERATE_INTERVAL_CONFIG_PROP = "KeyRegenerationInterval";
+    public static final long DEFAULT_REKEY_TIME_LIMIT = TimeUnit.HOURS.toMillis(1L);
+    // see http://manpages.ubuntu.com/manpages/precise/en/man5/sshd_config.5.html
+    public static final String CIPHERS_CONFIG_PROP = "Ciphers";
+    public static final String DEFAULT_CIPHERS =
+            "aes128-ctr,aes192-ctr,aes256-ctr,arcfour256,arcfour128,aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc,aes192-cbc,aes256-cbc,arcfour";
+    // see http://manpages.ubuntu.com/manpages/precise/en/man5/sshd_config.5.html
+    public static final String MACS_CONFIG_PROP = "MACs";
+    public static final String DEFAULT_MACS =
+            "hmac-md5,hmac-sha1,umac-64@openssh.com,hmac-ripemd160,hmac-sha1-96,hmac-md5-96,hmac-sha2-256,hmac-sha2-256-96,hmac-sha2-512,hmac-sha2-512-96";
+    // see http://manpages.ubuntu.com/manpages/precise/en/man5/sshd_config.5.html
+    public static final String KEX_ALGORITHMS_CONFIG_PROP = "KexAlgorithms";
+    public static final String DEFAULT_KEX_ALGORITHMS =
+            "ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521"
+                    + "," + "diffie-hellman-group-exchange-sha256,diffie-hellman-group-exchange-sha1"
+                    // RFC-8268 groups
+                    + "," + "diffie-hellman-group18-sha512,diffie-hellman-group17-sha512"
+                    + "," + "diffie-hellman-group16-sha512,diffie-hellman-group15-sha512"
+                    + "," + "diffie-hellman-group14-sha256"
+                    // Legacy groups
+                    + "," + "diffie-hellman-group14-sha1,diffie-hellman-group1-sha1";
+    // see http://linux.die.net/man/5/ssh_config
+    public static final String HOST_KEY_ALGORITHMS_CONFIG_PROP = "HostKeyAlgorithms";
+    // see https://tools.ietf.org/html/rfc5656
+    public static final String DEFAULT_HOST_KEY_ALGORITHMS =
+            KeyPairProvider.SSH_RSA + "," + KeyPairProvider.SSH_DSS;
+    // see http://manpages.ubuntu.com/manpages/precise/en/man5/sshd_config.5.html
+    public static final String LOG_LEVEL_CONFIG_PROP = "LogLevel";
+    public static final LogLevelValue DEFAULT_LOG_LEVEL = LogLevelValue.INFO;
+    // see https://www.freebsd.org/cgi/man.cgi?query=sshd_config&sektion=5
+    public static final String SYSLOG_FACILITY_CONFIG_PROP = "SyslogFacility";
+    public static final SyslogFacilityValue DEFAULT_SYSLOG_FACILITY = SyslogFacilityValue.AUTH;
+    public static final String SUBSYSTEM_CONFIG_PROP = "Subsystem";
+
+    private ConfigFileReaderSupport() {
+        throw new UnsupportedOperationException("No instance");
+    }
+
+    public static Properties readConfigFile(File file) throws IOException {
+        return readConfigFile(file.toPath(), IoUtils.EMPTY_OPEN_OPTIONS);
+    }
+
+    public static Properties readConfigFile(Path path, OpenOption... options) throws IOException {
+        try (InputStream input = Files.newInputStream(path, options)) {
+            return readConfigFile(input, true);
+        }
+    }
+
+    public static Properties readConfigFile(URL url) throws IOException {
+        try (InputStream input = url.openStream()) {
+            return readConfigFile(input, true);
+        }
+    }
+
+    public static Properties readConfigFile(String path) throws IOException {
+        try (InputStream input = new FileInputStream(path)) {
+            return readConfigFile(input, true);
+        }
+    }
+
+    public static Properties readConfigFile(InputStream input, boolean okToClose) throws IOException {
+        try (Reader reader = new InputStreamReader(NoCloseInputStream.resolveInputStream(input, okToClose), StandardCharsets.UTF_8)) {
+            return readConfigFile(reader, true);
+        }
+    }
+
+    public static Properties readConfigFile(Reader reader, boolean okToClose) throws IOException {
+        try (BufferedReader buf = new BufferedReader(NoCloseReader.resolveReader(reader, okToClose))) {
+            return readConfigFile(buf);
+        }
+    }
+
+    /**
+     * Reads the configuration file contents into a {@link Properties} instance.
+     * <B>Note:</B> multiple keys value are concatenated using a comma - it is up to
+     * the caller to know which keys are expected to have multiple values and handle
+     * the split accordingly
+     *
+     * @param rdr The {@link BufferedReader} for reading the file
+     * @return The read properties
+     * @throws IOException If failed to read or malformed content
+     */
+    public static Properties readConfigFile(BufferedReader rdr) throws IOException {
+        Properties props = new Properties();
+        int lineNumber = 1;
+        for (String line = rdr.readLine(); line != null; line = rdr.readLine(), lineNumber++) {
+            line = GenericUtils.replaceWhitespaceAndTrim(line);
+            if (GenericUtils.isEmpty(line)) {
+                continue;
+            }
+
+            int pos = line.indexOf(COMMENT_CHAR);
+            if (pos == 0) {
+                continue;
+            }
+
+            if (pos > 0) {
+                line = line.substring(0, pos);
+                line = line.trim();
+            }
+
+            /*
+             * Some options use '=', others use ' ' - try both
+             * NOTE: we do not validate the format for each option separately
+             */
+            pos = line.indexOf(' ');
+            if (pos < 0) {
+                pos = line.indexOf('=');
+            }
+
+            if (pos < 0) {
+                throw new StreamCorruptedException("No delimiter at line " + lineNumber + ": " + line);
+            }
+
+            String key = line.substring(0, pos);
+            String value = line.substring(pos + 1).trim();
+            // see if need to concatenate multi-valued keys
+            String prev = props.getProperty(key);
+            if (!GenericUtils.isEmpty(prev)) {
+                value = prev + "," + value;
+            }
+
+            props.setProperty(key, value);
+        }
+
+        return props;
+    }
+
+    /**
+     * @param v Checks if the value is &quot;yes&quot;, &quot;y&quot;
+     *          or &quot;on&quot; or &quot;true&quot;.
+     * @return The result - <B>Note:</B> {@code null}/empty values are
+     * interpreted as {@code false}
+     */
+    public static boolean parseBooleanValue(String v) {
+        return "yes".equalsIgnoreCase(v)
+            || "y".equalsIgnoreCase(v)
+            || "on".equalsIgnoreCase(v)
+            || "true".equalsIgnoreCase(v);
+    }
+
+    /**
+     * Returns a &quot;yes&quot; or &quot;no&quot; value based on the input
+     * parameter
+     *
+     * @param flag The required state
+     * @return &quot;yes&quot; if {@code true}, &quot;no&quot; otherwise
+     */
+    public static String yesNoValueOf(boolean flag) {
+        return flag ? "yes" : "no";
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/FactoriesListParseResult.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/FactoriesListParseResult.java b/sshd-common/src/main/java/org/apache/sshd/common/config/FactoriesListParseResult.java
new file mode 100644
index 0000000..7a1cee1
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/FactoriesListParseResult.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config;
+
+import java.util.List;
+
+import org.apache.sshd.common.Factory;
+
+/**
+ * @param <T> Result type
+ * @param <F> Factory type
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class FactoriesListParseResult<T, F extends Factory<T>> extends ListParseResult<F> {
+    protected FactoriesListParseResult(List<F> parsed, List<String> unsupported) {
+        super(parsed, unsupported);
+    }
+
+    /**
+     * @return The {@link List} of successfully parsed {@link Factory} instances
+     * in the <U>same order</U> as they were encountered during parsing
+     */
+    public final List<F> getParsedFactories() {
+        return getParsedValues();
+    }
+
+    /**
+     * @return A {@link List} of unknown/unsupported configuration values for
+     * the factories
+     */
+    public List<String> getUnsupportedFactories() {
+        return getUnsupportedValues();
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/ListParseResult.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/ListParseResult.java b/sshd-common/src/main/java/org/apache/sshd/common/config/ListParseResult.java
new file mode 100644
index 0000000..97590cb
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/ListParseResult.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config;
+
+import java.util.List;
+
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * Used to hold the result of parsing a list of value. Such result contains known
+ * and unknown values - which are accessible via the respective {@link #getParsedValues()}
+ * and {@link #getUnsupportedValues()} methods. <B>Note:</B> the returned {@link List}s may
+ * be un-modifiable, so it is recommended to avoid attempting changing the, returned
+ * list(s)
+ *
+ * @param <E> Type of list item
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class ListParseResult<E> {
+    private final List<E> parsed;
+    private final List<String> unsupported;
+
+    protected ListParseResult(List<E> parsed, List<String> unsupported) {
+        this.parsed = parsed;
+        this.unsupported = unsupported;
+    }
+
+    /**
+     * @return The {@link List} of successfully parsed value instances
+     * in the <U>same order</U> as they were encountered during parsing
+     */
+    public final List<E> getParsedValues() {
+        return parsed;
+    }
+
+    /**
+     * @return A {@link List} of unknown/unsupported configuration values for
+     * the factories
+     */
+    public List<String> getUnsupportedValues() {
+        return unsupported;
+    }
+
+    @Override
+    public String toString() {
+        return "parsed=" + GenericUtils.join(getParsedValues(), ',')
+                + ";unsupported=" + GenericUtils.join(getUnsupportedValues(), ',');
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/LogLevelValue.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/LogLevelValue.java b/sshd-common/src/main/java/org/apache/sshd/common/config/LogLevelValue.java
new file mode 100644
index 0000000..2fbdc80
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/LogLevelValue.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config;
+
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.Set;
+
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ * @see <A HREF="http://manpages.ubuntu.com/manpages/precise/en/man5/sshd_config.5.html"><I>LogLevel</I> configuration value</A>
+ */
+public enum LogLevelValue {
+    /*
+     * NOTE(s):
+     * 1. DEBUG and DEBUG1 are EQUIVALENT
+     * 2. Order is important (!!!)
+     */
+    QUIET, FATAL, ERROR, INFO, VERBOSE, DEBUG, DEBUG1, DEBUG2, DEBUG3;
+
+    public static final Set<LogLevelValue> VALUES =
+            Collections.unmodifiableSet(EnumSet.allOf(LogLevelValue.class));
+
+    public static LogLevelValue fromName(String n) {
+        if (GenericUtils.isEmpty(n)) {
+            return null;
+        }
+
+        for (LogLevelValue l : VALUES) {
+            if (n.equalsIgnoreCase(l.name())) {
+                return l;
+            }
+        }
+
+        return null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/NamedFactoriesListParseResult.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/NamedFactoriesListParseResult.java b/sshd-common/src/main/java/org/apache/sshd/common/config/NamedFactoriesListParseResult.java
new file mode 100644
index 0000000..246cae0
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/NamedFactoriesListParseResult.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config;
+
+import java.util.List;
+
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * Holds the result of parsing a list of {@link NamedFactory}ies
+ *
+ * @param <T> Result type
+ * @param <F> Factory type
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class NamedFactoriesListParseResult<T, F extends NamedFactory<T>>
+        extends FactoriesListParseResult<T, F> {
+
+    protected NamedFactoriesListParseResult(List<F> parsed, List<String> unsupported) {
+        super(parsed, unsupported);
+    }
+
+    @Override
+    public String toString() {
+        return "parsed=" + NamedResource.getNames(getParsedFactories())
+                + ";unknown=" + GenericUtils.join(getUnsupportedFactories(), ',');
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/NamedResourceListParseResult.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/NamedResourceListParseResult.java b/sshd-common/src/main/java/org/apache/sshd/common/config/NamedResourceListParseResult.java
new file mode 100644
index 0000000..feb45f4
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/NamedResourceListParseResult.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config;
+
+import java.util.List;
+
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * @param <R> Type of result {@link NamedResource}
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class NamedResourceListParseResult<R extends NamedResource> extends ListParseResult<R> {
+    protected NamedResourceListParseResult(List<R> parsed, List<String> unsupported) {
+        super(parsed, unsupported);
+    }
+
+    /**
+     * @return The {@link List} of successfully parsed {@link NamedResource} instances
+     * in the <U>same order</U> as they were encountered during parsing
+     */
+    public final List<R> getParsedResources() {
+        return getParsedValues();
+    }
+
+    /**
+     * @return A {@link List} of unknown/unsupported configuration values for
+     * the resources
+     */
+    public List<String> getUnsupportedResources() {
+        return getUnsupportedValues();
+    }
+
+    @Override
+    public String toString() {
+        return "parsed=" + NamedResource.getNames(getParsedResources())
+                + ";unknown=" + GenericUtils.join(getUnsupportedResources(), ',');
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/SyslogFacilityValue.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/SyslogFacilityValue.java b/sshd-common/src/main/java/org/apache/sshd/common/config/SyslogFacilityValue.java
new file mode 100644
index 0000000..f52ae36
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/SyslogFacilityValue.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config;
+
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.Set;
+
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ * @see <A HREF="https://www.freebsd.org/cgi/man.cgi?query=sshd_config&sektion=5"><I>SyslogFacility</I> configuration value</A>
+ */
+public enum SyslogFacilityValue {
+    DAEMON, USER, AUTH, LOCAL0, LOCAL1, LOCAL2, LOCAL3, LOCAL4, LOCAL5, LOCAL6, LOCAL7;
+
+    public static final Set<SyslogFacilityValue> VALUES =
+            Collections.unmodifiableSet(EnumSet.allOf(SyslogFacilityValue.class));
+
+    public static SyslogFacilityValue fromName(String n) {
+        if (GenericUtils.isEmpty(n)) {
+            return null;
+        }
+
+        for (SyslogFacilityValue f : VALUES) {
+            if (n.equalsIgnoreCase(f.name())) {
+                return f;
+            }
+        }
+
+        return null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/TimeValueConfig.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/TimeValueConfig.java b/sshd-common/src/main/java/org/apache/sshd/common/config/TimeValueConfig.java
new file mode 100644
index 0000000..222cf84
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/TimeValueConfig.java
@@ -0,0 +1,180 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config;
+
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.EnumSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ * @see <A HREF="http://unixhelp.ed.ac.uk/CGI/man-cgi?sshd_config+5">Time formats for SSH configuration values</A>
+ */
+public enum TimeValueConfig {
+    SECONDS('s', 'S', TimeUnit.SECONDS.toMillis(1L)),
+    MINUTES('m', 'M', TimeUnit.MINUTES.toMillis(1L)),
+    HOURS('h', 'H', TimeUnit.HOURS.toMillis(1L)),
+    DAYS('d', 'D', TimeUnit.DAYS.toMillis(1L)),
+    WEEKS('w', 'W', TimeUnit.DAYS.toMillis(7L));
+
+    public static final Set<TimeValueConfig> VALUES =
+            Collections.unmodifiableSet(EnumSet.allOf(TimeValueConfig.class));
+
+    private final char loChar;
+    private final char hiChar;
+    private final long interval;
+
+    TimeValueConfig(char lo, char hi, long interval) {
+        loChar = lo;
+        hiChar = hi;
+        this.interval = interval;
+    }
+
+    public final char getLowerCaseValue() {
+        return loChar;
+    }
+
+    public final char getUpperCaseValue() {
+        return hiChar;
+    }
+
+    public final long getInterval() {
+        return interval;
+    }
+
+    public static TimeValueConfig fromValueChar(char ch) {
+        if ((ch <= ' ') || (ch >= 0x7F)) {
+            return null;
+        }
+
+        for (TimeValueConfig v : VALUES) {
+            if ((v.getLowerCaseValue() == ch) || (v.getUpperCaseValue() == ch)) {
+                return v;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * @param s A time specification
+     * @return The specified duration in milliseconds
+     * @see #parse(String)
+     * @see #durationOf(Map)
+     */
+    public static long durationOf(String s) {
+        Map<TimeValueConfig, Long> spec = parse(s);
+        return durationOf(spec);
+    }
+
+    /**
+     * @param s An input time specification containing possibly mixed numbers
+     *          and units - e.g., {@code 3h10m} to indicate 3 hours and 10 minutes
+     * @return A {@link Map} specifying for each time unit its count
+     * @throws NumberFormatException    If bad numbers found - e.g., negative counts
+     * @throws IllegalArgumentException If bad format - e.g., unknown unit
+     */
+    public static Map<TimeValueConfig, Long> parse(String s) throws IllegalArgumentException {
+        if (GenericUtils.isEmpty(s)) {
+            return Collections.emptyMap();
+        }
+
+        int lastPos = 0;
+        Map<TimeValueConfig, Long> spec = new EnumMap<>(TimeValueConfig.class);
+        for (int curPos = 0; curPos < s.length(); curPos++) {
+            char ch = s.charAt(curPos);
+            if ((ch >= '0') && (ch <= '9')) {
+                continue;
+            }
+
+            if (curPos <= lastPos) {
+                throw new IllegalArgumentException("parse(" + s + ") missing count value at index=" + curPos);
+            }
+
+            TimeValueConfig c = fromValueChar(ch);
+            if (c == null) {
+                throw new IllegalArgumentException("parse(" + s + ") unknown time value character: '" + ch + "'");
+            }
+
+            String v = s.substring(lastPos, curPos);
+            long count = Long.parseLong(v);
+            if (count < 0L) {
+                throw new IllegalArgumentException("parse(" + s + ") negative count (" + v + ") for " + c.name());
+            }
+
+            Long prev = spec.put(c, count);
+            if (prev != null) {
+                throw new IllegalArgumentException("parse(" + s + ") " + c.name() + " value re-specified: current=" + count + ", previous=" + prev);
+            }
+
+            lastPos = curPos + 1;
+            if (lastPos >= s.length()) {
+                break;
+            }
+        }
+
+        if (lastPos < s.length()) {
+            String v = s.substring(lastPos);
+            long count = Long.parseLong(v);
+            if (count < 0L) {
+                throw new IllegalArgumentException("parse(" + s + ") negative count (" + v + ") for last component");
+            }
+
+            Long prev = spec.put(SECONDS, count);
+            if (prev != null) {
+                throw new IllegalArgumentException("parse(" + s + ") last component (" + SECONDS.name() + ") value re-specified: current=" + count + ", previous=" + prev);
+            }
+        }
+
+        return spec;
+    }
+
+    /**
+     * @param spec The {@link Map} specifying the count for each {@link TimeValueConfig}
+     * @return The total duration in milliseconds
+     * @throws IllegalArgumentException If negative count for a time unit
+     */
+    public static long durationOf(Map<TimeValueConfig, ? extends Number> spec) throws IllegalArgumentException {
+        if (GenericUtils.isEmpty(spec)) {
+            return -1L;
+        }
+
+        long total = 0L;
+        // Cannot use forEach because the total value is not effectively final
+        for (Map.Entry<TimeValueConfig, ? extends Number> se : spec.entrySet()) {
+            TimeValueConfig v = se.getKey();
+            Number c = se.getValue();
+            long factor = c.longValue();
+            if (factor < 0L) {
+                throw new IllegalArgumentException("valueOf(" + spec + ") bad factor (" + c + ") for " + v.name());
+            }
+
+            long added = v.getInterval() * factor;
+            total += added;
+        }
+
+        return total;
+    }
+}


[29/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common utilities code from sshd-core into sshd-common (new artifact)

Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderRegistrarTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderRegistrarTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderRegistrarTest.java
new file mode 100644
index 0000000..c2ce550
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderRegistrarTest.java
@@ -0,0 +1,85 @@
+/*
+ * 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.security.eddsa;
+
+import java.security.KeyFactory;
+import java.security.KeyPairGenerator;
+import java.security.Provider;
+import java.security.Signature;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+
+import org.apache.sshd.common.util.security.SecurityProviderRegistrar;
+import org.apache.sshd.common.util.security.SecurityProviderRegistrarTestSupport;
+import org.apache.sshd.common.util.security.SecurityUtils;
+import org.junit.Assume;
+import org.junit.BeforeClass;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+import net.i2p.crypto.eddsa.EdDSASecurityProvider;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class EdDSASecurityProviderRegistrarTest extends SecurityProviderRegistrarTestSupport {
+    private static SecurityProviderRegistrar registrarInstance;
+
+    public EdDSASecurityProviderRegistrarTest() {
+        super();
+    }
+
+    @BeforeClass
+    public static void checkEDDSASupported() {
+        Assume.assumeTrue(SecurityUtils.isEDDSACurveSupported());
+        registrarInstance = new EdDSASecurityProviderRegistrar();
+    }
+
+    @Test
+    public void testSupportedSecurityEntities() {
+        assertSecurityEntitySupportState(getCurrentTestName(), registrarInstance, true, registrarInstance.getName(),
+                KeyPairGenerator.class, KeyFactory.class);
+        assertSecurityEntitySupportState(getCurrentTestName(), registrarInstance, true,
+                SecurityUtils.CURVE_ED25519_SHA512, Signature.class);
+
+        Collection<Class<?>> supported = new HashSet<>(Arrays.asList(KeyPairGenerator.class, KeyFactory.class, Signature.class));
+        for (Class<?> entity : SecurityProviderRegistrar.SECURITY_ENTITIES) {
+            if (supported.contains(entity)) {
+                continue;
+            }
+            assertFalse("Unexpected support for " + entity.getSimpleName(), registrarInstance.isSecurityEntitySupported(entity, registrarInstance.getName()));
+        }
+    }
+
+    @Test
+    public void testGetSecurityProvider() {
+        Provider expected = registrarInstance.getSecurityProvider();
+        assertNotNull("No provider created", expected);
+        assertEquals("Mismatched provider name", registrarInstance.getName(), expected.getName());
+        assertObjectInstanceOf("Mismatched provider type", EdDSASecurityProvider.class, expected);
+    }
+
+    @Test
+    public void testGetSecurityProviderCaching() {
+        testGetSecurityProviderCaching(getCurrentTestName(), registrarInstance);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/server/keyprovider/AbstractGeneratorHostKeyProviderTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/server/keyprovider/AbstractGeneratorHostKeyProviderTest.java b/sshd-common/src/test/java/org/apache/sshd/server/keyprovider/AbstractGeneratorHostKeyProviderTest.java
new file mode 100644
index 0000000..2978a18
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/server/keyprovider/AbstractGeneratorHostKeyProviderTest.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.server.keyprovider;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class AbstractGeneratorHostKeyProviderTest extends JUnitTestSupport {
+    public AbstractGeneratorHostKeyProviderTest() {
+        super();
+    }
+
+    @SuppressWarnings("synthetic-access")
+    @Test
+    public void testOverwriteKey() throws Exception {
+        Path tempDir = assertHierarchyTargetFolderExists(getTempTargetFolder());
+        Path keyPairFile = tempDir.resolve(getCurrentTestName() + ".key");
+        Files.deleteIfExists(keyPairFile);
+
+        TestProvider provider = new TestProvider(keyPairFile);
+        provider.loadKeys();
+        assertEquals("Mismatched generate write count", 1, provider.getWriteCount());
+
+        provider = new TestProvider(keyPairFile);
+        provider.setOverwriteAllowed(false);
+        provider.loadKeys();
+        assertEquals("Mismatched load write count", 0, provider.getWriteCount());
+    }
+
+    private static final class TestProvider extends AbstractGeneratorHostKeyProvider {
+        private final AtomicInteger writes = new AtomicInteger(0);
+
+        private TestProvider(Path file) {
+            setKeySize(512);
+            setPath(file);
+        }
+
+        @Override
+        protected KeyPair doReadKeyPair(String resourceKey, InputStream inputStream) throws IOException, GeneralSecurityException {
+            return null;
+        }
+
+        @Override
+        protected void doWriteKeyPair(String resourceKey, KeyPair kp, OutputStream outputStream) throws IOException, GeneralSecurityException {
+            writes.incrementAndGet();
+        }
+
+        public int getWriteCount() {
+            return writes.get();
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/server/keyprovider/PEMGeneratorHostKeyProviderTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/server/keyprovider/PEMGeneratorHostKeyProviderTest.java b/sshd-common/src/test/java/org/apache/sshd/server/keyprovider/PEMGeneratorHostKeyProviderTest.java
new file mode 100644
index 0000000..3be5b6d
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/server/keyprovider/PEMGeneratorHostKeyProviderTest.java
@@ -0,0 +1,141 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.server.keyprovider;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.KeyPair;
+import java.security.PublicKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.ECGenParameterSpec;
+
+import org.apache.sshd.common.cipher.ECCurves;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.Assume;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class PEMGeneratorHostKeyProviderTest extends JUnitTestSupport {
+    public PEMGeneratorHostKeyProviderTest() {
+        super();
+    }
+
+    @Test
+    public void testDSA() throws IOException {
+        Assume.assumeTrue("BouncyCastle not registered", SecurityUtils.isBouncyCastleRegistered());
+        testPEMGeneratorHostKeyProvider(KeyUtils.DSS_ALGORITHM, KeyPairProvider.SSH_DSS, 512, null);
+    }
+
+    @Test
+    public void testRSA() throws IOException {
+        Assume.assumeTrue("BouncyCastle not registered", SecurityUtils.isBouncyCastleRegistered());
+        testPEMGeneratorHostKeyProvider(KeyUtils.RSA_ALGORITHM, KeyPairProvider.SSH_RSA, 512, null);
+    }
+
+    @Test
+    public void testECnistp256() throws IOException {
+        Assume.assumeTrue("BouncyCastle not registered", SecurityUtils.isBouncyCastleRegistered());
+        Assume.assumeTrue("ECC not supported", SecurityUtils.isECCSupported());
+        Assume.assumeTrue(ECCurves.nistp256 + " N/A", ECCurves.nistp256.isSupported());
+        testPEMGeneratorHostKeyProvider(KeyUtils.EC_ALGORITHM, KeyPairProvider.ECDSA_SHA2_NISTP256, -1, new ECGenParameterSpec("prime256v1"));
+    }
+
+    @Test
+    public void testECnistp384() throws IOException {
+        Assume.assumeTrue("BouncyCastle not registered", SecurityUtils.isBouncyCastleRegistered());
+        Assume.assumeTrue("ECC not supported", SecurityUtils.isECCSupported());
+        Assume.assumeTrue(ECCurves.nistp384 + " N/A", ECCurves.nistp384.isSupported());
+        testPEMGeneratorHostKeyProvider(KeyUtils.EC_ALGORITHM, KeyPairProvider.ECDSA_SHA2_NISTP384, -1, new ECGenParameterSpec("P-384"));
+    }
+
+    @Test
+    public void testECnistp521() throws IOException {
+        Assume.assumeTrue("BouncyCastle not registered", SecurityUtils.isBouncyCastleRegistered());
+        Assume.assumeTrue("ECC not supported", SecurityUtils.isECCSupported());
+        Assume.assumeTrue(ECCurves.nistp521 + " N/A", ECCurves.nistp521.isSupported());
+        testPEMGeneratorHostKeyProvider(KeyUtils.EC_ALGORITHM, KeyPairProvider.ECDSA_SHA2_NISTP521, -1, new ECGenParameterSpec("P-521"));
+    }
+
+    private Path testPEMGeneratorHostKeyProvider(String algorithm, String keyType, int keySize, AlgorithmParameterSpec keySpec) throws IOException {
+        Path path = initKeyFileLocation(algorithm);
+        KeyPair kpWrite = invokePEMGeneratorHostKeyProvider(path, algorithm, keyType, keySize, keySpec);
+        assertTrue("Key file not generated: " + path, Files.exists(path, IoUtils.EMPTY_LINK_OPTIONS));
+
+        KeyPair kpRead = invokePEMGeneratorHostKeyProvider(path, algorithm, keyType, keySize, keySpec);
+        PublicKey pubWrite = kpWrite.getPublic();
+        PublicKey pubRead = kpRead.getPublic();
+        if (pubWrite instanceof ECPublicKey) {
+            // The algorithm is reported as ECDSA instead of EC
+            assertECPublicKeyEquals("Mismatched EC public key", ECPublicKey.class.cast(pubWrite), ECPublicKey.class.cast(pubRead));
+        } else {
+            assertKeyEquals("Mismatched public keys", pubWrite, pubRead);
+        }
+        return path;
+    }
+
+    private static KeyPair invokePEMGeneratorHostKeyProvider(Path path, String algorithm, String keyType, int keySize, AlgorithmParameterSpec keySpec) {
+        AbstractGeneratorHostKeyProvider provider = SecurityUtils.createGeneratorHostKeyProvider(path.toAbsolutePath().normalize());
+        provider.setAlgorithm(algorithm);
+        provider.setOverwriteAllowed(true);
+        if (keySize > 0) {
+            provider.setKeySize(keySize);
+        }
+        if (keySpec != null) {
+            provider.setKeySpec(keySpec);
+        }
+
+        return validateKeyPairProvider(provider, keyType);
+    }
+
+    private static KeyPair validateKeyPairProvider(KeyPairProvider provider, String keyType) {
+        Iterable<String> types = provider.getKeyTypes();
+        KeyPair kp = null;
+        for (String type : types) {
+            if (keyType.equals(type)) {
+                kp = provider.loadKey(keyType);
+                assertNotNull("Failed to load key for " + keyType, kp);
+                break;
+            }
+        }
+
+        assertNotNull("Expected key type not found: " + keyType, kp);
+        return kp;
+    }
+
+    private Path initKeyFileLocation(String algorithm) throws IOException {
+        Path path = assertHierarchyTargetFolderExists(getTempTargetRelativeFile(getClass().getSimpleName()));
+        path = path.resolve(algorithm + "-PEM.key");
+        Files.deleteIfExists(path);
+        return path;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProviderTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProviderTest.java b/sshd-common/src/test/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProviderTest.java
new file mode 100644
index 0000000..dc5e9fd
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProviderTest.java
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.server.keyprovider;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.KeyPair;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.ECGenParameterSpec;
+
+import org.apache.sshd.common.cipher.ECCurves;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.Assume;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * TODO Add javadoc
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class SimpleGeneratorHostKeyProviderTest extends JUnitTestSupport {
+    public SimpleGeneratorHostKeyProviderTest() {
+        super();
+    }
+
+    @Test
+    public void testDSA() throws IOException {
+        testSimpleGeneratorHostKeyProvider(KeyUtils.DSS_ALGORITHM, KeyPairProvider.SSH_DSS, 512, null);
+    }
+
+    @Test
+    public void testRSA() throws IOException {
+        testSimpleGeneratorHostKeyProvider(KeyUtils.RSA_ALGORITHM, KeyPairProvider.SSH_RSA, 512, null);
+    }
+
+    @Test
+    public void testECnistp256() throws IOException {
+        Assume.assumeTrue("BouncyCastle not registered", SecurityUtils.isBouncyCastleRegistered());
+        Assume.assumeTrue("ECC not supported", SecurityUtils.isECCSupported());
+        Assume.assumeTrue(ECCurves.nistp256 + " N/A", ECCurves.nistp256.isSupported());
+        testSimpleGeneratorHostKeyProvider(KeyUtils.EC_ALGORITHM, KeyPairProvider.ECDSA_SHA2_NISTP256, -1, new ECGenParameterSpec("prime256v1"));
+    }
+
+    @Test
+    public void testECnistp384() throws IOException {
+        Assume.assumeTrue("BouncyCastle not registered", SecurityUtils.isBouncyCastleRegistered());
+        Assume.assumeTrue("ECC not supported", SecurityUtils.isECCSupported());
+        Assume.assumeTrue(ECCurves.nistp384 + " N/A", ECCurves.nistp384.isSupported());
+        testSimpleGeneratorHostKeyProvider(KeyUtils.EC_ALGORITHM, KeyPairProvider.ECDSA_SHA2_NISTP384, -1, new ECGenParameterSpec("P-384"));
+    }
+
+    @Test
+    public void testECnistp521() throws IOException {
+        Assume.assumeTrue("BouncyCastle not registered", SecurityUtils.isBouncyCastleRegistered());
+        Assume.assumeTrue("ECC not supported", SecurityUtils.isECCSupported());
+        Assume.assumeTrue(ECCurves.nistp521 + " N/A", ECCurves.nistp521.isSupported());
+        testSimpleGeneratorHostKeyProvider(KeyUtils.EC_ALGORITHM, KeyPairProvider.ECDSA_SHA2_NISTP521, -1, new ECGenParameterSpec("P-521"));
+    }
+
+    private Path testSimpleGeneratorHostKeyProvider(String algorithm, String keyType, int keySize, AlgorithmParameterSpec keySpec) throws IOException {
+        Path path = initKeyFileLocation(algorithm);
+        KeyPair kpWrite = invokeSimpleGeneratorHostKeyProvider(path, algorithm, keyType, keySize, keySpec);
+        assertTrue("Key file not generated: " + path, Files.exists(path, IoUtils.EMPTY_LINK_OPTIONS));
+
+        KeyPair kpRead = invokeSimpleGeneratorHostKeyProvider(path, algorithm, keyType, keySize, keySpec);
+        assertKeyPairEquals("Mismatched write/read key pairs", kpWrite, kpRead);
+        return path;
+    }
+
+    private static KeyPair invokeSimpleGeneratorHostKeyProvider(Path path, String algorithm, String keyType, int keySize, AlgorithmParameterSpec keySpec) {
+        SimpleGeneratorHostKeyProvider provider = new SimpleGeneratorHostKeyProvider();
+        provider.setAlgorithm(algorithm);
+        provider.setOverwriteAllowed(true);
+        provider.setPath(path);
+        if (keySize > 0) {
+            provider.setKeySize(keySize);
+        }
+        if (keySpec != null) {
+            provider.setKeySpec(keySpec);
+        }
+
+        return validateKeyPairProvider(provider, keyType);
+    }
+
+    private static KeyPair validateKeyPairProvider(KeyPairProvider provider, String keyType) {
+        Iterable<String> types = provider.getKeyTypes();
+        KeyPair kp = null;
+        for (String type : types) {
+            if (keyType.equals(type)) {
+                kp = provider.loadKey(keyType);
+                assertNotNull("Failed to load key for " + keyType, kp);
+                break;
+            }
+        }
+
+        assertNotNull("Expected key type not found: " + keyType, kp);
+        return kp;
+    }
+
+    private Path initKeyFileLocation(String algorithm) throws IOException {
+        Path path = assertHierarchyTargetFolderExists(getTempTargetRelativeFile(getClass().getSimpleName()));
+        path = path.resolve(algorithm + "-simple.key");
+        Files.deleteIfExists(path);
+        return path;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/util/test/CommonTestSupportUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/util/test/CommonTestSupportUtils.java b/sshd-common/src/test/java/org/apache/sshd/util/test/CommonTestSupportUtils.java
new file mode 100644
index 0000000..7ae3703
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/util/test/CommonTestSupportUtils.java
@@ -0,0 +1,620 @@
+/*
+ * 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.util.test;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.Path;
+import java.security.CodeSource;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.ProtectionDomain;
+import java.security.spec.InvalidKeySpecException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.sshd.common.Factory;
+import org.apache.sshd.common.cipher.ECCurves;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.keyprovider.FileKeyPairProvider;
+import org.apache.sshd.common.keyprovider.KeyIdentityProvider;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.keyprovider.KeyPairProviderHolder;
+import org.apache.sshd.common.random.Random;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
+
+/**
+ * TODO Add javadoc
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class CommonTestSupportUtils {
+    /**
+     * URL/URI scheme that refers to a file
+     */
+    public static final String FILE_URL_SCHEME = "file";
+    /**
+     * Prefix used in URL(s) that reference a file resource
+     */
+    public static final String FILE_URL_PREFIX = FILE_URL_SCHEME + ":";
+
+    /**
+     * Separator used in URL(s) that reference a resource inside a JAR
+     * to denote the sub-path inside the JAR
+     */
+    public static final char RESOURCE_SUBPATH_SEPARATOR = '!';
+
+    /**
+     * Suffix of JAR files
+     */
+    public static final String JAR_FILE_SUFFIX = ".jar";
+
+    /**
+     * URL/URI scheme that refers to a JAR
+     */
+    public static final String JAR_URL_SCHEME = "jar";
+
+    /**
+     * Prefix used in URL(s) that reference a resource inside a JAR
+     */
+    public static final String JAR_URL_PREFIX = JAR_URL_SCHEME + ":";
+
+    /**
+     * Suffix of compile Java class files
+     */
+    public static final String CLASS_FILE_SUFFIX = ".class";
+
+    public static final List<String> TARGET_FOLDER_NAMES =    // NOTE: order is important
+        Collections.unmodifiableList(
+            Arrays.asList("target" /* Maven */, "build" /* Gradle */));
+
+    public static final String DEFAULT_TEST_HOST_KEY_PROVIDER_ALGORITHM = KeyUtils.RSA_ALGORITHM;
+    // uses a cached instance to avoid re-creating the keys as it is a time-consuming effort
+    private static final AtomicReference<KeyPairProvider> KEYPAIR_PROVIDER_HOLDER = new AtomicReference<>();
+    // uses a cached instance to avoid re-creating the keys as it is a time-consuming effort
+    private static final Map<String, FileKeyPairProvider> PROVIDERS_MAP = new ConcurrentHashMap<>();
+
+    private CommonTestSupportUtils() {
+        throw new UnsupportedOperationException("No instance allowed");
+    }
+
+    /**
+     * @param clazz A {@link Class} object
+     * @return A {@link URI} to the location of the class bytes container
+     * - e.g., the root folder, the containing JAR, etc.. Returns
+     * {@code null} if location could not be resolved
+     * @throws URISyntaxException if location is not a valid URI
+     * @see #getClassContainerLocationURL(Class)
+     */
+    public static URI getClassContainerLocationURI(Class<?> clazz) throws URISyntaxException {
+        URL url = getClassContainerLocationURL(clazz);
+        return (url == null) ? null : url.toURI();
+    }
+
+    /**
+     * @param clazz A {@link Class} object
+     * @return A {@link URL} to the location of the class bytes container
+     * - e.g., the root folder, the containing JAR, etc.. Returns
+     * {@code null} if location could not be resolved
+     */
+    public static URL getClassContainerLocationURL(Class<?> clazz) {
+        ProtectionDomain pd = clazz.getProtectionDomain();
+        CodeSource cs = (pd == null) ? null : pd.getCodeSource();
+        URL url = (cs == null) ? null : cs.getLocation();
+        if (url == null) {
+            url = getClassBytesURL(clazz);
+            if (url == null) {
+                return null;
+            }
+
+            String srcForm = getURLSource(url);
+            if (GenericUtils.isEmpty(srcForm)) {
+                return null;
+            }
+
+            try {
+                url = new URL(srcForm);
+            } catch (MalformedURLException e) {
+                throw new IllegalArgumentException("getClassContainerLocationURL(" + clazz.getName() + ")"
+                        + " Failed to create URL=" + srcForm + " from " + url.toExternalForm()
+                        + ": " + e.getMessage());
+            }
+        }
+
+        return url;
+    }
+
+    /**
+     * @param uri The {@link URI} value - ignored if {@code null}
+     * @return The URI(s) source path where {@link #JAR_URL_PREFIX} and
+     * any sub-resource are stripped
+     * @see #getURLSource(String)
+     */
+    public static String getURLSource(URI uri) {
+        return getURLSource((uri == null) ? null : uri.toString());
+    }
+
+    /**
+     * @param url The {@link URL} value - ignored if {@code null}
+     * @return The URL(s) source path where {@link #JAR_URL_PREFIX} and
+     * any sub-resource are stripped
+     * @see #getURLSource(String)
+     */
+    public static String getURLSource(URL url) {
+        return getURLSource((url == null) ? null : url.toExternalForm());
+    }
+
+    /**
+     * @param externalForm The {@link URL#toExternalForm()} string - ignored if
+     *                     {@code null}/empty
+     * @return The URL(s) source path where {@link #JAR_URL_PREFIX} and
+     * any sub-resource are stripped
+     */
+    public static String getURLSource(String externalForm) {
+        String url = externalForm;
+        if (GenericUtils.isEmpty(url)) {
+            return url;
+        }
+
+        url = stripJarURLPrefix(externalForm);
+        if (GenericUtils.isEmpty(url)) {
+            return url;
+        }
+
+        int sepPos = url.indexOf(RESOURCE_SUBPATH_SEPARATOR);
+        if (sepPos < 0) {
+            return adjustURLPathValue(url);
+        } else {
+            return adjustURLPathValue(url.substring(0, sepPos));
+        }
+    }
+
+    /**
+     * @param url A {@link URL} - ignored if {@code null}
+     * @return The path after stripping any trailing '/' provided the path
+     * is not '/' itself
+     * @see #adjustURLPathValue(String)
+     */
+    public static String adjustURLPathValue(URL url) {
+        return adjustURLPathValue((url == null) ? null : url.getPath());
+    }
+
+    /**
+     * @param path A URL path value - ignored if {@code null}/empty
+     * @return The path after stripping any trailing '/' provided the path
+     * is not '/' itself
+     */
+    public static String adjustURLPathValue(final String path) {
+        final int pathLen = (path == null) ? 0 : path.length();
+        if ((pathLen <= 1) || (path.charAt(pathLen - 1) != '/')) {
+            return path;
+        }
+
+        return path.substring(0, pathLen - 1);
+    }
+
+    public static String stripJarURLPrefix(String externalForm) {
+        String url = externalForm;
+        if (GenericUtils.isEmpty(url)) {
+            return url;
+        }
+
+        if (url.startsWith(JAR_URL_PREFIX)) {
+            return url.substring(JAR_URL_PREFIX.length());
+        }
+
+        return url;
+    }
+
+    /**
+     * @param clazz The request {@link Class}
+     * @return A {@link URL} to the location of the <code>.class</code> file
+     * - {@code null} if location could not be resolved
+     */
+    public static URL getClassBytesURL(Class<?> clazz) {
+        String className = clazz.getName();
+        int sepPos = className.indexOf('$');
+        // if this is an internal class, then need to use its parent as well
+        if (sepPos > 0) {
+            sepPos = className.lastIndexOf('.');
+            if (sepPos > 0) {
+                className = className.substring(sepPos + 1);
+            }
+        } else {
+            className = clazz.getSimpleName();
+        }
+
+        return clazz.getResource(className + CLASS_FILE_SUFFIX);
+    }
+
+    public static String getClassBytesResourceName(Class<?> clazz) {
+        return getClassBytesResourceName((clazz == null) ? null : clazz.getName());
+    }
+
+    /**
+     * @param name The fully qualified class name - ignored if {@code null}/empty
+     * @return The relative path of the class file byte-code resource
+     */
+    public static String getClassBytesResourceName(String name) {
+        if (GenericUtils.isEmpty(name)) {
+            return name;
+        } else {
+            return name.replace('.', '/') + CLASS_FILE_SUFFIX;
+        }
+    }
+
+    public static Path resolve(Path root, String... children) {
+        if (GenericUtils.isEmpty(children)) {
+            return root;
+        } else {
+            return resolve(root, Arrays.asList(children));
+        }
+    }
+
+    public static Path resolve(Path root, Collection<String> children) {
+        Path path = root;
+        if (!GenericUtils.isEmpty(children)) {
+            for (String child : children) {
+                path = path.resolve(child);
+            }
+        }
+
+        return path;
+    }
+
+    /**
+     * @param anchor An anchor {@link Class} whose container we want to use
+     * as the starting point for the &quot;target&quot; folder lookup up the
+     * hierarchy
+     * @return The &quot;target&quot; <U>folder</U> - {@code null} if not found
+     * @see #detectTargetFolder(File)
+     */
+    public static File detectTargetFolder(Class<?> anchor) {
+        return detectTargetFolder(getClassContainerLocationFile(anchor));
+    }
+
+    /**
+     * @param clazz A {@link Class} object
+     * @return A {@link File} of the location of the class bytes container
+     * - e.g., the root folder, the containing JAR, etc.. Returns
+     * {@code null} if location could not be resolved
+     * @throws IllegalArgumentException If location is not a valid {@link File} location
+     * @see #getClassContainerLocationURI(Class)
+     * @see #toFileSource(URI)
+     */
+    public static File getClassContainerLocationFile(Class<?> clazz)
+            throws IllegalArgumentException {
+        try {
+            URI uri = getClassContainerLocationURI(clazz);
+            return toFileSource(uri);
+        } catch (URISyntaxException | MalformedURLException e) {
+            throw new IllegalArgumentException(e.getClass().getSimpleName() + ": " + e.getMessage(), e);
+        }
+    }
+
+    /**
+     * Converts a {@link URL} that may refer to an internal resource to
+     * a {@link File} representing is &quot;source&quot; container (e.g.,
+     * if it is a resource in a JAR, then the result is the JAR's path)
+     *
+     * @param url The {@link URL} - ignored if {@code null}
+     * @return The matching {@link File}
+     * @throws MalformedURLException If source URL does not refer to a file location
+     * @see #toFileSource(URI)
+     */
+    public static File toFileSource(URL url) throws MalformedURLException {
+        if (url == null) {
+            return null;
+        }
+
+        try {
+            return toFileSource(url.toURI());
+        } catch (URISyntaxException e) {
+            throw new MalformedURLException("toFileSource(" + url.toExternalForm() + ")"
+                    + " cannot (" + e.getClass().getSimpleName() + ")"
+                    + " convert to URI: " + e.getMessage());
+        }
+    }
+
+    /**
+     * Converts a {@link URI} that may refer to an internal resource to
+     * a {@link File} representing is &quot;source&quot; container (e.g.,
+     * if it is a resource in a JAR, then the result is the JAR's path)
+     *
+     * @param uri The {@link URI} - ignored if {@code null}
+     * @return The matching {@link File}
+     * @throws MalformedURLException If source URI does not refer to a file location
+     * @see #getURLSource(URI)
+     */
+    public static File toFileSource(URI uri) throws MalformedURLException {
+        String src = getURLSource(uri);
+        if (GenericUtils.isEmpty(src)) {
+            return null;
+        }
+
+        if (!src.startsWith(FILE_URL_PREFIX)) {
+            throw new MalformedURLException("toFileSource(" + src + ") not a '" + FILE_URL_SCHEME + "' scheme");
+        }
+
+        try {
+            return new File(new URI(src));
+        } catch (URISyntaxException e) {
+            throw new MalformedURLException("toFileSource(" + src + ")"
+                    + " cannot (" + e.getClass().getSimpleName() + ")"
+                    + " convert to URI: " + e.getMessage());
+        }
+    }
+
+    /**
+     * @param anchorFile An anchor {@link File} we want to use
+     * as the starting point for the &quot;target&quot; or &quot;build&quot; folder
+     * lookup up the hierarchy
+     * @return The &quot;target&quot; <U>folder</U> - {@code null} if not found
+     */
+    public static File detectTargetFolder(File anchorFile) {
+        for (File file = anchorFile; file != null; file = file.getParentFile()) {
+            if (!file.isDirectory()) {
+                continue;
+            }
+
+            String name = file.getName();
+            if (TARGET_FOLDER_NAMES.contains(name)) {
+                return file;
+            }
+        }
+
+        return null;
+    }
+
+    public static KeyPair generateKeyPair(String algorithm, int keySize) throws GeneralSecurityException {
+        KeyPairGenerator gen = SecurityUtils.getKeyPairGenerator(algorithm);
+        if (KeyUtils.EC_ALGORITHM.equalsIgnoreCase(algorithm)) {
+            ECCurves curve = ECCurves.fromCurveSize(keySize);
+            if (curve == null) {
+                throw new InvalidKeySpecException("Unknown curve for key size=" + keySize);
+            }
+            gen.initialize(curve.getParameters());
+        } else {
+            gen.initialize(keySize);
+        }
+
+        return gen.generateKeyPair();
+    }
+
+    public static KeyPairProvider createTestHostKeyProvider(Class<?> anchor) {
+        KeyPairProvider provider = KEYPAIR_PROVIDER_HOLDER.get();
+        if (provider != null) {
+            return provider;
+        }
+
+        File targetFolder = Objects.requireNonNull(CommonTestSupportUtils.detectTargetFolder(anchor), "Failed to detect target folder");
+        File file = new File(targetFolder, "hostkey." + DEFAULT_TEST_HOST_KEY_PROVIDER_ALGORITHM.toLowerCase());
+        provider = createTestHostKeyProvider(file);
+
+        KeyPairProvider prev = KEYPAIR_PROVIDER_HOLDER.getAndSet(provider);
+        if (prev != null) { // check if somebody else beat us to it
+            return prev;
+        } else {
+            return provider;
+        }
+    }
+
+    public static KeyPairProvider createTestHostKeyProvider(File file) {
+        return createTestHostKeyProvider(Objects.requireNonNull(file, "No file").toPath());
+    }
+
+    public static KeyPairProvider createTestHostKeyProvider(Path path) {
+        SimpleGeneratorHostKeyProvider keyProvider = new SimpleGeneratorHostKeyProvider();
+        keyProvider.setPath(Objects.requireNonNull(path, "No path"));
+        keyProvider.setAlgorithm(DEFAULT_TEST_HOST_KEY_PROVIDER_ALGORITHM);
+        return validateKeyPairProvider(keyProvider);
+    }
+
+    public static KeyPair getFirstKeyPair(KeyPairProviderHolder holder) {
+        return getFirstKeyPair(Objects.requireNonNull(holder, "No holder").getKeyPairProvider());
+    }
+
+    public static KeyPair getFirstKeyPair(KeyIdentityProvider provider) {
+        Objects.requireNonNull(provider, "No key pair provider");
+        Iterable<? extends KeyPair> pairs = Objects.requireNonNull(provider.loadKeys(), "No loaded keys");
+        Iterator<? extends KeyPair> iter = Objects.requireNonNull(pairs.iterator(), "No keys iterator");
+        ValidateUtils.checkTrue(iter.hasNext(), "Empty loaded kyes iterator");
+        return Objects.requireNonNull(iter.next(), "No key pair in iterator");
+    }
+
+    private static File getFile(String resource) {
+        URL url = CommonTestSupportUtils.class.getClassLoader().getResource(resource);
+        try {
+            return new File(url.toURI());
+        } catch (URISyntaxException e) {
+            return new File(url.getPath());
+        }
+    }
+
+    public static File resolve(File root, String... children) {
+        if (GenericUtils.isEmpty(children)) {
+            return root;
+        } else {
+            return resolve(root, Arrays.asList(children));
+        }
+    }
+
+    public static File resolve(File root, Collection<String> children) {
+        File path = root;
+        if (!GenericUtils.isEmpty(children)) {
+            for (String child : children) {
+                path = new File(path, child);
+            }
+        }
+
+        return path;
+    }
+
+    /**
+     * Removes the specified file - if it is a directory, then its children
+     * are deleted recursively and then the directory itself. <B>Note:</B>
+     * no attempt is made to make sure that {@link File#delete()} was successful
+     *
+     * @param file The {@link File} to be deleted - ignored if {@code null}
+     *             or does not exist anymore
+     * @return The <tt>file</tt> argument
+     */
+    public static File deleteRecursive(File file) {
+        if ((file == null) || (!file.exists())) {
+            return file;
+        }
+
+        if (file.isDirectory()) {
+            File[] children = file.listFiles();
+            if (!GenericUtils.isEmpty(children)) {
+                for (File child : children) {
+                    deleteRecursive(child);
+                }
+            }
+        }
+
+        // seems that if a file is not writable it cannot be deleted
+        if (!file.canWrite()) {
+            file.setWritable(true, false);
+        }
+
+        if (!file.delete()) {
+            System.err.append("Failed to delete ").println(file.getAbsolutePath());
+        }
+
+        return file;
+    }
+
+    /**
+     * Removes the specified file - if it is a directory, then its children
+     * are deleted recursively and then the directory itself.
+     *
+     * @param path    The file {@link Path} to be deleted - ignored if {@code null}
+     *                or does not exist anymore
+     * @param options The {@link LinkOption}s to use
+     * @return The <tt>path</tt> argument
+     * @throws IOException If failed to access/remove some file(s)
+     */
+    public static Path deleteRecursive(Path path, LinkOption... options) throws IOException {
+        if ((path == null) || (!Files.exists(path))) {
+            return path;
+        }
+
+        if (Files.isDirectory(path)) {
+            try (DirectoryStream<Path> ds = Files.newDirectoryStream(path)) {
+                for (Path child : ds) {
+                    deleteRecursive(child, options);
+                }
+            }
+        }
+
+        try {
+            // seems that if a file is not writable it cannot be deleted
+            if (!Files.isWritable(path)) {
+                path.toFile().setWritable(true, false);
+            }
+            Files.delete(path);
+        } catch (IOException e) {
+            // same logic as deleteRecursive(File) which does not check if deletion succeeded
+            System.err.append("Failed (").append(e.getClass().getSimpleName()).append(")")
+                    .append(" to delete ").append(path.toString())
+                    .append(": ").println(e.getMessage());
+        }
+
+        return path;
+    }
+
+    public static String resolveRelativeRemotePath(Path root, Path file) {
+        Path relPath = root.relativize(file);
+        return relPath.toString().replace(File.separatorChar, '/');
+    }
+
+    public static FileKeyPairProvider createTestKeyPairProvider(String resource) {
+        File file = getFile(resource);
+        String filePath = file.getAbsolutePath();
+        FileKeyPairProvider provider = PROVIDERS_MAP.get(filePath);
+        if (provider != null) {
+            return provider;
+        }
+
+        provider = new FileKeyPairProvider();
+        provider.setFiles(Collections.singletonList(file));
+        provider = validateKeyPairProvider(provider);
+
+        FileKeyPairProvider prev = PROVIDERS_MAP.put(filePath, provider);
+        if (prev != null) { // check if somebody else beat us to it
+            return prev;
+        } else {
+            return provider;
+        }
+    }
+
+    private static <P extends KeyIdentityProvider> P validateKeyPairProvider(P provider) {
+        Objects.requireNonNull(provider, "No provider");
+
+        // get the I/O out of the way
+        Iterable<KeyPair> keys = Objects.requireNonNull(provider.loadKeys(), "No keys loaded");
+        if (keys instanceof Collection<?>) {
+            ValidateUtils.checkNotNullAndNotEmpty((Collection<?>) keys, "Empty keys loaded");
+        }
+
+        return provider;
+    }
+
+    public static Random getRandomizerInstance() {
+        Factory<Random> factory = SecurityUtils.getRandomFactory();
+        return factory.create();
+    }
+
+    /**
+     * @param path The {@link Path} to write the data to
+     * @param data The data to write (as UTF-8)
+     * @return The UTF-8 data bytes
+     * @throws IOException If failed to write
+     */
+    public static byte[] writeFile(Path path, String data) throws IOException {
+        try (OutputStream fos = Files.newOutputStream(path, IoUtils.EMPTY_OPEN_OPTIONS)) {
+            byte[] bytes = data.getBytes(StandardCharsets.UTF_8);
+            fos.write(bytes);
+            return bytes;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/util/test/JUnit4ClassRunnerWithParameters.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/util/test/JUnit4ClassRunnerWithParameters.java b/sshd-common/src/test/java/org/apache/sshd/util/test/JUnit4ClassRunnerWithParameters.java
new file mode 100644
index 0000000..acf44a5
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/util/test/JUnit4ClassRunnerWithParameters.java
@@ -0,0 +1,48 @@
+/*
+ * 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.util.test;
+
+import org.junit.runners.model.InitializationError;
+import org.junit.runners.parameterized.BlockJUnit4ClassRunnerWithParameters;
+import org.junit.runners.parameterized.TestWithParameters;
+
+/**
+ * Uses a cached created instance instead of a new one on every call of {@code #createTest()}
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class JUnit4ClassRunnerWithParameters extends BlockJUnit4ClassRunnerWithParameters {
+    private volatile Object testInstance;
+
+    public JUnit4ClassRunnerWithParameters(TestWithParameters test) throws InitializationError {
+        super(test);
+    }
+
+    @Override
+    public Object createTest() throws Exception {
+        synchronized (this) {
+            if (testInstance == null) {
+                testInstance = super.createTest();
+            }
+        }
+
+        return testInstance;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/util/test/JUnit4ClassRunnerWithParametersFactory.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/util/test/JUnit4ClassRunnerWithParametersFactory.java b/sshd-common/src/test/java/org/apache/sshd/util/test/JUnit4ClassRunnerWithParametersFactory.java
new file mode 100644
index 0000000..12ab252
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/util/test/JUnit4ClassRunnerWithParametersFactory.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.util.test;
+
+import org.junit.runner.Runner;
+import org.junit.runners.model.InitializationError;
+import org.junit.runners.parameterized.ParametersRunnerFactory;
+import org.junit.runners.parameterized.TestWithParameters;
+
+/**
+ * Avoids re-creating a test class instance for each parameterized test method. Usage:
+ *
+ * <PRE><code>
+ * @FixMethodOrder(MethodSorters.NAME_ASCENDING)
+ * @RunWith(Parameterized.class)
+ * @UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
+ * public class MyParameterizedTest {
+ *      public MyParameterizedTest(...params...) {
+ *          ....
+ *      }
+ *
+ *      @Parameters(...)
+ *      public static List<Object[]> parameters() {
+ *          ...
+ *      }
+ * }
+ * </code></PRE>
+ *
+ * @see JUnit4ClassRunnerWithParameters
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class JUnit4ClassRunnerWithParametersFactory implements ParametersRunnerFactory {
+    public JUnit4ClassRunnerWithParametersFactory() {
+        super();
+    }
+
+    @Override
+    public Runner createRunnerForTestWithParameters(TestWithParameters test) throws InitializationError {
+        return new JUnit4ClassRunnerWithParameters(test);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/util/test/JUnit4SingleInstanceClassRunner.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/util/test/JUnit4SingleInstanceClassRunner.java b/sshd-common/src/test/java/org/apache/sshd/util/test/JUnit4SingleInstanceClassRunner.java
new file mode 100644
index 0000000..a8f11ce
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/util/test/JUnit4SingleInstanceClassRunner.java
@@ -0,0 +1,54 @@
+/*
+ * 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.util.test;
+
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.junit.runners.BlockJUnit4ClassRunner;
+import org.junit.runners.model.InitializationError;
+import org.junit.runners.model.TestClass;
+
+/**
+ * @see <A HREF="https://issues.apache.org/jira/browse/SSHD-764">SSHD-764</A>
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class JUnit4SingleInstanceClassRunner extends BlockJUnit4ClassRunner {
+    private final AtomicReference<Map.Entry<Class<?>, ?>> testHolder = new AtomicReference<>();
+
+    public JUnit4SingleInstanceClassRunner(Class<?> klass) throws InitializationError {
+        super(klass);
+    }
+
+    @Override
+    protected Object createTest() throws Exception {
+        Map.Entry<Class<?>, ?> lastTest = testHolder.get();
+        Class<?> lastTestClass = (lastTest == null) ? null : lastTest.getKey();
+        TestClass curTest = getTestClass();
+        Class<?> curTestClass = curTest.getJavaClass();
+        if (curTestClass == lastTestClass) {
+            return lastTest.getValue();
+        }
+
+        Object instance = super.createTest();
+        testHolder.set(new SimpleImmutableEntry<>(curTestClass, instance));
+        return instance;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/util/test/JUnitTestSupport.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/util/test/JUnitTestSupport.java b/sshd-common/src/test/java/org/apache/sshd/util/test/JUnitTestSupport.java
new file mode 100644
index 0000000..a854699
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/util/test/JUnitTestSupport.java
@@ -0,0 +1,572 @@
+/*
+ * 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.util.test;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.security.Key;
+import java.security.KeyPair;
+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.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.ECField;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPoint;
+import java.security.spec.EllipticCurve;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.io.IoUtils;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.junit.runner.RunWith;
+
+/**
+ * TODO Add javadoc
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@RunWith(JUnit4SingleInstanceClassRunner.class)
+public abstract class JUnitTestSupport extends Assert {
+    public static final String TEMP_SUBFOLDER_NAME = "temp";
+    public static final boolean OUTPUT_DEBUG_MESSAGES =
+        Boolean.parseBoolean(System.getProperty("org.apache.sshd.test.outputDebugMessages", "false"));
+    public static final String MAIN_SUBFOLDER = "main";
+    public static final String TEST_SUBFOLDER = "test";
+    public static final String RESOURCES_SUBFOLDER = "resources";
+
+    // useful test sizes for keys
+    public static final List<Integer> DSS_SIZES =
+            Collections.unmodifiableList(Arrays.asList(512, 768, 1024));
+    public static final List<Integer> RSA_SIZES =
+            Collections.unmodifiableList(Arrays.asList(1024, 2048, 3072, 4096));
+    public static final List<Integer> ED25519_SIZES =
+            Collections.unmodifiableList(Arrays.asList(256));
+
+    @Rule
+    public final TestName testNameHolder = new TestName();
+
+    private Path targetFolder;
+    private Path tempFolder;
+
+    protected JUnitTestSupport() {
+        super();
+    }
+
+    public final String getCurrentTestName() {
+        return testNameHolder.getMethodName();
+    }
+
+    /**
+     * Attempts to build a <U>relative</U> path whose root is the location
+     * of the TEMP sub-folder of the Maven &quot;target&quot; folder associated
+     * with the project
+     *
+     * @param comps The path components - ignored if {@code null}/empty
+     * @return The {@link Path} representing the result - same as target folder if no components
+     * @see #TEMP_SUBFOLDER_NAME
+     * @see #getTargetRelativeFile(Collection)
+     */
+    protected Path getTempTargetRelativeFile(String... comps) {
+        return getTempTargetRelativeFile(GenericUtils.isEmpty(comps) ? Collections.emptyList() : Arrays.asList(comps));
+    }
+
+    /**
+     * Attempts to build a <U>relative</U> path whose root is the location
+     * of the TEMP sub-folder of the Maven &quot;target&quot; folder associated
+     * with the project
+     *
+     * @param comps The path components - ignored if {@code null}/empty
+     * @return The {@link Path} representing the result - same as target folder if no components
+     * @see #TEMP_SUBFOLDER_NAME
+     * @see #getTempTargetFolder()
+     */
+    protected Path getTempTargetRelativeFile(Collection<String> comps) {
+        return CommonTestSupportUtils.resolve(getTempTargetFolder(), comps);
+    }
+
+    /**
+     * @return The TEMP sub-folder {@link Path} of the Maven &quot;target&quot; folder
+     * associated with the project - never {@code null}
+     */
+    protected Path getTempTargetFolder() {
+        synchronized (TEMP_SUBFOLDER_NAME) {
+            if (tempFolder == null) {
+                tempFolder = Objects.requireNonNull(detectTargetFolder(), "No target folder detected").resolve(TEMP_SUBFOLDER_NAME);
+            }
+        }
+
+        return tempFolder;
+    }
+
+    /**
+     * Attempts to build a <U>relative</U> path whose root is the location
+     * of the Maven &quot;target&quot; folder associated with the project
+     *
+     * @param comps The path components - ignored if {@code null}/empty
+     * @return The {@link Path} representing the result - same as target folder if no components
+     */
+    protected Path getTargetRelativeFile(String... comps) {
+        return getTargetRelativeFile(GenericUtils.isEmpty(comps) ? Collections.emptyList() : Arrays.asList(comps));
+    }
+
+    /**
+     * Attempts to build a <U>relative</U> path whose root is the location
+     * of the Maven &quot;target&quot; folder associated with the project
+     *
+     * @param comps The path components - ignored if {@code null}/empty
+     * @return The {@link Path} representing the result - same as target folder if no components
+     * @see #detectTargetFolder()
+     */
+    protected Path getTargetRelativeFile(Collection<String> comps) {
+        return CommonTestSupportUtils.resolve(detectTargetFolder(), comps);
+    }
+
+    /**
+     * Attempts to detect the location of the Maven &quot;target&quot; folder
+     * associated with the project that contains the actual class extending this
+     * base class
+     *
+     * @return The {@link File} representing the location of the &quot;target&quot; folder
+     * @throws IllegalArgumentException If failed to detect the folder
+     */
+    protected Path detectTargetFolder() throws IllegalArgumentException {
+        synchronized (TEMP_SUBFOLDER_NAME) {
+            if (targetFolder == null) {
+                File path = CommonTestSupportUtils.detectTargetFolder(getClass());
+                targetFolder = Objects.requireNonNull(path, "Failed to detect target folder").toPath();
+            }
+        }
+
+        return targetFolder;
+    }
+
+    /**
+     * Creates a folder bearing the class's simple name under the project's target temporary folder
+     *
+     * @return The created folder {@link Path}
+     * @throws IOException If failed to detect or create the folder's location
+     * @see #detectTargetFolder() detectTargetFolder
+     * @see #assertHierarchyTargetFolderExists(Path, LinkOption...) assertHierarchyTargetFolderExists
+     */
+    protected Path createTempClassFolder() throws IOException {
+        Path tmpDir = detectTargetFolder();
+        return assertHierarchyTargetFolderExists(tmpDir.resolve(getClass().getSimpleName()));
+    }
+
+    protected Path detectSourcesFolder() throws IllegalStateException {
+        Path target = detectTargetFolder();
+        Path parent = target.getParent();
+        return parent.resolve("src");
+    }
+
+    protected Path getTestResourcesFolder() {
+        try {
+            URL url = getClass().getResource(getClass().getSimpleName() + ".class");
+            return Paths.get(url.toURI()).getParent();
+        } catch (URISyntaxException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    protected Path getClassResourcesFolder(String resType /* test or main */) {
+        return getClassResourcesFolder(resType, getClass());
+    }
+
+    protected Path getClassResourcesFolder(String resType /* test or main */, Class<?> clazz) {
+        return getPackageResourcesFolder(resType, clazz.getPackage());
+    }
+
+    protected Path getPackageResourcesFolder(String resType /* test or main */, Package pkg) {
+        return getPackageResourcesFolder(resType, pkg.getName());
+    }
+
+    protected Path getPackageResourcesFolder(String resType /* test or main */, String pkgName) {
+        Path src = detectSourcesFolder();
+        Path root = src.resolve(resType);
+        Path resources = root.resolve(RESOURCES_SUBFOLDER);
+        return resources.resolve(pkgName.replace('.', File.separatorChar));
+    }
+
+    protected KeyPairProvider createTestHostKeyProvider() {
+        return CommonTestSupportUtils.createTestHostKeyProvider(getClass());
+    }
+
+    /* ------------------- Useful extra test helpers ---------------------- */
+
+    public static String shuffleCase(CharSequence cs) {
+        if (GenericUtils.isEmpty(cs)) {
+            return "";
+        }
+
+        StringBuilder sb = new StringBuilder(cs.length());
+        for (int index = 0; index < cs.length(); index++) {
+            char ch = cs.charAt(index);
+            double v = Math.random();
+            if (Double.compare(v, 0.3d) < 0) {
+                ch = Character.toUpperCase(ch);
+            } else if ((Double.compare(v, 0.3d) >= 0) && (Double.compare(v, 0.6d) < 0)) {
+                ch = Character.toLowerCase(ch);
+            }
+            sb.append(ch);
+        }
+
+        return sb.toString();
+    }
+
+    public static String repeat(CharSequence csq, int nTimes) {
+        if (GenericUtils.isEmpty(csq) || (nTimes <= 0)) {
+            return "";
+        }
+
+        StringBuilder sb = new StringBuilder(nTimes * csq.length());
+        for (int index = 0; index < nTimes; index++) {
+            sb.append(csq);
+        }
+
+        return sb.toString();
+    }
+
+    public static List<Object[]> parameterize(Collection<?> params) {
+        if (GenericUtils.isEmpty(params)) {
+            return Collections.emptyList();
+        }
+
+        List<Object[]> result = new ArrayList<>(params.size());
+        for (Object p : params) {
+            result.add(new Object[]{p});
+        }
+
+        return result;
+    }
+
+    /* ----------------------- Useful extra assertions --------------------- */
+
+    public static void assertEquals(String message, boolean expected, boolean actual) {
+        assertEquals(message, Boolean.valueOf(expected), Boolean.valueOf(actual));
+    }
+
+    public static <T> void assertEquals(String message, Iterable<? extends T> expected, Iterable<? extends T> actual) {
+        if (expected != actual) {
+            assertEquals(message, expected.iterator(), actual.iterator());
+        }
+    }
+
+    public static <T> void assertEquals(String message, Iterator<? extends T> expected, Iterator<? extends T> actual) {
+        if (expected == actual) {
+            return;
+        }
+
+        for (int index = 0; expected.hasNext(); index++) {
+            assertTrue(message + "[next actual index=" + index + "]", actual.hasNext());
+
+            T expValue = expected.next();
+            T actValue = actual.next();
+            assertEquals(message + "[iterator index=" + index + "]", expValue, actValue);
+        }
+
+        // once expected is exhausted make sure no more actual items left
+        assertFalse(message + "[non-empty-actual]", actual.hasNext());
+    }
+
+    public static Path assertHierarchyTargetFolderExists(Path folder, LinkOption... options) throws IOException {
+        if (Files.exists(folder, options)) {
+            assertTrue("Target is an existing file instead of a folder: " + folder, Files.isDirectory(folder, options));
+        } else {
+            Files.createDirectories(folder);
+        }
+
+        return folder;
+    }
+
+    public static void assertFileContentsEquals(String prefix, Path expected, Path actual) throws IOException {
+        long cmpSize = Files.size(expected);
+        assertEquals(prefix + ": Mismatched file size", cmpSize, Files.size(expected));
+
+        try (InputStream expStream = Files.newInputStream(expected);
+             InputStream actStream = Files.newInputStream(actual)) {
+            byte[] expData = new byte[IoUtils.DEFAULT_COPY_SIZE];
+            byte[] actData = new byte[expData.length];
+
+            for (long offset = 0L; offset < cmpSize;) {
+                Arrays.fill(expData, (byte) 0);
+                int expLen = expStream.read(expData);
+                Arrays.fill(actData, (byte) 0);
+                int actLen = actStream.read(actData);
+                assertEquals(prefix + ": Mismatched read size at offset=" + offset, expLen, actLen);
+                assertArrayEquals(prefix + ": Mismatched data at offset=" + offset, expData, actData);
+
+                offset += expLen;
+            }
+        }
+    }
+
+    public static File assertHierarchyTargetFolderExists(File folder) {
+        if (folder.exists()) {
+            assertTrue("Target is an existing file instead of a folder: " + folder.getAbsolutePath(), folder.isDirectory());
+        } else {
+            assertTrue("Failed to create hierarchy of " + folder.getAbsolutePath(), folder.mkdirs());
+        }
+
+        return folder;
+    }
+
+    public static void assertObjectInstanceOf(String message, Class<?> expected, Object obj) {
+        assertNotNull(message + " - no actual object", obj);
+
+        Class<?> actual = obj.getClass();
+        if (!expected.isAssignableFrom(actual)) {
+            fail(message + " - actual object type (" + actual.getName() + ") incompatible with expected (" + expected.getName() + ")");
+        }
+    }
+
+    public static <E> void assertListEquals(String message, List<? extends E> expected, List<? extends E> actual) {
+        int expSize = GenericUtils.size(expected);
+        int actSize = GenericUtils.size(actual);
+        assertEquals(message + "[size]", expSize, actSize);
+
+        for (int index = 0; index < expSize; index++) {
+            E expValue = expected.get(index);
+            E actValue = actual.get(index);
+            assertEquals(message + "[" + index + "]", expValue, actValue);
+        }
+    }
+
+    public static <K, V> void assertMapEquals(String message, Map<? extends K, ? extends V> expected, Map<? super K, ? extends V> actual) {
+        int numItems = GenericUtils.size(expected);
+        assertEquals(message + "[size]", numItems, GenericUtils.size(actual));
+
+        if (numItems > 0) {
+            expected.forEach((key, expValue) -> {
+                V actValue = actual.get(key);
+                assertEquals(message + "[" + key + "]", expValue, actValue);
+            });
+        }
+    }
+
+    public static void assertKeyPairEquals(String message, KeyPair expected, KeyPair actual) {
+        assertKeyEquals(message + "[public]", expected.getPublic(), actual.getPublic());
+        assertKeyEquals(message + "[private]", expected.getPrivate(), actual.getPrivate());
+    }
+
+    public static <T extends Key> void assertKeyEquals(String message, T expected, T actual) {
+        if (expected == actual) {
+            return;
+        }
+
+        assertEquals(message + "[algorithm]", expected.getAlgorithm(), actual.getAlgorithm());
+
+        if (expected instanceof RSAPublicKey) {
+            assertRSAPublicKeyEquals(message, RSAPublicKey.class.cast(expected), RSAPublicKey.class.cast(actual));
+        } else if (expected instanceof DSAPublicKey) {
+            assertDSAPublicKeyEquals(message, DSAPublicKey.class.cast(expected), DSAPublicKey.class.cast(actual));
+        } else if (expected instanceof ECPublicKey) {
+            assertECPublicKeyEquals(message, ECPublicKey.class.cast(expected), ECPublicKey.class.cast(actual));
+        } else if (expected instanceof RSAPrivateKey) {
+            assertRSAPrivateKeyEquals(message, RSAPrivateKey.class.cast(expected), RSAPrivateKey.class.cast(actual));
+        } else if (expected instanceof ECPrivateKey) {
+            assertECPrivateKeyEquals(message, ECPrivateKey.class.cast(expected), ECPrivateKey.class.cast(actual));
+        }
+        assertArrayEquals(message + "[encdoded-data]", expected.getEncoded(), actual.getEncoded());
+    }
+
+    public static void assertRSAPublicKeyEquals(String message, RSAPublicKey expected, RSAPublicKey actual) {
+        if (expected == actual) {
+            return;
+        }
+
+        assertEquals(message + "[e]", expected.getPublicExponent(), actual.getPublicExponent());
+        assertEquals(message + "[n]", expected.getModulus(), actual.getModulus());
+    }
+
+    public static void assertDSAPublicKeyEquals(String message, DSAPublicKey expected, DSAPublicKey actual) {
+        if (expected == actual) {
+            return;
+        }
+
+        assertEquals(message + "[y]", expected.getY(), actual.getY());
+        assertDSAParamsEquals(message + "[params]", expected.getParams(), actual.getParams());
+    }
+
+    public static void assertECPublicKeyEquals(String message, ECPublicKey expected, ECPublicKey actual) {
+        if (expected == actual) {
+            return;
+        }
+
+        assertECPointEquals(message + "[W]", expected.getW(), actual.getW());
+        assertECParameterSpecEquals(message, expected, actual);
+    }
+
+    public static void assertRSAPrivateKeyEquals(String message, RSAPrivateKey expected, RSAPrivateKey actual) {
+        if (expected == actual) {
+            return;
+        }
+
+        assertEquals(message + "[d]", expected.getPrivateExponent(), actual.getPrivateExponent());
+        assertEquals(message + "[n]", expected.getModulus(), actual.getModulus());
+    }
+
+    public static void assertDSAPrivateKeyEquals(String message, DSAPrivateKey expected, DSAPrivateKey actual) {
+        if (expected == actual) {
+            return;
+        }
+
+        assertEquals(message + "[x]", expected.getX(), actual.getX());
+        assertDSAParamsEquals(message + "[params]", expected.getParams(), actual.getParams());
+    }
+
+    public static void assertDSAParamsEquals(String message, DSAParams expected, DSAParams actual) {
+        if (expected == actual) {
+            return;
+        }
+
+        assertEquals(message + "[g]", expected.getG(), actual.getG());
+        assertEquals(message + "[p]", expected.getP(), actual.getP());
+        assertEquals(message + "[q]", expected.getQ(), actual.getQ());
+    }
+
+    public static void assertECPrivateKeyEquals(String message, ECPrivateKey expected, ECPrivateKey actual) {
+        if (expected == actual) {
+            return;
+        }
+
+        assertEquals(message + "[S]", expected.getS(), actual.getS());
+        assertECParameterSpecEquals(message, expected, actual);
+    }
+
+    public static void assertECParameterSpecEquals(String message, ECKey expected, ECKey actual) {
+        if (expected == actual) {
+            return;
+        }
+        assertECParameterSpecEquals(message, expected.getParams(), actual.getParams());
+    }
+
+    public static void assertECParameterSpecEquals(String message, ECParameterSpec expected, ECParameterSpec actual) {
+        if (expected == actual) {
+            return;
+        }
+
+        assertEquals(message + "[order]", expected.getOrder(), actual.getOrder());
+        assertEquals(message + "[cofactor]", expected.getCofactor(), actual.getCofactor());
+        assertECPointEquals(message + "[generator]", expected.getGenerator(), actual.getGenerator());
+        assertCurveEquals(message + "[curve]", expected.getCurve(), actual.getCurve());
+    }
+
+    public static void assertCurveEquals(String message, EllipticCurve expected, EllipticCurve actual) {
+        if (expected == actual) {
+            return;
+        }
+
+        assertEquals(message + "[A]", expected.getA(), actual.getA());
+        assertEquals(message + "[B]", expected.getB(), actual.getB());
+        assertArrayEquals(message + "[seed]", expected.getSeed(), actual.getSeed());
+        assertECFieldEquals(message + "[field]", expected.getField(), actual.getField());
+    }
+
+    public static void assertECFieldEquals(String message, ECField expected, ECField actual) {
+        if (expected == actual) {
+            return;
+        }
+
+        assertEquals(message + "[size]", expected.getFieldSize(), actual.getFieldSize());
+    }
+
+    public static void assertECPointEquals(String message, ECPoint expected, ECPoint actual) {
+        if (expected == actual) {
+            return;
+        }
+
+        assertEquals(message + "[x]", expected.getAffineX(), actual.getAffineX());
+        assertEquals(message + "[y]", expected.getAffineY(), actual.getAffineY());
+    }
+
+    public static void assertFileLength(File file, long length, long timeout) throws Exception {
+        assertFileLength(file.toPath(), length, timeout);
+    }
+
+    /**
+     * Waits the specified timeout for the file to exist and have the required length
+     *
+     * @param file    The file {@link Path} to check
+     * @param length  Expected length
+     * @param timeout Timeout (msec.) to wait for satisfying the requirements
+     * @throws Exception If failed to access the file
+     */
+    public static void assertFileLength(Path file, long length, long timeout) throws Exception {
+        if (waitForFile(file, length, timeout)) {
+            return;
+        }
+        assertTrue("File not found: " + file, Files.exists(file));
+        assertEquals("Mismatched file size for " + file, length, Files.size(file));
+    }
+
+    public static boolean waitForFile(Path file, long length, long timeout) throws Exception {
+        while (timeout > 0L) {
+            long sleepTime = Math.min(timeout, 100L);
+            if (Files.exists(file) && (Files.size(file) == length)) {
+                return true;
+            }
+
+            long sleepStart = System.nanoTime();
+            Thread.sleep(sleepTime);
+            long sleepEnd = System.nanoTime();
+            long nanoSleep = sleepEnd - sleepStart;
+
+            sleepTime = TimeUnit.NANOSECONDS.toMillis(nanoSleep);
+            timeout -= sleepTime;
+        }
+
+        return false;
+    }
+
+    public static void outputDebugMessage(String format, Object... args) {
+        if (OUTPUT_DEBUG_MESSAGES) {
+            outputDebugMessage(String.format(format, args));
+        }
+    }
+
+    public static void outputDebugMessage(Object message) {
+        if (OUTPUT_DEBUG_MESSAGES) {
+            System.out.append("===[DEBUG]=== ").println(message);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/util/test/NoIoTestCase.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/util/test/NoIoTestCase.java b/sshd-common/src/test/java/org/apache/sshd/util/test/NoIoTestCase.java
new file mode 100644
index 0000000..7d52892
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/util/test/NoIoTestCase.java
@@ -0,0 +1,30 @@
+/*
+ * 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.util.test;
+
+/**
+ * Marker interface used as <A HREF="https://github.com/junit-team/junit4/wiki/categories">jUnit category</A>
+ * to indicate a test that does not require real client/server interaction.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface NoIoTestCase {
+    // Marker interface
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/util/test/OutputCountTrackingOutputStream.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/util/test/OutputCountTrackingOutputStream.java b/sshd-common/src/test/java/org/apache/sshd/util/test/OutputCountTrackingOutputStream.java
new file mode 100644
index 0000000..b4eb84c
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/util/test/OutputCountTrackingOutputStream.java
@@ -0,0 +1,56 @@
+/*
+ * 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.util.test;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class OutputCountTrackingOutputStream extends FilterOutputStream {
+    protected long writeCount;
+
+    public OutputCountTrackingOutputStream(OutputStream out) {
+        super(out);
+    }
+
+    @Override
+    public void write(int b) throws IOException {
+        out.write(b);
+        updateWriteCount(1L);
+    }
+
+    @Override
+    public void write(byte[] b, int off, int len) throws IOException {
+        out.write(b, off, len); // don't call super since it calls the single 'write'
+        updateWriteCount(len);
+    }
+
+    public long getWriteCount() {
+        return writeCount;
+    }
+
+    protected long updateWriteCount(long delta) {
+        writeCount += delta;
+        return writeCount;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/util/test/TeeOutputStream.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/util/test/TeeOutputStream.java b/sshd-common/src/test/java/org/apache/sshd/util/test/TeeOutputStream.java
new file mode 100644
index 0000000..4cd6014
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/util/test/TeeOutputStream.java
@@ -0,0 +1,64 @@
+/*
+ * 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.util.test;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * TODO Add javadoc
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class TeeOutputStream extends OutputStream {
+
+    private OutputStream[] tees;
+
+    public TeeOutputStream(OutputStream... tees) {
+        this.tees = tees;
+    }
+
+    @Override
+    public void write(int b) throws IOException {
+        for (OutputStream s : tees) {
+            s.write(b);
+        }
+    }
+
+    @Override
+    public void write(byte[] b, int off, int len) throws IOException {
+        for (OutputStream s : tees) {
+            s.write(b, off, len);
+        }
+    }
+
+    @Override
+    public void flush() throws IOException {
+        for (OutputStream s : tees) {
+            s.flush();
+        }
+    }
+
+    @Override
+    public void close() throws IOException {
+        for (OutputStream s : tees) {
+            s.close();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/log4j.properties
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/log4j.properties b/sshd-common/src/test/resources/log4j.properties
new file mode 100644
index 0000000..cf1d08a
--- /dev/null
+++ b/sshd-common/src/test/resources/log4j.properties
@@ -0,0 +1,38 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed  under the  License is distributed on an "AS IS" BASIS,
+# WITHOUT  WARRANTIES OR CONDITIONS  OF ANY KIND, either  express  or
+# implied.
+#
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#
+
+#
+# The logging properties used during tests..
+#
+log4j.rootLogger=INFO, stdout, logfile
+#log4j.logger.org.apache.sshd=TRACE
+#log4j.logger.org.apache.sshd.common.channel.Window=DEBUG
+
+# CONSOLE appender
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d | %-5.5p | %-16.16t | %-32.32c{1} | %-64.64C %4L | %m%n
+
+# File appender
+log4j.appender.logfile=org.apache.log4j.FileAppender
+log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
+log4j.appender.logfile.layout.ConversionPattern=%d | %-5.5p | %-16.16t | %-32.32c{1} | %-64.64C %4L | %m%n
+log4j.appender.logfile.file=target/sshd-common-tests.log
+log4j.appender.logfile.append=true

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/client/config/hosts/testReadGlobalHostsConfigEntries.config.txt
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/client/config/hosts/testReadGlobalHostsConfigEntries.config.txt b/sshd-common/src/test/resources/org/apache/sshd/client/config/hosts/testReadGlobalHostsConfigEntries.config.txt
new file mode 100644
index 0000000..5f772cd
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/client/config/hosts/testReadGlobalHostsConfigEntries.config.txt
@@ -0,0 +1,22 @@
+# Global section first
+
+    User global
+    Port 7365
+    IdentityFile ~/.ssh/github.key
+    HostName 37.77.34.7
+
+# User override
+Host *.apache.org
+    User mina-sshd
+
+# Port override
+Host *.github.com
+    Port 443
+
+# Host name override
+Host 10.*.*.*
+    HostName 7.3.6.5
+
+# Identity override
+Host 192.168.*.*
+    IdentityFile ~/.ssh/internal.key

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/client/config/hosts/testReadMultipleHostPatterns.config.txt
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/client/config/hosts/testReadMultipleHostPatterns.config.txt b/sshd-common/src/test/resources/org/apache/sshd/client/config/hosts/testReadMultipleHostPatterns.config.txt
new file mode 100644
index 0000000..0f5edcb
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/client/config/hosts/testReadMultipleHostPatterns.config.txt
@@ -0,0 +1,5 @@
+# Same configuration duplicated in multiple configuration entries
+Host *.github.com *.apache.org 10.*.*.*
+    User mina-sshd
+    Port 443
+    HostName 7.3.6.5

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/client/config/hosts/testReadSimpleHostsConfigEntries.config.txt
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/client/config/hosts/testReadSimpleHostsConfigEntries.config.txt b/sshd-common/src/test/resources/org/apache/sshd/client/config/hosts/testReadSimpleHostsConfigEntries.config.txt
new file mode 100644
index 0000000..1058777
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/client/config/hosts/testReadSimpleHostsConfigEntries.config.txt
@@ -0,0 +1,8 @@
+# Simple configuration file
+Host *.github.com
+    User mina-sshd
+    Port 443
+    IdentityFile ~/.ssh/github.key
+
+Host *.apache.org
+    User mina-sshd

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/client/config/keys/id_dsa
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/client/config/keys/id_dsa b/sshd-common/src/test/resources/org/apache/sshd/client/config/keys/id_dsa
new file mode 100644
index 0000000..1d3ef24
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/client/config/keys/id_dsa
@@ -0,0 +1,12 @@
+-----BEGIN DSA PRIVATE KEY-----
+MIIBvAIBAAKBgQDIPyMbBuQcZxeYDOyCqqkdK37cWQvp+RpWzvieB/oiG/ykfDQX
+oZMRtwqwWTBfejNitbBBmC6G/t5OK+9aFmj7pfJ+a7fZKXfiUquIg9soDsoOindf
+2AwR6MZ3os8uiP2xrC8IQAClnETa15mFShs4a4b2VjddgCQ6tphnY97MywIVAPtr
+YyW11RIXsVTf/9KlbhYaNlt5AoGAX9JzbHykC/0xDKOyKU6xDIOVdEZ0ooAl9Psl
+BEUuNhlv2XgmQScO6C9l2W7gbbut7zIw4FaZ2/dgXa3D4IyexBVug5XMnrssErZo
+NcoF5g0dgEAsb9Hl9gkIK3VHM5kWteeUg1VE700JTtSMisdL8CgIdR+xN8iVH5Ew
+CbLWxmECgYEAtv+cdRfNevYFkp55jVqazc8zRLvfb64jzgc5oSJVc64kFs4yx+ab
+YpGX9WxNxDlG6g2WiY8voDBB0YnUJsn0kVRjBKX9OceROxrfT4K4dVbQZsdt+SLa
+XWL4lGJFrFZL3LZqvySvq6xfhJfakQDDivW4hUOhFPXPHrE5/Ia3T7ACFQCE6flG
+nmVCAbzo9YsbdJWBnxMnBA==
+-----END DSA PRIVATE KEY-----
\ No newline at end of file


[31/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common utilities code from sshd-core into sshd-common (new artifact)

Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/signature/BuiltinSignaturesTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/signature/BuiltinSignaturesTest.java b/sshd-common/src/test/java/org/apache/sshd/common/signature/BuiltinSignaturesTest.java
new file mode 100644
index 0000000..0ea58f7
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/signature/BuiltinSignaturesTest.java
@@ -0,0 +1,149 @@
+/*
+ * 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.signature;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Random;
+
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.signature.BuiltinSignatures.ParseResult;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+import org.mockito.Mockito;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class BuiltinSignaturesTest extends JUnitTestSupport {
+    public BuiltinSignaturesTest() {
+        super();
+    }
+
+    @Test
+    public void testFromName() {
+        for (BuiltinSignatures expected : BuiltinSignatures.VALUES) {
+            String name = expected.getName();
+            BuiltinSignatures actual = BuiltinSignatures.fromFactoryName(name);
+            assertSame(name, expected, actual);
+        }
+    }
+
+    @Test
+    public void testParseSignaturesList() {
+        List<String> builtin = NamedResource.getNameList(BuiltinSignatures.VALUES);
+        List<String> unknown = Arrays.asList(getClass().getPackage().getName(), getClass().getSimpleName(), getCurrentTestName());
+        Random rnd = new Random();
+        for (int index = 0; index < (builtin.size() + unknown.size()); index++) {
+            Collections.shuffle(builtin, rnd);
+            Collections.shuffle(unknown, rnd);
+
+            List<String> weavedList = new ArrayList<>(builtin.size() + unknown.size());
+            for (int bIndex = 0, uIndex = 0; (bIndex < builtin.size()) || (uIndex < unknown.size());) {
+                boolean useBuiltin = false;
+                if (bIndex < builtin.size()) {
+                    useBuiltin = uIndex >= unknown.size() || rnd.nextBoolean();
+                }
+
+                if (useBuiltin) {
+                    weavedList.add(builtin.get(bIndex));
+                    bIndex++;
+                } else if (uIndex < unknown.size()) {
+                    weavedList.add(unknown.get(uIndex));
+                    uIndex++;
+                }
+            }
+
+            String fullList = GenericUtils.join(weavedList, ',');
+            ParseResult result = BuiltinSignatures.parseSignatureList(fullList);
+            List<String> parsed = NamedResource.getNameList(result.getParsedFactories());
+            List<String> missing = result.getUnsupportedFactories();
+
+            // makes sure not only that the contents are the same but also the order
+            assertListEquals(fullList + "[parsed]", builtin, parsed);
+            assertListEquals(fullList + "[unsupported]", unknown, missing);
+        }
+    }
+
+    @Test
+    public void testResolveFactoryOnBuiltinValues() {
+        for (SignatureFactory expected : BuiltinSignatures.VALUES) {
+            String name = expected.getName();
+            SignatureFactory actual = BuiltinSignatures.resolveFactory(name);
+            assertSame(name, expected, actual);
+        }
+    }
+
+    @Test
+    public void testNotAllowedToRegisterBuiltinFactories() {
+        for (SignatureFactory expected : BuiltinSignatures.VALUES) {
+            try {
+                BuiltinSignatures.registerExtension(expected);
+                fail("Unexpected success for " + expected.getName());
+            } catch (IllegalArgumentException e) {
+                // expected - ignored
+            }
+        }
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testNotAllowedToOverrideRegisteredFactories() {
+        SignatureFactory expected = Mockito.mock(SignatureFactory.class);
+        Mockito.when(expected.getName()).thenReturn(getCurrentTestName());
+
+        String name = expected.getName();
+        try {
+            for (int index = 1; index <= Byte.SIZE; index++) {
+                BuiltinSignatures.registerExtension(expected);
+                assertEquals("Unexpected success at attempt #" + index, 1, index);
+            }
+        } finally {
+            BuiltinSignatures.unregisterExtension(name);
+        }
+    }
+
+    @Test
+    public void testResolveFactoryOnRegisteredExtension() {
+        SignatureFactory expected = Mockito.mock(SignatureFactory.class);
+        Mockito.when(expected.getName()).thenReturn(getCurrentTestName());
+
+        String name = expected.getName();
+        try {
+            assertNull("Extension already registered", BuiltinSignatures.resolveFactory(name));
+            BuiltinSignatures.registerExtension(expected);
+
+            SignatureFactory actual = BuiltinSignatures.resolveFactory(name);
+            assertSame("Mismatched resolved instance", expected, actual);
+        } finally {
+            SignatureFactory actual = BuiltinSignatures.unregisterExtension(name);
+            assertSame("Mismatched unregistered instance", expected, actual);
+            assertNull("Extension not un-registered", BuiltinSignatures.resolveFactory(name));
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/signature/SignatureDSATest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/signature/SignatureDSATest.java b/sshd-common/src/test/java/org/apache/sshd/common/signature/SignatureDSATest.java
new file mode 100644
index 0000000..d9f507d
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/signature/SignatureDSATest.java
@@ -0,0 +1,109 @@
+/*
+ * 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.signature;
+
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.spec.DSAPublicKeySpec;
+
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class SignatureDSATest extends JUnitTestSupport {
+    public SignatureDSATest() {
+        super();
+    }
+
+    @Test
+    public void testTooShortSignature() throws Exception {
+        KeyFactory kf = SecurityUtils.getKeyFactory(KeyUtils.DSS_ALGORITHM);
+        SignatureDSA signatureDSA = new SignatureDSA(KeyUtils.DSS_ALGORITHM) {
+            @Override
+            protected java.security.Signature doInitSignature(String algo, boolean forSigning) throws GeneralSecurityException {
+                return java.security.Signature.getInstance(algo);
+
+            }
+        };
+
+        byte[] y = new byte[] {
+            0, -92, 59, 5, 72, 124, 101, 124, -18, 114, 7, 100, 98, -61, 73, -104,
+            120, -98, 54, 118, 17, -62, 91, -110, 29, 98, 50, -101, -41, 99, -116,
+            101, 107, -123, 124, -97, 62, 119, 88, -109, -110, -1, 109, 119, -51,
+            69, -98, -105, 2, -69, -121, -82, -118, 23, -6, 96, -61, -65, 102, -58,
+            -74, 32, -104, 116, -6, -35, -83, -10, -88, -68, 106, -112, 72, -2, 35,
+            38, 15, -11, -22, 30, -114, -46, -47, -18, -17, -71, 24, -25, 28, 13, 29,
+            -40, 101, 18, 81, 45, -120, -67, -53, -41, 11, 50, -89, -33, 50, 54, -14,
+            -91, -35, 12, -42, 13, -84, -19, 100, -3, -85, -18, 74, 99, -49, 64, -49,
+            51, -83, -82, -127, 116, 64 };
+        byte[] p = new byte[] {
+            0, -3, 127, 83, -127, 29, 117, 18, 41, 82, -33, 74, -100, 46, -20, -28,
+            -25, -10, 17, -73, 82, 60, -17, 68, 0, -61, 30, 63, -128, -74, 81, 38,
+            105, 69, 93, 64, 34, 81, -5, 89, 61, -115, 88, -6, -65, -59, -11, -70,
+            48, -10, -53, -101, 85, 108, -41, -127, 59, -128, 29, 52, 111, -14, 102,
+            96, -73, 107, -103, 80, -91, -92, -97, -97, -24, 4, 123, 16, 34, -62, 79,
+            -69, -87, -41, -2, -73, -58, 27, -8, 59, 87, -25, -58, -88, -90, 21, 15, 4,
+            -5, -125, -10, -45, -59, 30, -61, 2, 53, 84, 19, 90, 22, -111, 50, -10, 117,
+            -13, -82, 43, 97, -41, 42, -17, -14, 34, 3, 25, -99, -47, 72, 1, -57 };
+        byte[] q = new byte[] {
+            0, -105, 96, 80, -113, 21, 35, 11, -52, -78, -110, -71, -126, -94, -21,
+            -124, 11, -16, 88, 28, -11 };
+        byte[] g = new byte[] {
+            0, -9, -31, -96, -123, -42, -101, 61, -34, -53, -68, -85, 92, 54, -72, 87,
+            -71, 121, -108, -81, -69, -6, 58, -22, -126, -7, 87, 76, 11, 61, 7, -126,
+            103, 81, 89, 87, -114, -70, -44, 89, 79, -26, 113, 7, 16, -127, -128, -76,
+            73, 22, 113, 35, -24, 76, 40, 22, 19, -73, -49, 9, 50, -116, -56, -90, -31,
+            60, 22, 122, -117, 84, 124, -115, 40, -32, -93, -82, 30, 43, -77, -90, 117,
+            -111, 110, -93, 127, 11, -6, 33, 53, 98, -15, -5, 98, 122, 1, 36, 59, -52,
+            -92, -15, -66, -88, 81, -112, -119, -88, -125, -33, -31, 90, -27, -97, 6,
+            -110, -117, 102, 94, -128, 123, 85, 37, 100, 1, 76, 59, -2, -49, 73, 42 };
+
+        BigInteger bigY = new BigInteger(y);
+        BigInteger bigP = new BigInteger(p);
+        BigInteger bigQ = new BigInteger(q);
+        BigInteger bigG = new BigInteger(g);
+
+        DSAPublicKeySpec dsaPublicKey = new DSAPublicKeySpec(bigY, bigP, bigQ, bigG);
+        signatureDSA.initVerifier(kf.generatePublic(dsaPublicKey));
+        byte[] h = new byte[] {
+            -4, 111, -103, 111, 72, -106, 105, -19, 81, -123, 84, -13, -40, -53, -3,
+            -97, -8, 43, -22, -2, -23, -15, 28, 116, -63, 96, -79, -127, -84, 63, -6, -94 };
+        signatureDSA.update(h);
+
+        byte[] sig_of_h = new byte[] {
+            0, 0, 0, 7, 115, 115, 104, 45, 100, 115, 115, 0, 0, 0, 40, 0, 79,
+            84, 118, -50, 11, -117, -112, 52, -25, -78, -50, -20, 6, -69, -26,
+            7, 90, -34, -124, 80, 76, -32, -23, -8, 43, 38, -48, -89, -17, -60,
+            -1, -78, 112, -88, 14, -39, -78, -98, -80 };
+        boolean verified = signatureDSA.verify(sig_of_h);
+
+        assertTrue(verified);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/signature/SignatureRSATest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/signature/SignatureRSATest.java b/sshd-common/src/test/java/org/apache/sshd/common/signature/SignatureRSATest.java
new file mode 100644
index 0000000..5671ac3
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/signature/SignatureRSATest.java
@@ -0,0 +1,119 @@
+/*
+ * 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.signature;
+
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.Provider;
+import java.security.PublicKey;
+import java.security.spec.RSAPublicKeySpec;
+import java.util.Base64;
+import java.util.Map;
+
+import org.apache.sshd.common.Factory;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.junit.BeforeClass;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class SignatureRSATest extends JUnitTestSupport {
+    private static final Base64.Decoder B64_DECODER = Base64.getDecoder();
+    @SuppressWarnings("checkstyle:linelength")
+    private static final byte[] TEST_MSG =
+            B64_DECODER.decode("AAAAFPHgK1MeV9zNnok3pwNJhCd8SONqMgAAAAlidWlsZHVzZXIAAAAOc3NoLWNvbm5lY3Rpb24AAAAJcHVibGlja2V5AQAAAAdzc2gtcnNhAAABFQAAAAdzc2gtcnNhAAAAASMAAAEBAMs9HO/NH/Now+6fSnESebaG4wzaYQWA1b/q1TGV1wHNtCg9fGFGVSKs0VxKF4cfVyrSLtgLjnlXQTn+Lm7xiYKGbBbsTQWOqEDaBVBsRbAkxIkpuvr6/EBxwrtDbKmSQYTJZVJSD2bZRYjGsR9gpZXPorOOKFd5EPCMHXsqnhp2hidTGH7cK6RuLk7MNnPISsY0Nbx8/ZvikiPROGcoTZ8bzUv4IaLr3veW6epSeQem8tJqhnrpTHhbLU99zf045M0Gsnk/azjjlBM+qrHZ5FNdC1kowJnLtf2Oy/rUQNpkGJtcBPT8xvreV0wLsn9t3hSxzsc0+VkDNTQRlfU+o3M=");
+    @SuppressWarnings("checkstyle:linelength")
+    private static final byte[] TEST_SIGNATURE =
+            B64_DECODER.decode("AAAAB3NzaC1yc2EAAAD/+Ntnf4qfr2J1voDS6I+u3VRjtMn+LdWJsAZfkLDxRkK1rQxP7QAjLdNqpT4CkWHp8dtoTGFlBFt6NieNJCMTA2KSOxJMZKsX7e/lHkh7C+vhQvJ9eLTKWjCxSFUrcM0NvFhmwbRCffwXSHvAKak4wbmofxQMpd+G4jZkNMz5kGpmeICBcNjRLPb7oXzuGr/g4x/3ge5Qaawqrg/gcZr/sKN6SdE8SszgKYO0SB320N4gcUoShVdLYr9uwdJ+kJoobfkUK6Or171JCctP/cu2nM79lDqVnJw/2jOG8OnTc8zRDXAh0RKoR5rOU8cOHm0Ls2MATsFdnyRU5FGUxqZ+");
+    private static PublicKey testKey;
+
+    public SignatureRSATest() {
+        super();
+    }
+
+    @BeforeClass
+    public static void initializeTestKey() throws GeneralSecurityException {
+        byte[] exp = B64_DECODER.decode("Iw==");
+        @SuppressWarnings("checkstyle:linelength")
+        byte[] mod = B64_DECODER.decode("AMs9HO/NH/Now+6fSnESebaG4wzaYQWA1b/q1TGV1wHNtCg9fGFGVSKs0VxKF4cfVyrSLtgLjnlXQTn+Lm7xiYKGbBbsTQWOqEDaBVBsRbAkxIkpuvr6/EBxwrtDbKmSQYTJZVJSD2bZRYjGsR9gpZXPorOOKFd5EPCMHXsqnhp2hidTGH7cK6RuLk7MNnPISsY0Nbx8/ZvikiPROGcoTZ8bzUv4IaLr3veW6epSeQem8tJqhnrpTHhbLU99zf045M0Gsnk/azjjlBM+qrHZ5FNdC1kowJnLtf2Oy/rUQNpkGJtcBPT8xvreV0wLsn9t3hSxzsc0+VkDNTQRlfU+o3M=");
+        KeyFactory kf = SecurityUtils.getKeyFactory(KeyUtils.RSA_ALGORITHM);
+        testKey = kf.generatePublic(new RSAPublicKeySpec(new BigInteger(mod), new BigInteger(exp)));
+    }
+
+    @Test   // see SSHD-642
+    public void testLeadingZeroesBC() throws Throwable {
+        testLeadingZeroes(new Factory<SignatureRSA>() {
+            @Override
+            public SignatureRSA create() {
+                return new SignatureRSA() {
+                    @Override
+                    protected java.security.Signature doInitSignature(String algo, boolean forSigning) throws GeneralSecurityException {
+                        assertFalse("Signature not initialized for verification", forSigning);
+                        java.security.Signature signature = super.doInitSignature(algo, forSigning);
+                        if (SecurityUtils.isBouncyCastleRegistered()) {
+                            Provider provider = signature.getProvider();
+                            String name = provider.getName();
+                            assertEquals("Mismatched BC provider name", SecurityUtils.BOUNCY_CASTLE, name);
+                        }
+                        return signature;
+                    }
+                };
+            }
+        });
+    }
+
+    @Test   // see SSHD-642
+    public void testLeadingZeroesJCE() throws Throwable {
+        testLeadingZeroes(() -> new SignatureRSA() {
+            @Override
+            protected java.security.Signature doInitSignature(String algo, boolean forSigning) throws GeneralSecurityException {
+                assertFalse("Signature not initialized for verification", forSigning);
+                java.security.Signature signature = java.security.Signature.getInstance(algo);
+                Provider provider = signature.getProvider();
+                String name = provider.getName();
+                assertNotEquals("BC provider used although not required", SecurityUtils.BOUNCY_CASTLE, name);
+                return signature;
+            }
+        });
+    }
+
+    private void testLeadingZeroes(Factory<? extends SignatureRSA> factory) throws Exception {
+        SignatureRSA rsa = factory.create();
+        rsa.initVerifier(testKey);
+
+        int vSize = rsa.getVerifierSignatureSize();
+        assertTrue("Verifier signature size not initialized", vSize > 0);
+
+        // make sure padding is required
+        Map.Entry<String, byte[]> encoding = rsa.extractEncodedSignature(TEST_SIGNATURE);
+        assertNotNull("Signature is not encoded", encoding);
+        byte[] data = encoding.getValue();
+        assertTrue("Signature data size (" + data.length + ") not below verifier size (" + vSize + ")", data.length < vSize);
+
+        rsa.update(TEST_MSG);
+        assertTrue("Failed to verify", rsa.verify(TEST_SIGNATURE));
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/signature/SignaturesDevelopment.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/signature/SignaturesDevelopment.java b/sshd-common/src/test/java/org/apache/sshd/common/signature/SignaturesDevelopment.java
new file mode 100644
index 0000000..2e15341
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/signature/SignaturesDevelopment.java
@@ -0,0 +1,81 @@
+/*
+ * 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.signature;
+
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.buffer.BufferUtils;
+import org.apache.sshd.common.util.security.eddsa.EdDSASecurityProviderUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.junit.Ignore;
+
+/**
+ * A &quot;scratch-pad&quot; class for testing signatures related code during development
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@Ignore("Used only for development")
+public class SignaturesDevelopment extends JUnitTestSupport {
+    public SignaturesDevelopment() {
+        super();
+    }
+
+    public static void testSignatureFactory(
+            SignatureFactory factory, KeyPair kp, byte[] data, boolean generateSignature, byte[] signature)
+                throws Exception {
+        Signature signer = factory.create();
+        if (generateSignature) {
+            signer.initSigner(kp.getPrivate());
+            signer.update(data);
+            signature = signer.sign();
+            System.out.append('\t').append("Signature: ").println(BufferUtils.toHex(':', signature));
+        } else {
+            signer.initVerifier(kp.getPublic());
+            signer.update(data);
+            if (signer.verify(signature)) {
+                System.out.append('\t').println("Valid signature");
+            } else {
+                System.err.append('\t').println("Invalid signature");
+            }
+        }
+    }
+
+    //////////////////////////////////////////////////////////////////////////
+
+    // args[0]=signatureName, args[1]=publicKey, args[2]=privateKey, args[3]=sign/verify, args[4]=data, args[5]=signature(if verify required)
+    public static void main(String[] args) throws Exception {
+        SignatureFactory factory = BuiltinSignatures.resolveFactory(args[0]);
+        // TODO recover public/private keys according to factory name
+        byte[] publicKey = BufferUtils.decodeHex(':', args[1]);
+        PublicKey pubKey = EdDSASecurityProviderUtils.generateEDDSAPublicKey(publicKey);
+        byte[] privateKey = BufferUtils.decodeHex(':', args[2]);
+        PrivateKey prvKey = EdDSASecurityProviderUtils.generateEDDSAPrivateKey(privateKey);
+        String op = args[3];
+        byte[] data = BufferUtils.decodeHex(':', args[4]);
+        byte[] signature = GenericUtils.EMPTY_BYTE_ARRAY;
+        if ("verify".equalsIgnoreCase(op)) {
+            signature = BufferUtils.decodeHex(':', args[5]);
+        }
+
+        testSignatureFactory(factory, new KeyPair(pubKey, prvKey), data, "sign".equalsIgnoreCase(op), signature);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/EventListenerUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/EventListenerUtilsTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/EventListenerUtilsTest.java
new file mode 100644
index 0000000..1619cd3
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/EventListenerUtilsTest.java
@@ -0,0 +1,149 @@
+/*
+ * 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.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.EventListener;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class EventListenerUtilsTest extends JUnitTestSupport {
+    public EventListenerUtilsTest() {
+        super();
+    }
+
+    @Test
+    public void testProxyWrapper() {
+        List<ProxyListenerImpl> impls = new ArrayList<>();
+        for (int index = 0; index < Byte.SIZE; index++) {
+            impls.add(new ProxyListenerImpl());
+        }
+
+        ProxyListener listener = EventListenerUtils.proxyWrapper(ProxyListener.class, impls);
+        String expStr = getCurrentTestName();
+        Number expNum = System.currentTimeMillis();
+        listener.callMeWithString(expStr);
+        listener.callMeWithNumber(expNum);
+
+        for (int index = 0; index < impls.size(); index++) {
+            ProxyListenerImpl l = impls.get(index);
+            assertSame("Mismatched string at listener #" + index, expStr, l.getStringValue());
+            assertSame("Mismatched number at listener #" + index, expNum, l.getNumberValue());
+        }
+    }
+
+    @Test
+    public void testListenerInstanceComparatorOnProxy() {
+        Comparator<? super EventListener> comparator = EventListenerUtils.LISTENER_INSTANCE_COMPARATOR;
+        ProxyListener p1 = EventListenerUtils.proxyWrapper(ProxyListener.class, Collections.singletonList(new ProxyListenerImpl()));
+        assertEquals("Mismatched self reference comparison", 0, comparator.compare(p1, p1));
+
+        EventListener l = new EventListener() { /* nothing extra */ };
+        assertEquals("Mismatched proxy vs. non-proxy result", 1, Integer.signum(comparator.compare(p1, l)));
+        assertEquals("Mismatched non-proxy vs. proxy result", -1, Integer.signum(comparator.compare(l, p1)));
+
+        ProxyListener p2 = EventListenerUtils.proxyWrapper(ProxyListener.class, Collections.singletonList(new ProxyListenerImpl()));
+        int p1vsp2 = Integer.signum(comparator.compare(p1, p2));
+        assertNotEquals("Mismatched p1 vs. p2 comparison", 0, p1vsp2);
+        assertEquals("Mismatched p2 vs. p1 comparison result", 0 - p1vsp2, Integer.signum(comparator.compare(p2, p1)));
+    }
+
+    @Test
+    public void testListenerInstanceComparatorOnNonProxy() {
+        Comparator<? super EventListener> comparator = EventListenerUtils.LISTENER_INSTANCE_COMPARATOR;
+        EventListener l1 = new EventListener() { /* nothing extra */ };
+        assertEquals("Mismatched self reference comparison", 0, comparator.compare(l1, l1));
+
+        EventListener l2 = new EventListener() { /* nothing extra */ };
+        int l1vsl2 = Integer.signum(comparator.compare(l1, l2));
+        assertNotEquals("Mismatched l1 vs. l2 comparison result", 0, l1vsl2);
+        assertEquals("Mismatched l2 vs. l1 comparison result", 0 - l1vsl2, Integer.signum(comparator.compare(l2, l1)));
+    }
+
+    @Test
+    public void testSynchronizedListenersSetOnProxies() {
+        ProxyListener p1 = EventListenerUtils.proxyWrapper(ProxyListener.class, Collections.singletonList(new ProxyListenerImpl()));
+        Set<ProxyListener> s = EventListenerUtils.synchronizedListenersSet();
+        for (int index = 1; index <= Byte.SIZE; index++) {
+            boolean modified = s.add(p1);
+            assertEquals("Mismatched p1 modification indicator at attempt #" + index, index == 1, modified);
+            assertEquals("Mismatched p1 set size at attempt #" + index, 1, s.size());
+        }
+
+        ProxyListener p2 = EventListenerUtils.proxyWrapper(ProxyListener.class, Collections.singletonList(new ProxyListenerImpl()));
+        for (int index = 1; index <= Byte.SIZE; index++) {
+            boolean modified = s.add(p2);
+            assertEquals("Mismatched p2 modification indicator at attempt #" + index, index == 1, modified);
+            assertEquals("Mismatched p2 set size at attempt #" + index, 2, s.size());
+        }
+
+        assertTrue("Failed to remove p1", s.remove(p1));
+        assertEquals("Mismatched post p1-remove size", 1, s.size());
+        assertTrue("Failed to remove p2", s.remove(p2));
+        assertEquals("Mismatched post p2-remove size", 0, s.size());
+    }
+
+    interface ProxyListener extends SshdEventListener {
+        void callMeWithString(String s);
+
+        void callMeWithNumber(Number n);
+    }
+
+    static class ProxyListenerImpl implements ProxyListener {
+        private String strValue;
+        private Number numValue;
+
+        ProxyListenerImpl() {
+            super();
+        }
+
+        public String getStringValue() {
+            return strValue;
+        }
+
+        @Override
+        public void callMeWithString(String s) {
+            strValue = s;
+        }
+
+        public Number getNumberValue() {
+            return numValue;
+        }
+
+        @Override
+        public void callMeWithNumber(Number n) {
+            numValue = n;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/GenericUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/GenericUtilsTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/GenericUtilsTest.java
new file mode 100644
index 0000000..5727e88
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/GenericUtilsTest.java
@@ -0,0 +1,173 @@
+/*
+ * 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.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category(NoIoTestCase.class)
+public class GenericUtilsTest extends JUnitTestSupport {
+    public GenericUtilsTest() {
+        super();
+    }
+
+    @Test
+    public void testSplitAndJoin() {
+        List<String> expected = Collections.unmodifiableList(
+                Arrays.asList(getClass().getPackage().getName().replace('.', '/'), getClass().getSimpleName(), getCurrentTestName()));
+
+        // NOTE: we also test characters that have meaning in String.split(...) as regex ones
+        for (char ch : new char[]{',', '.', '*', '?'}) {
+            String sep = String.valueOf(ch);
+            String s = GenericUtils.join(expected, sep);
+            String[] actual = GenericUtils.split(s, ch);
+            assertEquals("Mismatched split length for separator=" + sep, expected.size(), GenericUtils.length((Object[]) actual));
+
+            for (int index = 0; index < actual.length; index++) {
+                String e = expected.get(index);
+                String a = actual[index];
+                if (!e.endsWith(a)) {
+                    fail("Mismatched value at index=" + index + " for separator=" + sep + ": expected=" + e + ", actual=" + a);
+                }
+            }
+        }
+    }
+
+    @Test
+    public void testStripQuotes() {
+        String expected = getCurrentTestName();
+        assertSame("Unexpected un-quoted stripping", expected, GenericUtils.stripQuotes(expected));
+
+        StringBuilder sb = new StringBuilder(2 + expected.length()).append('|').append(expected).append('|');
+        for (int index = 0; index < GenericUtils.QUOTES.length(); index++) {
+            char delim = GenericUtils.QUOTES.charAt(index);
+            sb.setCharAt(0, delim);
+            sb.setCharAt(sb.length() - 1, delim);
+
+            CharSequence actual = GenericUtils.stripQuotes(sb);
+            assertEquals("Mismatched result for delim (" + delim + ")", expected, actual.toString());
+        }
+    }
+
+    @Test
+    public void testStripOnlyFirstLayerQuotes() {
+        StringBuilder sb = new StringBuilder().append("||").append(getCurrentTestName()).append("||");
+        char[] delims = {'\'', '"', '"', '\''};
+        for (int index = 0; index < delims.length; index += 2) {
+            char topDelim = delims[index];
+            char innerDelim = delims[index + 1];
+            sb.setCharAt(0, topDelim);
+            sb.setCharAt(1, innerDelim);
+            sb.setCharAt(sb.length() - 2, innerDelim);
+            sb.setCharAt(sb.length() - 1, topDelim);
+
+            CharSequence expected = sb.subSequence(1, sb.length() - 1);
+            CharSequence actual = GenericUtils.stripQuotes(sb);
+            assertEquals("Mismatched result for delim (" + topDelim + "/" + innerDelim + ")", expected.toString(), actual.toString());
+        }
+    }
+
+    @Test
+    public void testStripDelimiters() {
+        String expected = getCurrentTestName();
+        final char delim = '|';
+        assertSame("Unexpected un-delimited stripping", expected, GenericUtils.stripDelimiters(expected, delim));
+
+        CharSequence actual = GenericUtils.stripDelimiters(
+                new StringBuilder(2 + expected.length()).append(delim).append(expected).append(delim), delim);
+        assertEquals("Mismatched stripped values", expected, actual.toString());
+    }
+
+    @Test
+    public void testStripDelimitersOnlyIfOnBothEnds() {
+        final char delim = '$';
+        StringBuilder expected = new StringBuilder().append(delim).append(getCurrentTestName()).append(delim);
+        for (int index : new int[]{0, expected.length() - 1}) {
+            // restore original delimiters
+            expected.setCharAt(0, delim);
+            expected.setCharAt(expected.length() - 1, delim);
+            // trash one end
+            expected.setCharAt(index, (char) (delim + 1));
+
+            assertSame("Mismatched result for delim at index=" + index, expected, GenericUtils.stripDelimiters(expected, delim));
+        }
+    }
+
+    @Test
+    public void testAccumulateExceptionOnNullValues() {
+        assertNull("Unexpected null/null result", GenericUtils.accumulateException(null, null));
+
+        Throwable expected = new NoSuchMethodException(getClass().getName() + "#" + getCurrentTestName());
+        assertSame("Mismatched null/extra result", expected, GenericUtils.accumulateException(null, expected));
+        assertSame("Mismatched current/null result", expected, GenericUtils.accumulateException(expected, null));
+    }
+
+    @Test
+    public void testAccumulateExceptionOnExistingCurrent() {
+        RuntimeException[] expected = new RuntimeException[]{
+            new IllegalArgumentException(getCurrentTestName()),
+            new ClassCastException(getClass().getName()),
+            new NoSuchElementException(getClass().getPackage().getName())
+        };
+        RuntimeException current = new UnsupportedOperationException("top");
+        for (RuntimeException extra : expected) {
+            RuntimeException actual = GenericUtils.accumulateException(current, extra);
+            assertSame("Mismatched returned actual exception", current, actual);
+        }
+
+        Throwable[] actual = current.getSuppressed();
+        assertArrayEquals("Suppressed", expected, actual);
+    }
+
+    @Test
+    public void testNullOrEmptyCharArrayComparison() {
+        char[][] values = new char[][]{null, GenericUtils.EMPTY_CHAR_ARRAY};
+        for (char[] c1 : values) {
+            for (char[] c2 : values) {
+                assertEquals(((c1 == null) ? "null" : "empty") + " vs. " + ((c2 == null) ? "null" : "empty"), 0, GenericUtils.compare(c1, c2));
+            }
+        }
+    }
+
+    @Test
+    public void testCharArrayComparison() {
+        String s1 = getClass().getSimpleName();
+        char[] c1 = s1.toCharArray();
+        assertEquals("Same value equality", 0, GenericUtils.compare(c1, s1.toCharArray()));
+
+        String s2 = getCurrentTestName();
+        char[] c2 = s2.toCharArray();
+        assertEquals("s1 vs. s2", Integer.signum(s1.compareTo(s2)), Integer.signum(GenericUtils.compare(c1, c2)));
+        assertEquals("s2 vs. s1", Integer.signum(s2.compareTo(s1)), Integer.signum(GenericUtils.compare(c2, c1)));
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/Int2IntFunctionTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/Int2IntFunctionTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/Int2IntFunctionTest.java
new file mode 100644
index 0000000..b0c48ca
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/Int2IntFunctionTest.java
@@ -0,0 +1,154 @@
+/*
+ * 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.Random;
+import java.util.function.IntUnaryOperator;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class Int2IntFunctionTest extends JUnitTestSupport {
+    public Int2IntFunctionTest() {
+        super();
+    }
+
+    @Test
+    public void testAdd() {
+        int factor = Byte.SIZE;
+        IntUnaryOperator func = Int2IntFunction.add(factor);
+        for (int index = 1, sum = 0; index <= Byte.SIZE; index++) {
+            sum = func.applyAsInt(sum);
+            assertEquals(factor * index, sum);
+        }
+    }
+
+    @Test
+    public void testAddIdentity() {
+        IntUnaryOperator func = Int2IntFunction.add(0);
+        Random rnd = new Random(System.nanoTime());
+        for (int index = 1; index <= Byte.SIZE; index++) {
+            int expected = rnd.nextInt();
+            int actual = func.applyAsInt(expected);
+            assertEquals(expected, actual);
+        }
+    }
+
+    @Test
+    public void testSub() {
+        int factor = Byte.SIZE;
+        IntUnaryOperator func = Int2IntFunction.sub(factor);
+        for (int index = 1, sum = 0; index <= Byte.SIZE; index++) {
+            sum = func.applyAsInt(sum);
+            assertEquals(factor * index * -1, sum);
+        }
+    }
+
+    @Test
+    public void testSubIdentity() {
+        IntUnaryOperator func = Int2IntFunction.sub(0);
+        Random rnd = new Random(System.nanoTime());
+        for (int index = 1; index <= Byte.SIZE; index++) {
+            int expected = rnd.nextInt();
+            int actual = func.applyAsInt(expected);
+            assertEquals(expected, actual);
+        }
+    }
+
+    @Test
+    public void testMul() {
+        int factor = 2;
+        IntUnaryOperator func = Int2IntFunction.mul(factor);
+        for (int index = 1, mul = 1, expected = factor; index <= Byte.SIZE; index++, expected *= factor) {
+            mul = func.applyAsInt(mul);
+            assertEquals(expected, mul);
+        }
+    }
+
+    @Test
+    public void testMulIdentity() {
+        IntUnaryOperator func = Int2IntFunction.mul(1);
+        Random rnd = new Random(System.nanoTime());
+        for (int index = 1; index <= Byte.SIZE; index++) {
+            int expected = rnd.nextInt();
+            int actual = func.applyAsInt(expected);
+            assertEquals(expected, actual);
+        }
+    }
+
+    @Test
+    public void testMulZero() {
+        IntUnaryOperator func = Int2IntFunction.mul(0);
+        Random rnd = new Random(System.nanoTime());
+        for (int index = 1; index <= Byte.SIZE; index++) {
+            int value = rnd.nextInt();
+            int actual = func.applyAsInt(value);
+            assertEquals(Integer.toString(value), 0, actual);
+        }
+    }
+
+    @Test
+    public void testConstant() {
+        int expected = 377347;
+        IntUnaryOperator func = Int2IntFunction.constant(expected);
+        Random rnd = new Random(System.nanoTime());
+        for (int index = 1; index <= Byte.SIZE; index++) {
+            int value = rnd.nextInt();
+            int actual = func.applyAsInt(value);
+            assertEquals(Integer.toString(value), expected, actual);
+        }
+    }
+
+    @Test
+    public void testDiv() {
+        int factor = 2;
+        IntUnaryOperator func = Int2IntFunction.div(factor);
+        for (int index = 1, quot = 65536, expected = quot / factor; index <= Byte.SIZE; index++, expected /= factor) {
+            quot = func.applyAsInt(quot);
+            assertEquals(expected, quot);
+        }
+    }
+
+    @Test
+    public void testDivIdentity() {
+        IntUnaryOperator func = Int2IntFunction.div(1);
+        Random rnd = new Random(System.nanoTime());
+        for (int index = 1; index <= Byte.SIZE; index++) {
+            int expected = rnd.nextInt();
+            int actual = func.applyAsInt(expected);
+            assertEquals(expected, actual);
+        }
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testDivZeroFactor() {
+        IntUnaryOperator func = Int2IntFunction.div(0);
+        fail("Unexpected success: " + func);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/NumberUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/NumberUtilsTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/NumberUtilsTest.java
new file mode 100644
index 0000000..927cecb
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/NumberUtilsTest.java
@@ -0,0 +1,77 @@
+/*
+ * 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.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class NumberUtilsTest extends JUnitTestSupport {
+    public NumberUtilsTest() {
+        super();
+    }
+
+    @Test
+    public void testPowersOf2List() {
+        assertEquals("Mismatched values size for " + NumberUtils.POWERS_OF_TWO, Long.SIZE, GenericUtils.size(NumberUtils.POWERS_OF_TWO));
+        long expected = 1L;
+        for (int index = 0; index < Long.SIZE; index++, expected <<= 1) {
+            Long actual = NumberUtils.POWERS_OF_TWO.get(index);
+            assertEquals("Mismatched value at index=" + index, Long.toHexString(expected), Long.toHexString(actual));
+        }
+    }
+
+    @Test
+    public void testNextPowerOf2() {
+        for (Long v : NumberUtils.POWERS_OF_TWO) {
+            long expected = v;
+            if (expected > 2L) {
+                assertEquals("Mismatched lower bound value", expected, NumberUtils.getNextPowerOf2(expected - 1L));
+            }
+
+            if (expected > 0L) {    // avoid the negative value
+                assertEquals("Mismatched exact value", expected, NumberUtils.getNextPowerOf2(expected));
+            }
+        }
+    }
+
+    @Test
+    public void testToInteger() {
+        assertNull("Unexpected null value", NumberUtils.toInteger(null));
+        for (Number n : new Number[]{
+                Byte.valueOf(Byte.MAX_VALUE), Short.valueOf(Short.MIN_VALUE),
+                Integer.valueOf(Short.MAX_VALUE), Long.valueOf(82007160L)}) {
+            Integer i = NumberUtils.toInteger(n);
+            if (n instanceof Integer) {
+                assertSame("Unexpected conversion", n, i);
+            } else {
+                assertEquals("Mismatched values", n.intValue(), i.intValue());
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/OsUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/OsUtilsTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/OsUtilsTest.java
new file mode 100644
index 0000000..af78a11
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/OsUtilsTest.java
@@ -0,0 +1,135 @@
+/*
+ * 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;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class OsUtilsTest extends JUnitTestSupport {
+    public OsUtilsTest() {
+        super();
+    }
+
+    @Test
+    public void testSetOsTypeByProperty() {
+        try {
+            for (String osType : new String[]{"Some-Windows", "Some-Linux"}) {
+                OsUtils.setWin32(null); // force re-detection
+
+                try {
+                    boolean expected = osType.contains("Windows");
+                    System.setProperty(OsUtils.OS_TYPE_OVERRIDE_PROP, osType);
+                    boolean actual = OsUtils.isWin32();
+                    assertEquals(osType, expected, actual);
+                } finally {
+                    System.clearProperty(OsUtils.OS_TYPE_OVERRIDE_PROP);
+                }
+            }
+        } finally {
+            OsUtils.setWin32(null); // force re-detection
+        }
+    }
+
+    @Test
+    public void testSetOsTypeProgrammatically() {
+        try {
+            for (boolean expected : new boolean[]{true, false}) {
+                OsUtils.setWin32(expected); // force value
+                assertEquals("Mismatched detection value", expected, OsUtils.isWin32());
+            }
+        } finally {
+            OsUtils.setWin32(null); // force re-detection
+        }
+    }
+
+    @Test
+    public void testSetCurrentUserByProperty() {
+        try {
+            for (String expected : new String[]{getClass().getSimpleName(), getCurrentTestName()}) {
+                OsUtils.setCurrentUser(null); // force re-detection
+
+                try {
+                    System.setProperty(OsUtils.CURRENT_USER_OVERRIDE_PROP, expected);
+                    String actual = OsUtils.getCurrentUser();
+                    assertEquals("Mismatched reported current user", expected, actual);
+                } finally {
+                    System.clearProperty(OsUtils.CURRENT_USER_OVERRIDE_PROP);
+                }
+            }
+        } finally {
+            OsUtils.setCurrentUser(null); // force re-detection
+        }
+    }
+
+    @Test
+    public void testSetCurrentUserProgrammatically() {
+        try {
+            for (String expected : new String[]{getClass().getSimpleName(), getCurrentTestName()}) {
+                OsUtils.setCurrentUser(expected); // force value
+                assertEquals("Mismatched detection value", expected, OsUtils.getCurrentUser());
+            }
+        } finally {
+            OsUtils.setCurrentUser(null); // force re-detection
+        }
+    }
+
+    @Test
+    public void testSetJavaVersionByProperty() {
+        try {
+            for (String value : new String[]{"7.3.6_5", "37.77.34_7-" + getCurrentTestName()}) {
+                OsUtils.setJavaVersion(null); // force re-detection
+
+                try {
+                    System.setProperty(OsUtils.JAVA_VERSION_OVERRIDE_PROP, value);
+                    String expected = value.replace('_', '.');
+                    String actual = Objects.toString(OsUtils.getJavaVersion(), null);
+                    assertTrue("Mismatched reported version value: " + actual, expected.startsWith(actual));
+                } finally {
+                    System.clearProperty(OsUtils.JAVA_VERSION_OVERRIDE_PROP);
+                }
+            }
+        } finally {
+            OsUtils.setJavaVersion(null); // force re-detection
+        }
+    }
+
+    @Test
+    public void testSetJavaVersionProgrammatically() {
+        try {
+            for (VersionInfo expected : new VersionInfo[]{VersionInfo.parse("7.3.6.5"), VersionInfo.parse("37.77.34.7")}) {
+                OsUtils.setJavaVersion(expected); // force value
+                assertEquals("Mismatched detection value", expected, OsUtils.getJavaVersion());
+            }
+        } finally {
+            OsUtils.setJavaVersion(null); // force re-detection
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/SelectorUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/SelectorUtilsTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/SelectorUtilsTest.java
new file mode 100644
index 0000000..3044699
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/SelectorUtilsTest.java
@@ -0,0 +1,147 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util;
+
+import java.io.File;
+import java.util.Random;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.Assume;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class SelectorUtilsTest extends JUnitTestSupport {
+    public SelectorUtilsTest() {
+        super();
+    }
+
+    @Test
+    public void testApplyLinuxSeparatorSlashifyRules() {
+        testApplySlashifyRules('/');
+    }
+
+    @Test
+    public void testApplyWindowsSeparatorSlashifyRules() {
+        testApplySlashifyRules('\\');
+    }
+
+    private void testApplySlashifyRules(char slash) {
+        for (String expected : new String[]{
+            null, "", getCurrentTestName(),
+            getClass().getSimpleName() + Character.toString(slash) + getCurrentTestName(),
+            Character.toString(slash)  + getClass().getSimpleName(),
+            Character.toString(slash)  + getClass().getSimpleName() + Character.toString(slash)  + getCurrentTestName()
+        }) {
+            String actual = SelectorUtils.applySlashifyRules(expected, slash);
+            assertSame("Mismatched results for '" + expected + "'", expected, actual);
+        }
+
+        String[] comps = {getClass().getSimpleName(),  getCurrentTestName()};
+        Random rnd = new Random(System.nanoTime());
+        StringBuilder sb = new StringBuilder(Byte.MAX_VALUE);
+        for (int index = 0; index < Long.SIZE; index++) {
+            if (sb.length() > 0) {
+                sb.setLength(0);        // start from scratch
+            }
+
+            boolean prepend = rnd.nextBoolean();
+            if (prepend) {
+                slashify(sb, rnd, slash);
+            }
+
+            sb.append(comps[0]);
+            for (int j = 1; j < comps.length; j++) {
+                slashify(sb, rnd, slash);
+                sb.append(comps[j]);
+            }
+
+            boolean append = rnd.nextBoolean();
+            if (append) {
+                slashify(sb, rnd, slash);
+            }
+
+            String path = sb.toString();
+            sb.setLength(0);
+            if (prepend) {
+                sb.append(slash);
+            }
+
+            sb.append(comps[0]);
+            for (int j = 1; j < comps.length; j++) {
+                sb.append(slash).append(comps[j]);
+            }
+
+            if (append) {
+                sb.append(slash).append('.');
+            }
+
+            String expected = sb.toString();
+            String actual = SelectorUtils.applySlashifyRules(path, slash);
+            assertEquals("Mismatched results for path=" + path, expected, actual);
+        }
+    }
+
+    private static int slashify(StringBuilder sb, Random rnd, char slash) {
+        int slashes = 1 /* at least one slash */ + rnd.nextInt(Byte.SIZE);
+        for (int k = 0; k < slashes; k++) {
+            sb.append(slash);
+        }
+
+        return slashes;
+    }
+
+    @Test
+    public void testTranslateToFileSystemPath() {
+        String path = getClass().getPackage().getName().replace('.', File.separatorChar)
+                    + File.separator + getClass().getSimpleName()
+                    + File.separator + getCurrentTestName();
+        for (String expected : new String[] {null, "", path}) {
+            String actual = SelectorUtils.translateToFileSystemPath(expected, File.separator, File.separator);
+            assertSame("Mismatched instance for translated result", expected, actual);
+        }
+
+        for (String fsSeparator : new String[] {String.valueOf('.'), "##"}) {
+            String expected = path.replace(File.separator, fsSeparator);
+            String actual = SelectorUtils.translateToFileSystemPath(path, File.separator, fsSeparator);
+            assertEquals("Mismatched translation result for separator='" + fsSeparator + "'", expected, actual);
+
+            actual = SelectorUtils.translateToFileSystemPath(actual, fsSeparator, File.separator);
+            assertEquals("Mismatched translation revert for separator='" + fsSeparator + "'", path, actual);
+        }
+    }
+
+    @Test
+    public void testAbsoluteWindowsPathTranslation() {
+        Assume.assumeTrue("Not tested on Windows", OsUtils.isWin32());
+        String expected = detectTargetFolder().toString();
+        for (String prefix : new String[]{"", "/"}) {
+            String actual = SelectorUtils.translateToLocalPath(prefix + expected.replace('/', File.separatorChar));
+            assertEquals("Mismatched result for prefix='" + prefix + "'", expected, actual);
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/ThreadUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/ThreadUtilsTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/ThreadUtilsTest.java
new file mode 100644
index 0000000..10e6f5b
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/ThreadUtilsTest.java
@@ -0,0 +1,72 @@
+/*
+ * 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.Collection;
+
+import org.apache.sshd.common.util.threads.CloseableExecutorService;
+import org.apache.sshd.common.util.threads.ThreadUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class ThreadUtilsTest extends JUnitTestSupport {
+    public ThreadUtilsTest() {
+        super();
+    }
+
+    @Test
+    public void testProtectExecutorServiceShutdown() {
+        for (boolean shutdownOnExit : new boolean[]{true, false}) {
+            assertNull("Unexpected instance for shutdown=" + shutdownOnExit, ThreadUtils.protectExecutorServiceShutdown(null, shutdownOnExit));
+        }
+
+        CloseableExecutorService service = ThreadUtils.newSingleThreadExecutor("pool");
+        try {
+            assertSame("Unexpected wrapped instance", service, ThreadUtils.protectExecutorServiceShutdown(service, true));
+
+            CloseableExecutorService wrapped = ThreadUtils.protectExecutorServiceShutdown(service, false);
+            try {
+                assertNotSame("No wrapping occurred", service, wrapped);
+
+                wrapped.shutdown();
+                assertTrue("Wrapped service not shutdown", wrapped.isShutdown());
+                assertFalse("Protected service is shutdown", service.isShutdown());
+
+                Collection<?> running = wrapped.shutdownNow();
+                assertTrue("Non-empty runners list", running.isEmpty());
+                assertTrue("Wrapped service not shutdownNow", wrapped.isShutdown());
+                assertFalse("Protected service is shutdownNow", service.isShutdown());
+            } finally {
+                wrapped.shutdownNow();  // just in case
+            }
+        } finally {
+            service.shutdownNow();  // just in case...
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/ValidateUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/ValidateUtilsTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/ValidateUtilsTest.java
new file mode 100644
index 0000000..9efd5f9
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/ValidateUtilsTest.java
@@ -0,0 +1,44 @@
+/*
+ * 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.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category(NoIoTestCase.class)
+public class ValidateUtilsTest extends JUnitTestSupport {
+    public ValidateUtilsTest() {
+        super();
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void checkNotNull() {
+        ValidateUtils.checkNotNull(getClass(), getCurrentTestName());
+        ValidateUtils.checkNotNull(null, getCurrentTestName());
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/VersionInfoTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/VersionInfoTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/VersionInfoTest.java
new file mode 100644
index 0000000..834be24
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/VersionInfoTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class VersionInfoTest extends JUnitTestSupport {
+    public VersionInfoTest() {
+        super();
+    }
+
+    @Test
+    public void testLessThan4Components() {
+        VersionInfo expected = new VersionInfo(73, 65);
+        VersionInfo actual = VersionInfo.parse(NumberUtils.join('.', expected.getMajorVersion(), expected.getMinorVersion()));
+        assertEquals("Mismatched result", expected, actual);
+    }
+
+    @Test
+    public void testMoreThan4Components() {
+        VersionInfo expected = new VersionInfo(7, 3, 6, 5);
+        VersionInfo actual = VersionInfo.parse(expected.toString() + ".3.7.7.7.3.4.7");
+        assertEquals("Mismatched result", expected, actual);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/buffer/BufferTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/buffer/BufferTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/buffer/BufferTest.java
new file mode 100644
index 0000000..6f41b76
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/buffer/BufferTest.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util.buffer;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.nio.charset.StandardCharsets;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class BufferTest extends JUnitTestSupport {
+    public BufferTest() {
+        super();
+    }
+
+    @Test
+    public void testGetLong() throws Exception {
+        long expected = 1234567890123456789L;
+
+        try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) {
+            try (DataOutputStream ds = new DataOutputStream(stream)) {
+                ds.writeLong(expected);
+            }
+
+            Buffer buffer = new ByteArrayBuffer(stream.toByteArray());
+            assertEquals("Mismatched recovered value", expected, buffer.getLong());
+        }
+    }
+
+    @Test
+    public void testPutCharsWithNullOrEmptyValue() {
+        Buffer buffer = new ByteArrayBuffer(Integer.SIZE);
+        for (char[] chars : new char[][]{null, GenericUtils.EMPTY_CHAR_ARRAY}) {
+            buffer.putChars(chars);
+
+            String value = buffer.getString();
+            assertEquals("Mismatched value for " + ((chars == null) ? "null" : "empty") + " characters", "", value);
+        }
+    }
+
+    @Test
+    public void testPutCharsOnNonEmptyValue() {
+        String expected = getCurrentTestName();
+        Buffer buffer = new ByteArrayBuffer(expected.length() + Byte.SIZE);
+        buffer.putChars(expected.toCharArray());
+
+        String actual = buffer.getString();
+        assertEquals("Mismatched recovered values", expected, actual);
+    }
+
+    @Test
+    public void testPutAndWipeChars() {
+        String expected = getCurrentTestName();
+        char[] chars = expected.toCharArray();
+        Buffer buffer = new ByteArrayBuffer(chars.length + Byte.SIZE);
+        buffer.putAndWipeChars(chars);
+
+        String actual = buffer.getString();
+        assertEquals("Mismatched recovered values", expected, actual);
+
+        for (int index = 0; index < chars.length; index++) {
+            assertEquals("Character not wiped at index=" + index, 0, chars[index]);
+        }
+    }
+
+    @Test
+    public void testPutAndWipeBytes() {
+        String expected = getCurrentTestName();
+        byte[] bytes = expected.getBytes(StandardCharsets.UTF_8);
+        Buffer buffer = new ByteArrayBuffer(bytes.length + Byte.SIZE);
+        buffer.putAndWipeBytes(bytes);
+        String actual = buffer.getString();
+        assertEquals("Mismatched recovered values", expected, actual);
+
+        for (int index = 0; index < bytes.length; index++) {
+            assertEquals("Value not wiped at index=" + index, 0, bytes[index]);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/buffer/BufferUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/buffer/BufferUtilsTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/buffer/BufferUtilsTest.java
new file mode 100644
index 0000000..78122b7
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/buffer/BufferUtilsTest.java
@@ -0,0 +1,73 @@
+/*
+ * 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.buffer;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Random;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class BufferUtilsTest extends JUnitTestSupport {
+    public BufferUtilsTest() {
+        super();
+    }
+
+    @Test
+    public void testHexEncodeDecode() {
+        String expValue = getClass().getName() + "#" + getCurrentTestName();
+        byte[] expData = expValue.getBytes(StandardCharsets.UTF_8);
+        for (char sep : new char[]{BufferUtils.EMPTY_HEX_SEPARATOR, ':'}) {
+            String hexData = BufferUtils.toHex(sep, expData);
+            byte[] actData = BufferUtils.decodeHex(sep, hexData);
+            String actValue = new String(actData, StandardCharsets.UTF_8);
+            String sepName = (BufferUtils.EMPTY_HEX_SEPARATOR == sep) ? "EMPTY" : Character.toString(sep);
+            outputDebugMessage("Decode(sep=%s) expected=%s, actual=%s", sepName, expValue, actValue);
+            assertArrayEquals("Mismatched result for sep='" + sepName + "'", expData, actData);
+        }
+    }
+
+    @Test
+    public void testGetCompactClone() {
+        byte[] expected = getCurrentTestName().getBytes(StandardCharsets.UTF_8);
+        final int testOffset = Byte.SIZE / 2;
+        byte[] data = new byte[expected.length + 2 * testOffset];
+        Random rnd = new Random(System.nanoTime());
+        rnd.nextBytes(data);
+        System.arraycopy(expected, 0, data, testOffset, expected.length);
+
+        Buffer buf = ByteArrayBuffer.getCompactClone(data, testOffset, expected.length);
+        assertEquals("Mismatched cloned buffer read position", 0, buf.rpos());
+        assertEquals("Mismatched cloned buffer available size", expected.length, buf.available());
+
+        byte[] actual = buf.array();
+        assertNotSame("Original data not cloned", data, actual);
+        assertArrayEquals("Mismatched cloned contents", expected, actual);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/closeable/CloseableUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/closeable/CloseableUtilsTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/closeable/CloseableUtilsTest.java
new file mode 100644
index 0000000..6ed1187
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/closeable/CloseableUtilsTest.java
@@ -0,0 +1,163 @@
+/*
+ * 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.closeable;
+
+import java.io.IOException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.sshd.common.Closeable;
+import org.apache.sshd.common.future.CloseFuture;
+import org.apache.sshd.common.future.DefaultCloseFuture;
+import org.apache.sshd.common.future.SshFutureListener;
+import org.apache.sshd.common.util.threads.ThreadUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class CloseableUtilsTest extends JUnitTestSupport {
+    public CloseableUtilsTest() {
+        super();
+    }
+
+    @Test
+    public void testCloseImmediateNotCalledIfAlreadyClosed() throws IOException {
+        Closeable closeable = new IoBaseCloseable() {
+            @Override
+            public CloseFuture close(boolean immediately) {
+                fail("Unexpected call to close(" + immediately + ")");
+                return null;
+            }
+
+            @Override
+            public void addCloseFutureListener(SshFutureListener<CloseFuture> listener) {
+                fail("Unexpected call to addCloseFutureListener");
+            }
+
+            @Override
+            public void removeCloseFutureListener(SshFutureListener<CloseFuture> listener) {
+                fail("Unexpected call to removeCloseFutureListener");
+            }
+
+            @Override
+            public boolean isClosed() {
+                return true;
+            }
+
+            @Override
+            public boolean isClosing() {
+                return false;
+            }
+        };
+        closeable.close();
+    }
+
+    @Test
+    public void testCloseImmediateNotCalledIfIsClosing() throws IOException {
+        Closeable closeable = new IoBaseCloseable() {
+            @Override
+            public CloseFuture close(boolean immediately) {
+                fail("Unexpected call to close(" + immediately + ")");
+                return null;
+            }
+
+            @Override
+            public void addCloseFutureListener(SshFutureListener<CloseFuture> listener) {
+                fail("Unexpected call to addCloseFutureListener");
+            }
+
+            @Override
+            public void removeCloseFutureListener(SshFutureListener<CloseFuture> listener) {
+                fail("Unexpected call to removeCloseFutureListener");
+            }
+
+            @Override
+            public boolean isClosed() {
+                return false;
+            }
+
+            @Override
+            public boolean isClosing() {
+                return true;
+            }
+        };
+        closeable.close();
+    }
+
+    @Test
+    public void testCloseImmediateCalledAndWait() throws Exception {
+        DefaultCloseFuture future = new DefaultCloseFuture(this, this);
+        AtomicInteger callsCount = new AtomicInteger(0);
+        Closeable closeable = new IoBaseCloseable() {
+            @Override
+            public CloseFuture close(boolean immediately) {
+                assertTrue("Closure is not immediate", immediately);
+                assertEquals("Multiple close immediate calls", 1, callsCount.incrementAndGet());
+                return future;
+            }
+
+            @Override
+            public void addCloseFutureListener(SshFutureListener<CloseFuture> listener) {
+                fail("Unexpected call to addCloseFutureListener");
+            }
+
+            @Override
+            public void removeCloseFutureListener(SshFutureListener<CloseFuture> listener) {
+                fail("Unexpected call to removeCloseFutureListener");
+            }
+
+            @Override
+            public boolean isClosed() {
+                return false;
+            }
+
+            @Override
+            public boolean isClosing() {
+                return false;
+            }
+        };
+
+        ExecutorService service = ThreadUtils.newSingleThreadExecutor(getCurrentTestName());
+        try {
+            Future<?> task = service.submit((Runnable) () -> {
+                try {
+                    closeable.close();
+                } catch (IOException e) {
+                    throw new RuntimeException(e);
+                }
+            });
+            future.setClosed();  // signal close complete
+            task.get(5L, TimeUnit.SECONDS);  // make sure #await call terminated
+            assertEquals("Close immediate not called", 1, callsCount.get());
+        } finally {
+            service.shutdownNow();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/io/EmptyInputStreamTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/io/EmptyInputStreamTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/io/EmptyInputStreamTest.java
new file mode 100644
index 0000000..113758d
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/io/EmptyInputStreamTest.java
@@ -0,0 +1,120 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.sshd.common.util.buffer.BufferUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class EmptyInputStreamTest extends JUnitTestSupport {
+    public EmptyInputStreamTest() {
+        super();
+    }
+
+    @Test
+    public void testEmptyInputStream() throws IOException {
+        try (EmptyInputStream in = new EmptyInputStream()) {
+            testEmptyInputStream(in, false);
+        }
+    }
+
+    @Test
+    public void testCloseableEmptyInputStream() throws IOException {
+        try (EmptyInputStream in = new CloseableEmptyInputStream()) {
+            testEmptyInputStream(in, true);
+        }
+    }
+
+    private void testEmptyInputStream(InputStream in, boolean failAfterClose) throws IOException {
+        testEmptyInputStream("open", in, false);
+        in.close();
+        testEmptyInputStream("closed", in, failAfterClose);
+    }
+
+    private void testEmptyInputStream(String message, InputStream in, boolean errorExpected) {
+        assertFalse(message + ": unexpected markSupported()", in.markSupported());
+        try {
+            in.mark(Long.SIZE);
+            fail(message + ": unexpected mark success");
+        } catch (UnsupportedOperationException e) {
+            // expected
+        }
+
+        try {
+            int len = in.available();
+            assertFalse(message + ": Unexpected success in available(): " + len, errorExpected);
+            assertEquals(message + ": Mismatched available() result", 0, len);
+        } catch (IOException e) {
+            assertTrue(message + ": Unexpected error on available(): " + e.getMessage(), errorExpected);
+        }
+
+        try {
+            int data = in.read();
+            assertFalse(message + ": Unexpected success in read(): " + data, errorExpected);
+            assertEquals(message + ": Mismatched read() result", -1, data);
+        } catch (IOException e) {
+            assertTrue(message + ": Unexpected error on read(): " + e.getMessage(), errorExpected);
+        }
+
+        byte[] bytes = new byte[Byte.SIZE];
+        try {
+            int len = in.read(bytes);
+            assertFalse(message + ": Unexpected success in read([]): " + BufferUtils.toHex(':', bytes), errorExpected);
+            assertEquals(message + ": Mismatched read([]) result", -1, len);
+        } catch (IOException e) {
+            assertTrue(message + ": Unexpected error on read([]): " + e.getMessage(), errorExpected);
+        }
+
+        try {
+            int len = in.read(bytes, 0, bytes.length);
+            assertFalse(message + ": Unexpected success in read([],int,int): " + BufferUtils.toHex(':', bytes), errorExpected);
+            assertEquals(message + ": Mismatched read([],int,int) result", -1, len);
+        } catch (IOException e) {
+            assertTrue(message + ": Unexpected error on read([],int,int): " + e.getMessage(), errorExpected);
+        }
+
+        try {
+            long len = in.skip(Byte.MAX_VALUE);
+            assertFalse(message + ": Unexpected success in skip(): " + len, errorExpected);
+            assertEquals(message + ": Mismatched skip() result", 0L, len);
+        } catch (IOException e) {
+            assertTrue(message + ": Unexpected error on skip(): " + e.getMessage(), errorExpected);
+        }
+
+        try {
+            in.reset();
+            assertFalse(message + ": Unexpected success in reset()", errorExpected);
+        } catch (IOException e) {
+            assertTrue(message + ": Unexpected error on reset(): " + e.getMessage(), errorExpected);
+        }
+    }
+}


[42/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common utilities code from sshd-core into sshd-common (new artifact)

Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/digest/DigestUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/digest/DigestUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/digest/DigestUtils.java
new file mode 100644
index 0000000..30369ac
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/digest/DigestUtils.java
@@ -0,0 +1,228 @@
+/*
+ * 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.digest;
+
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.util.Base64;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Objects;
+
+import org.apache.sshd.common.Factory;
+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;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class DigestUtils {
+    private DigestUtils() {
+        throw new UnsupportedOperationException("No instance");
+    }
+
+    /**
+     * @param algorithm The digest algorithm - never {@code null}/empty
+     * @return {@code true} if this digest algorithm is supported
+     * @see SecurityUtils#getMessageDigest(String)
+     */
+    public static boolean checkSupported(String algorithm) {
+        ValidateUtils.checkNotNullAndNotEmpty(algorithm, "No algorithm");
+        try {
+            MessageDigest digest = SecurityUtils.getMessageDigest(algorithm);
+            return digest != null;  // just in case
+        } catch (Exception e) {
+            return false;
+        }
+    }
+
+    /**
+     * @param <D> The generic type of digest factory
+     * @param algo The required algorithm name - ignored if {@code null}/empty
+     * @param comp The {@link Comparator} to use to compare algorithm names
+     * @param digests The factories to check - ignored if {@code null}/empty
+     * @return The first {@link DigestFactory} whose algorithm matches the required one
+     * according to the comparator - {@code null} if no match found
+     */
+    public static <D extends Digest> D findDigestByAlgorithm(String algo, Comparator<? super String> comp, Collection<? extends D> digests) {
+        if (GenericUtils.isEmpty(algo) || GenericUtils.isEmpty(digests)) {
+            return null;
+        }
+
+        for (D d : digests) {
+            if (comp.compare(algo, d.getAlgorithm()) == 0) {
+                return d;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * @param <F> The generic type of digest factory
+     * @param algo The required algorithm name - ignored if {@code null}/empty
+     * @param comp The {@link Comparator} to use to compare algorithm names
+     * @param factories The factories to check - ignored if {@code null}/empty
+     * @return The first {@link DigestFactory} whose algorithm matches the required one
+     * according to the comparator - {@code null} if no match found
+     */
+    public static <F extends DigestFactory> F findFactoryByAlgorithm(String algo, Comparator<? super String> comp, Collection<? extends F> factories) {
+        if (GenericUtils.isEmpty(algo) || GenericUtils.isEmpty(factories)) {
+            return null;
+        }
+
+        for (F f : factories) {
+            if (comp.compare(algo, f.getAlgorithm()) == 0) {
+                return f;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * @param f The {@link Factory} to create the {@link Digest} to use
+     * @param s The {@link String} to digest - ignored if {@code null}/empty,
+     *          otherwise its UTF-8 representation is used as input for the fingerprint
+     * @return The fingerprint - {@code null} if {@code null}/empty input
+     * @throws Exception If failed to calculate the digest
+     * @see #getFingerPrint(Digest, String, Charset)
+     */
+    public static String getFingerPrint(Factory<? extends Digest> f, String s) throws Exception {
+        return getFingerPrint(f, s, StandardCharsets.UTF_8);
+    }
+
+    /**
+     * @param f       The {@link Factory} to create the {@link Digest} to use
+     * @param s       The {@link String} to digest - ignored if {@code null}/empty
+     * @param charset The {@link Charset} to use in order to convert the
+     *                string to its byte representation to use as input for the fingerprint
+     * @return The fingerprint - {@code null} if {@code null}/empty input
+     * @throws Exception If failed to calculate the digest
+     */
+    public static String getFingerPrint(Factory<? extends Digest> f, String s, Charset charset) throws Exception {
+        return getFingerPrint(Objects.requireNonNull(f, "No factory").create(), s, charset);
+    }
+
+    /**
+     * @param d The {@link Digest} to use
+     * @param s The {@link String} to digest - ignored if {@code null}/empty,
+     *          otherwise its UTF-8 representation is used as input for the fingerprint
+     * @return The fingerprint - {@code null} if {@code null}/empty input
+     * @throws Exception If failed to calculate the digest
+     * @see #getFingerPrint(Digest, String, Charset)
+     */
+    public static String getFingerPrint(Digest d, String s) throws Exception {
+        return getFingerPrint(d, s, StandardCharsets.UTF_8);
+    }
+
+    /**
+     * @param d       The {@link Digest} to use
+     * @param s       The {@link String} to digest - ignored if {@code null}/empty
+     * @param charset The {@link Charset} to use in order to convert the
+     *                string to its byte representation to use as input for the fingerprint
+     * @return The fingerprint - {@code null} if {@code null}/empty input
+     * @throws Exception If failed to calculate the digest
+     */
+    public static String getFingerPrint(Digest d, String s, Charset charset) throws Exception {
+        if (GenericUtils.isEmpty(s)) {
+            return null;
+        } else {
+            return DigestUtils.getFingerPrint(d, s.getBytes(charset));
+        }
+    }
+
+    /**
+     * @param f   The {@link Factory} to create the {@link Digest} to use
+     * @param buf The data buffer to be fingerprint-ed
+     * @return The fingerprint - {@code null} if empty data buffer
+     * @throws Exception If failed to calculate the fingerprint
+     * @see #getFingerPrint(Factory, byte[], int, int)
+     */
+    public static String getFingerPrint(Factory<? extends Digest> f, byte... buf) throws Exception {
+        return getFingerPrint(f, buf, 0, NumberUtils.length(buf));
+    }
+
+    /**
+     * @param f      The {@link Factory} to create the {@link Digest} to use
+     * @param buf    The data buffer to be fingerprint-ed
+     * @param offset The offset of the data in the buffer
+     * @param len    The length of data - ignored if non-positive
+     * @return The fingerprint - {@code null} if non-positive length
+     * @throws Exception If failed to calculate the fingerprint
+     */
+    public static String getFingerPrint(Factory<? extends Digest> f, byte[] buf, int offset, int len) throws Exception {
+        return getFingerPrint(Objects.requireNonNull(f, "No factory").create(), buf, offset, len);
+    }
+
+    /**
+     * @param d   The {@link Digest} to use
+     * @param buf The data buffer to be fingerprint-ed
+     * @return The fingerprint - {@code null} if empty data buffer
+     * @throws Exception If failed to calculate the fingerprint
+     * @see #getFingerPrint(Digest, byte[], int, int)
+     */
+    public static String getFingerPrint(Digest d, byte... buf) throws Exception {
+        return getFingerPrint(d, buf, 0, NumberUtils.length(buf));
+    }
+
+    /**
+     * @param d      The {@link Digest} to use
+     * @param buf    The data buffer to be fingerprint-ed
+     * @param offset The offset of the data in the buffer
+     * @param len    The length of data - ignored if non-positive
+     * @return The fingerprint - {@code null} if non-positive length
+     * @throws Exception If failed to calculate the fingerprint
+     * @see #getRawFingerprint(Digest, byte[], int, int)
+     */
+    public static String getFingerPrint(Digest d, byte[] buf, int offset, int len) throws Exception {
+        if (len <= 0) {
+            return null;
+        }
+
+        byte[] data = getRawFingerprint(d, buf, offset, len);
+        String algo = d.getAlgorithm();
+        if (BuiltinDigests.md5.getAlgorithm().equals(algo)) {
+            return algo + ":" + BufferUtils.toHex(':', data).toLowerCase();
+        }
+
+        Base64.Encoder encoder = Base64.getEncoder();
+        return algo.replace("-", "").toUpperCase() + ":" + encoder.encodeToString(data).replaceAll("=", "");
+    }
+
+    public static byte[] getRawFingerprint(Digest d, byte... buf) throws Exception {
+        return getRawFingerprint(d, buf, 0, NumberUtils.length(buf));
+    }
+
+    public static byte[] getRawFingerprint(Digest d, byte[] buf, int offset, int len) throws Exception {
+        if (len <= 0) {
+            return null;
+        }
+
+        Objects.requireNonNull(d, "No digest").init();
+        d.update(buf, offset, len);
+
+        return d.digest();
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/digest/package.html
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/digest/package.html b/sshd-common/src/main/java/org/apache/sshd/common/digest/package.html
new file mode 100644
index 0000000..65940e0
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/digest/package.html
@@ -0,0 +1,25 @@
+<!--
+    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.
+-->
+<html>
+<head>
+</head>
+<body>
+
+<a href="{@docRoot}/org/apache/sshd/common/digest/Digest.html"><code>Digest</code></a> implementations.
+
+</body>
+</html>

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/future/AbstractSshFuture.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/future/AbstractSshFuture.java b/sshd-common/src/main/java/org/apache/sshd/common/future/AbstractSshFuture.java
new file mode 100644
index 0000000..1dca54c
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/future/AbstractSshFuture.java
@@ -0,0 +1,194 @@
+/*
+ * 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.future;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.io.StreamCorruptedException;
+import java.util.function.Function;
+
+import org.apache.sshd.common.SshException;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
+
+/**
+ * @param <T> Type of future
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractSshFuture<T extends SshFuture> extends AbstractLoggingBean implements SshFuture<T> {
+    /**
+     * A default value to indicate the future has been canceled
+     */
+    protected static final Object CANCELED = new Object();
+
+    protected final boolean debugEnabled;
+    protected final boolean traceEnabled;
+    private final Object id;
+
+    /**
+     * @param id Some identifier useful as {@link #toString()} value
+     */
+    protected AbstractSshFuture(Object id) {
+        this.id = id;
+        this.debugEnabled = log.isDebugEnabled();
+        this.traceEnabled = log.isTraceEnabled();
+    }
+
+    @Override
+    public Object getId() {
+        return id;
+    }
+
+    @Override
+    public boolean await(long timeoutMillis) throws IOException {
+        return await0(timeoutMillis, true) != null;
+    }
+
+    @Override
+    public boolean awaitUninterruptibly(long timeoutMillis) {
+        try {
+            return await0(timeoutMillis, false) != null;
+        } catch (InterruptedIOException e) {
+            throw formatExceptionMessage(msg -> new InternalError(msg, e),
+                    "Unexpected interrupted exception wile awaitUninterruptibly %d msec: %s",
+                    timeoutMillis, e.getMessage());
+        }
+    }
+
+    /**
+     * <P>Waits (interruptible) for the specified timeout (msec.) and then checks
+     * the result:</P>
+     * <UL>
+     *      <LI><P>
+     *      If result is {@code null} then timeout is assumed to have expired - throw
+     *      an appropriate {@link IOException}
+     *      </P></LI>
+     *
+     *      <LI><P>
+     *      If the result is of the expected type, then cast and return it
+     *      </P></LI>
+     *
+     *      <LI><P>
+     *      If the result is an {@link IOException} then re-throw it
+     *      </P></LI>
+     *
+     *      <LI><P>
+     *      If the result is a {@link Throwable} then throw an {@link IOException}
+     *      whose cause is the original exception
+     *      </P></LI>
+     *
+     *      <LI><P>
+     *      Otherwise (should never happen), throw a {@link StreamCorruptedException}
+     *      with the name of the result type
+     *      </P></LI>
+     * </UL>
+     *
+     * @param <R>          The generic result type
+     * @param expectedType The expected result type
+     * @param timeout      The timeout (millis) to wait for a result
+     * @return The (never {@code null}) result
+     * @throws IOException If failed to retrieve the expected result on time
+     */
+    protected <R> R verifyResult(Class<? extends R> expectedType, long timeout) throws IOException {
+        Object value = await0(timeout, true);
+        if (value == null) {
+            throw formatExceptionMessage(SshException::new, "Failed to get operation result within specified timeout: %s", timeout);
+        }
+
+        Class<?> actualType = value.getClass();
+        if (expectedType.isAssignableFrom(actualType)) {
+            return expectedType.cast(value);
+        }
+
+        if (Throwable.class.isAssignableFrom(actualType)) {
+            Throwable t = GenericUtils.peelException((Throwable) value);
+            if (t != value) {
+                value = t;
+                actualType = value.getClass();
+            }
+
+            if (IOException.class.isAssignableFrom(actualType)) {
+                throw (IOException) value;
+            }
+
+            Throwable cause = GenericUtils.resolveExceptionCause(t);
+            throw formatExceptionMessage(msg -> new SshException(msg, cause), "Failed (%s) to execute: %s", t.getClass().getSimpleName(), t.getMessage());
+        } else {    // what else can it be ????
+            throw formatExceptionMessage(StreamCorruptedException::new, "Unknown result type: %s", actualType.getName());
+        }
+    }
+
+    /**
+     * Wait for the Future to be ready. If the requested delay is 0 or
+     * negative, this method returns immediately.
+     *
+     * @param timeoutMillis The delay we will wait for the Future to be ready
+     * @param interruptable Tells if the wait can be interrupted or not.
+     * If {@code true} and the thread is interrupted then an {@link InterruptedIOException}
+     * is thrown.
+     * @return The non-{@code null} result object if the Future is ready,
+     * {@code null} if the timeout expired and no result was received
+     * @throws InterruptedIOException If the thread has been interrupted when it's not allowed.
+     */
+    protected abstract Object await0(long timeoutMillis, boolean interruptable) throws InterruptedIOException;
+
+    @SuppressWarnings("unchecked")
+    protected SshFutureListener<T> asListener(Object o) {
+        return (SshFutureListener<T>) o;
+    }
+
+    protected void notifyListener(SshFutureListener<T> l) {
+        try {
+            l.operationComplete(asT());
+        } catch (Throwable t) {
+            log.warn("notifyListener({}) failed ({}) to invoke {}: {}",
+                     this, t.getClass().getSimpleName(), l, t.getMessage());
+            if (debugEnabled) {
+                log.debug("notifyListener(" + this + ")[" + l + "] invocation failure details", t);
+            }
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    protected T asT() {
+        return (T) this;
+    }
+
+    /**
+     * Generates an exception whose message is prefixed by the future simple class name + {@link #getId() identifier}
+     * as a hint to the context of the failure.
+     *
+     * @param <E> Type of {@link Throwable} being generated
+     * @param exceptionCreator The exception creator from the formatted message
+     * @param format The extra payload format as per {@link String#format(String, Object...)}
+     * @param args The formatting arguments
+     * @return The generated exception
+     */
+    protected <E extends Throwable> E formatExceptionMessage(Function<? super String, ? extends E> exceptionCreator, String format, Object... args) {
+        String messagePayload = String.format(format, args);
+        String excMessage = getClass().getSimpleName() + "[" + getId() + "]: " + messagePayload;
+        return exceptionCreator.apply(excMessage);
+    }
+
+    @Override
+    public String toString() {
+        return getClass().getSimpleName() + "[id=" + getId() + "]";
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/future/CloseFuture.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/future/CloseFuture.java b/sshd-common/src/main/java/org/apache/sshd/common/future/CloseFuture.java
new file mode 100644
index 0000000..808716d
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/future/CloseFuture.java
@@ -0,0 +1,40 @@
+/*
+ * 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.future;
+
+/**
+ * An {@link SshFuture} for asynchronous close requests.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface CloseFuture extends SshFuture<CloseFuture> {
+
+    /**
+     * @return <tt>true</tt> if the close request is finished and the target is closed.
+     */
+    boolean isClosed();
+
+    /**
+     * Marks this future as closed and notifies all threads waiting for this
+     * future.  This method is invoked by SSHD internally.  Please do not call
+     * this method directly.
+     */
+    void setClosed();
+
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/future/DefaultCloseFuture.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/future/DefaultCloseFuture.java b/sshd-common/src/main/java/org/apache/sshd/common/future/DefaultCloseFuture.java
new file mode 100644
index 0000000..4c34a06
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/future/DefaultCloseFuture.java
@@ -0,0 +1,52 @@
+/*
+ * 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.future;
+
+/**
+ * A default implementation of {@link CloseFuture}.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class DefaultCloseFuture extends DefaultSshFuture<CloseFuture> implements CloseFuture {
+
+    /**
+     * Create a new instance
+     *
+     * @param id Some identifier useful as {@link #toString()} value
+     * @param lock A synchronization object for locking access - if {@code null}
+     * then synchronization occurs on {@code this} instance
+     */
+    public DefaultCloseFuture(Object id, Object lock) {
+        super(id, lock);
+    }
+
+    @Override
+    public boolean isClosed() {
+        if (isDone()) {
+            return (Boolean) getValue();
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public void setClosed() {
+        setValue(Boolean.TRUE);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/future/DefaultSshFuture.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/future/DefaultSshFuture.java b/sshd-common/src/main/java/org/apache/sshd/common/future/DefaultSshFuture.java
new file mode 100644
index 0000000..cd475ff
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/future/DefaultSshFuture.java
@@ -0,0 +1,236 @@
+/*
+ * 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.future;
+
+import java.io.InterruptedIOException;
+import java.lang.reflect.Array;
+import java.util.Objects;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * A default implementation of {@link SshFuture}.
+ *
+ * @param <T> Type of future
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class DefaultSshFuture<T extends SshFuture> extends AbstractSshFuture<T> {
+    /**
+     * A lock used by the wait() method
+     */
+    private final Object lock;
+    private Object listeners;
+    private Object result;
+
+    /**
+     * Creates a new instance.
+     *
+     * @param id Some identifier useful as {@link #toString()} value
+     * @param lock A synchronization object for locking access - if {@code null}
+     * then synchronization occurs on {@code this} instance
+     */
+    public DefaultSshFuture(Object id, Object lock) {
+        super(id);
+
+        this.lock = (lock != null) ? lock : this;
+    }
+
+    @Override
+    protected Object await0(long timeoutMillis, boolean interruptable) throws InterruptedIOException {
+        ValidateUtils.checkTrue(timeoutMillis >= 0L, "Negative timeout N/A: %d", timeoutMillis);
+        long startTime = System.currentTimeMillis();
+        long curTime = startTime;
+        long endTime = ((Long.MAX_VALUE - timeoutMillis) < curTime) ? Long.MAX_VALUE : (curTime + timeoutMillis);
+
+        synchronized (lock) {
+            if ((result != null) || (timeoutMillis <= 0)) {
+                return result;
+            }
+
+            for (;;) {
+                try {
+                    lock.wait(endTime - curTime);
+                } catch (InterruptedException e) {
+                    if (interruptable) {
+                        curTime = System.currentTimeMillis();
+                        throw formatExceptionMessage(msg -> {
+                            InterruptedIOException exc = new InterruptedIOException(msg);
+                            exc.initCause(e);
+                            return exc;
+                        }, "Interrupted after %d msec.", curTime - startTime);
+                    }
+                }
+
+                curTime = System.currentTimeMillis();
+                if ((result != null) || (curTime >= endTime)) {
+                    return result;
+                }
+            }
+        }
+    }
+
+    @Override
+    public boolean isDone() {
+        synchronized (lock) {
+            return result != null;
+        }
+    }
+
+    /**
+     * Sets the result of the asynchronous operation, and mark it as finished.
+     *
+     * @param newValue The operation result
+     */
+    public void setValue(Object newValue) {
+        synchronized (lock) {
+            // Allow only once.
+            if (result != null) {
+                return;
+            }
+
+            result = (newValue != null) ? newValue : GenericUtils.NULL;
+            lock.notifyAll();
+        }
+
+        notifyListeners();
+    }
+
+    public int getNumRegisteredListeners() {
+        synchronized (lock) {
+            if (listeners == null) {
+                return 0;
+            } else if (listeners instanceof SshFutureListener) {
+                return 1;
+            } else {
+                int l = Array.getLength(listeners);
+                int count = 0;
+                for (int i = 0; i < l; i++) {
+                    if (Array.get(listeners, i) != null) {
+                        count++;
+                    }
+                }
+                return count;
+            }
+        }
+    }
+
+    /**
+     * @return The result of the asynchronous operation - or {@code null}
+     * if none set.
+     */
+    public Object getValue() {
+        synchronized (lock) {
+            return (result == GenericUtils.NULL) ? null : result;
+        }
+    }
+
+    @Override
+    public T addListener(SshFutureListener<T> listener) {
+        Objects.requireNonNull(listener, "Missing listener argument");
+        boolean notifyNow = false;
+        synchronized (lock) {
+            // if already have a result don't register the listener and invoke it directly
+            if (result != null) {
+                notifyNow = true;
+            } else if (listeners == null) {
+                listeners = listener;   // 1st listener ?
+            } else if (listeners instanceof SshFutureListener) {
+                listeners = new Object[]{listeners, listener};
+            } else {    // increase array of registered listeners
+                Object[] ol = (Object[]) listeners;
+                int l = ol.length;
+                Object[] nl = new Object[l + 1];
+                System.arraycopy(ol, 0, nl, 0, l);
+                nl[l] = listener;
+                listeners = nl;
+            }
+        }
+
+        if (notifyNow) {
+            notifyListener(listener);
+        }
+
+        return asT();
+    }
+
+    @Override
+    public T removeListener(SshFutureListener<T> listener) {
+        Objects.requireNonNull(listener, "No listener provided");
+
+        synchronized (lock) {
+            if (result != null) {
+                return asT();   // the train has already left the station...
+            }
+
+            if (listeners == null) {
+                return asT();   // no registered instances anyway
+            }
+
+            if (listeners == listener) {
+                listeners = null;   // the one and only
+            } else if (!(listeners instanceof SshFutureListener))  {
+                int l = Array.getLength(listeners);
+                for (int i = 0; i < l; i++) {
+                    if (Array.get(listeners, i) == listener) {
+                        Array.set(listeners, i, null);
+                        break;
+                    }
+                }
+            }
+        }
+
+        return asT();
+    }
+
+    protected void notifyListeners() {
+        /*
+         * There won't be any visibility problem or concurrent modification
+         * because result value is checked in both addListener and
+         * removeListener calls under lock. If the result is already set then
+         * both methods will not modify the internal listeners
+         */
+        if (listeners != null) {
+            if (listeners instanceof SshFutureListener) {
+                notifyListener(asListener(listeners));
+            } else {
+                int l = Array.getLength(listeners);
+                for (int i = 0; i < l; i++) {
+                    SshFutureListener<T> listener = asListener(Array.get(listeners, i));
+                    if (listener != null) {
+                        notifyListener(listener);
+                    }
+                }
+            }
+        }
+    }
+
+    public boolean isCanceled() {
+        return getValue() == CANCELED;
+    }
+
+    public void cancel() {
+        setValue(CANCELED);
+    }
+
+    @Override
+    public String toString() {
+        return super.toString() + "[value=" + result + "]";
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/future/DefaultVerifiableSshFuture.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/future/DefaultVerifiableSshFuture.java b/sshd-common/src/main/java/org/apache/sshd/common/future/DefaultVerifiableSshFuture.java
new file mode 100644
index 0000000..1b02fed
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/future/DefaultVerifiableSshFuture.java
@@ -0,0 +1,30 @@
+/*
+ * 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.future;
+
+/**
+ * @param <T> Type of future
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class DefaultVerifiableSshFuture<T extends SshFuture> extends DefaultSshFuture<T> implements VerifiableFuture<T> {
+    protected DefaultVerifiableSshFuture(Object id, Object lock) {
+        super(id, lock);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/future/SshFuture.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/future/SshFuture.java b/sshd-common/src/main/java/org/apache/sshd/common/future/SshFuture.java
new file mode 100644
index 0000000..b0d8e3c
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/future/SshFuture.java
@@ -0,0 +1,48 @@
+/*
+ * 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.future;
+
+/**
+ * Represents the completion of an asynchronous SSH operation on a given object
+ * (it may be an SSH session or an SSH channel).
+ * Can be listened for completion using a {@link SshFutureListener}.
+ *
+ * @param <T> Type of future
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface SshFuture<T extends SshFuture> extends WaitableFuture {
+    /**
+     * Adds an event <tt>listener</tt> which is notified when
+     * this future is completed. If the listener is added
+     * after the completion, the listener is directly notified.
+     *
+     * @param listener The {@link SshFutureListener} instance to add
+     * @return The future instance
+     */
+    T addListener(SshFutureListener<T> listener);
+
+    /**
+     * Removes an existing event <tt>listener</tt> so it won't be notified when
+     * the future is completed.
+     *
+     * @param listener The {@link SshFutureListener} instance to remove
+     * @return The future instance
+     */
+    T removeListener(SshFutureListener<T> listener);
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/future/SshFutureListener.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/future/SshFutureListener.java b/sshd-common/src/main/java/org/apache/sshd/common/future/SshFutureListener.java
new file mode 100644
index 0000000..a005c0f
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/future/SshFutureListener.java
@@ -0,0 +1,42 @@
+/*
+ * 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.future;
+
+import org.apache.sshd.common.util.SshdEventListener;
+
+/**
+ * Something interested in being notified when the completion
+ * of an asynchronous SSH operation : {@link SshFuture}.
+ *
+ * @param <T> type of future
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@SuppressWarnings("rawtypes")
+@FunctionalInterface
+public interface SshFutureListener<T extends SshFuture> extends SshdEventListener {
+
+    /**
+     * Invoked when the operation associated with the {@link SshFuture}
+     * has been completed even if you add the listener after the completion.
+     *
+     * @param future The source {@link SshFuture} which called this
+     *               callback.
+     */
+    void operationComplete(T future);
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/future/VerifiableFuture.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/future/VerifiableFuture.java b/sshd-common/src/main/java/org/apache/sshd/common/future/VerifiableFuture.java
new file mode 100644
index 0000000..e318de7
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/future/VerifiableFuture.java
@@ -0,0 +1,68 @@
+/*
+ * 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.future;
+
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Represents an asynchronous operation whose successful result can be
+ * verified somehow. The contract guarantees that if the {@code verifyXXX}
+ * method returns without an exception then the operation was completed
+ * <U>successfully</U>
+ *
+ * @param <T> Type of verification result
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FunctionalInterface
+public interface VerifiableFuture<T> {
+    /**
+     * Wait {@link Long#MAX_VALUE} msec. and verify that the operation was successful
+     *
+     * @return The (same) future instance
+     * @throws IOException If failed to verify successfully on time
+     * @see #verify(long)
+     */
+    default T verify() throws IOException {
+        return verify(Long.MAX_VALUE);
+    }
+
+    /**
+     * Wait and verify that the operation was successful
+     *
+     * @param timeout The number of time units to wait
+     * @param unit    The wait {@link TimeUnit}
+     * @return The (same) future instance
+     * @throws IOException If failed to verify successfully on time
+     * @see #verify(long)
+     */
+    default T verify(long timeout, TimeUnit unit) throws IOException {
+        return verify(unit.toMillis(timeout));
+    }
+
+    /**
+     * Wait and verify that the operation was successful
+     *
+     * @param timeoutMillis Wait timeout in milliseconds
+     * @return The (same) future instance
+     * @throws IOException If failed to verify successfully on time
+     */
+    T verify(long timeoutMillis) throws IOException;
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/future/WaitableFuture.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/future/WaitableFuture.java b/sshd-common/src/main/java/org/apache/sshd/common/future/WaitableFuture.java
new file mode 100644
index 0000000..aff4adc
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/future/WaitableFuture.java
@@ -0,0 +1,118 @@
+/*
+ * 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.future;
+
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Represents an asynchronous operation which one can wait for its completion.
+ * <B>Note:</B> the only thing guaranteed is that if {@code true} is returned
+ * from one of the {@code awaitXXX} methods then the operation has completed.
+ * However, the <B>caller</B> has to determine whether it was a successful or
+ * failed completion.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface WaitableFuture {
+    /**
+     * @return Some identifier useful as {@link #toString()} value
+     */
+    Object getId();
+
+    /**
+     * Wait {@link Long#MAX_VALUE} msec. for the asynchronous operation to complete.
+     * The attached listeners will be notified when the operation is
+     * completed.
+     *
+     * @return {@code true} if the operation is completed.
+     * @throws IOException if failed - specifically {@link java.io.InterruptedIOException}
+     *                     if waiting was interrupted
+     * @see #await(long)
+     */
+    default boolean await() throws IOException {
+        return await(Long.MAX_VALUE);
+    }
+
+    /**
+     * Wait for the asynchronous operation to complete with the specified timeout.
+     *
+     * @param timeout   The number of time units to wait
+     * @param unit      The {@link TimeUnit} for waiting
+     * @return {@code true} if the operation is completed.
+     * @throws IOException if failed - specifically {@link java.io.InterruptedIOException}
+     *                     if waiting was interrupted
+     * @see #await(long)
+     */
+    default boolean await(long timeout, TimeUnit unit) throws IOException {
+        return await(unit.toMillis(timeout));
+    }
+
+    /**
+     * Wait for the asynchronous operation to complete with the specified timeout.
+     *
+     * @param timeoutMillis Wait time in milliseconds
+     * @return {@code true} if the operation is completed.
+     * @throws IOException if failed - specifically {@link java.io.InterruptedIOException}
+     *                     if waiting was interrupted
+     */
+    boolean await(long timeoutMillis) throws IOException;
+
+    /**
+     * Wait {@link Long#MAX_VALUE} msec. for the asynchronous operation to complete
+     * uninterruptibly. The attached listeners will be notified when the operation is
+     * completed.
+     *
+     * @return {@code true} if the operation is completed.
+     * @see #awaitUninterruptibly(long)
+     */
+    default boolean awaitUninterruptibly() {
+        return awaitUninterruptibly(Long.MAX_VALUE);
+    }
+
+    /**
+     * Wait for the asynchronous operation to complete with the specified timeout
+     * uninterruptibly.
+     *
+     * @param timeout   The number of time units to wait
+     * @param unit      The {@link TimeUnit} for waiting
+     * @return {@code true} if the operation is completed.
+     * @see #awaitUninterruptibly(long)
+     */
+    default boolean awaitUninterruptibly(long timeout, TimeUnit unit) {
+        return awaitUninterruptibly(unit.toMillis(timeout));
+    }
+
+    /**
+     * Wait for the asynchronous operation to complete with the specified timeout
+     * uninterruptibly.
+     *
+     * @param timeoutMillis Wait time in milliseconds
+     * @return {@code true} if the operation is finished.
+     */
+    boolean awaitUninterruptibly(long timeoutMillis);
+
+    /**
+     * @return {@code true} if the asynchronous operation is completed. <B>Note:</B>
+     * it is up to the <B>caller</B> to determine whether it was a successful or
+     * failed completion.
+     */
+    boolean isDone();
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/AbstractKeyPairProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/AbstractKeyPairProvider.java b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/AbstractKeyPairProvider.java
new file mode 100644
index 0000000..077d199
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/AbstractKeyPairProvider.java
@@ -0,0 +1,32 @@
+/*
+ * 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.keyprovider;
+
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
+
+/**
+ * Provides a default implementation for some {@link KeyPairProvider} methods
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractKeyPairProvider extends AbstractLoggingBean implements KeyPairProvider {
+    protected AbstractKeyPairProvider() {
+        super();
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/AbstractResourceKeyPairProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/AbstractResourceKeyPairProvider.java b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/AbstractResourceKeyPairProvider.java
new file mode 100644
index 0000000..e4e941d
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/AbstractResourceKeyPairProvider.java
@@ -0,0 +1,234 @@
+/*
+ * 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.keyprovider;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.security.PublicKey;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * @param <R> Type of resource from which the {@link KeyPair} is generated
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractResourceKeyPairProvider<R> extends AbstractKeyPairProvider {
+    private FilePasswordProvider passwordFinder;
+    /*
+     * NOTE: the map is case insensitive even for Linux, as it is (very) bad
+     * practice to have 2 key files that differ from one another only in their
+     * case...
+     */
+    private final Map<String, KeyPair> cacheMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+
+    protected AbstractResourceKeyPairProvider() {
+        super();
+    }
+
+    public FilePasswordProvider getPasswordFinder() {
+        return passwordFinder;
+    }
+
+    public void setPasswordFinder(FilePasswordProvider passwordFinder) {
+        this.passwordFinder = passwordFinder;
+    }
+
+    /**
+     * Checks which of the new resources we already loaded and can keep the
+     * associated key pair
+     *
+     * @param resources The collection of new resources - can be {@code null}/empty
+     * in which case the cache is cleared
+     */
+    protected void resetCacheMap(Collection<?> resources) {
+        // if have any cached pairs then see what we can keep from previous load
+        Collection<String> toDelete = Collections.emptySet();
+        synchronized (cacheMap) {
+            if (cacheMap.size() <= 0) {
+                return; // already empty - nothing to keep
+            }
+
+            if (GenericUtils.isEmpty(resources)) {
+                cacheMap.clear();
+                return;
+            }
+
+            for (Object r : resources) {
+                String resourceKey = ValidateUtils.checkNotNullAndNotEmpty(Objects.toString(r, null), "No resource key value");
+                if (cacheMap.containsKey(resourceKey)) {
+                    continue;
+                }
+
+                if (toDelete.isEmpty()) {
+                    toDelete = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
+                }
+
+                if (!toDelete.add(resourceKey)) {
+                    continue;   // debug breakpoint
+                }
+            }
+
+            if (GenericUtils.size(toDelete) > 0) {
+                toDelete.forEach(cacheMap::remove);
+            }
+        }
+
+        if (log.isDebugEnabled()) {
+            log.debug("resetCacheMap(" + resources + ") removed previous cached keys for " + toDelete);
+        }
+    }
+
+    protected Iterable<KeyPair> loadKeys(final Collection<? extends R> resources) {
+        if (GenericUtils.isEmpty(resources)) {
+            return Collections.emptyList();
+        } else {
+            return () -> new KeyPairIterator(resources);
+        }
+    }
+
+    protected KeyPair doLoadKey(R resource) throws IOException, GeneralSecurityException {
+        String resourceKey = ValidateUtils.checkNotNullAndNotEmpty(Objects.toString(resource, null), "No resource string value");
+        KeyPair kp;
+        synchronized (cacheMap) {
+            // check if lucky enough to have already loaded this file
+            kp = cacheMap.get(resourceKey);
+        }
+
+        if (kp != null) {
+            if (log.isTraceEnabled()) {
+                PublicKey key = kp.getPublic();
+                log.trace("doLoadKey({}) use cached key {}-{}",
+                          resourceKey, KeyUtils.getKeyType(key), KeyUtils.getFingerPrint(key));
+            }
+            return kp;
+        }
+
+        kp = doLoadKey(resourceKey, resource, getPasswordFinder());
+        if (kp != null) {
+            boolean reusedKey;
+            synchronized (cacheMap) {
+                // if somebody else beat us to it, use the cached key - just in case file contents changed
+                reusedKey = cacheMap.containsKey(resourceKey);
+                if (reusedKey) {
+                    kp = cacheMap.get(resourceKey);
+                } else {
+                    cacheMap.put(resourceKey, kp);
+                }
+            }
+
+            if (log.isDebugEnabled()) {
+                PublicKey key = kp.getPublic();
+                log.debug("doLoadKey({}) {} {}-{}",
+                          resourceKey, reusedKey ? "re-loaded" : "loaded",
+                          KeyUtils.getKeyType(key), KeyUtils.getFingerPrint(key));
+            }
+        } else {
+            if (log.isDebugEnabled()) {
+                log.debug("doLoadKey({}) no key loaded", resourceKey);
+            }
+        }
+
+        return kp;
+    }
+
+    protected KeyPair doLoadKey(String resourceKey, R resource, FilePasswordProvider provider) throws IOException, GeneralSecurityException {
+        try (InputStream inputStream = openKeyPairResource(resourceKey, resource)) {
+            return doLoadKey(resourceKey, inputStream, provider);
+        }
+    }
+
+    protected abstract InputStream openKeyPairResource(String resourceKey, R resource) throws IOException;
+
+    protected KeyPair doLoadKey(String resourceKey, InputStream inputStream, FilePasswordProvider provider)
+            throws IOException, GeneralSecurityException {
+        return SecurityUtils.loadKeyPairIdentity(resourceKey, inputStream, provider);
+    }
+
+    protected class KeyPairIterator implements Iterator<KeyPair> {
+        private final Iterator<? extends R> iterator;
+        private KeyPair nextKeyPair;
+        private boolean nextKeyPairSet;
+
+        protected KeyPairIterator(Collection<? extends R> resources) {
+            iterator = resources.iterator();
+        }
+
+        @Override
+        public boolean hasNext() {
+            return nextKeyPairSet || setNextObject();
+        }
+
+        @Override
+        public KeyPair next() {
+            if (!nextKeyPairSet) {
+                if (!setNextObject()) {
+                    throw new NoSuchElementException("Out of files to try");
+                }
+            }
+            nextKeyPairSet = false;
+            return nextKeyPair;
+        }
+
+        @Override
+        public void remove() {
+            throw new UnsupportedOperationException("loadKeys(files) Iterator#remove() N/A");
+        }
+
+        @SuppressWarnings("synthetic-access")
+        private boolean setNextObject() {
+            boolean debugEnabled = log.isDebugEnabled();
+            while (iterator.hasNext()) {
+                R r = iterator.next();
+                try {
+                    nextKeyPair = doLoadKey(r);
+                } catch (Throwable e) {
+                    log.warn("Failed (" + e.getClass().getSimpleName() + ")"
+                           + " to load key resource=" + r + ": " + e.getMessage());
+                    if (debugEnabled) {
+                        log.debug("Key resource=" + r + " load failure details", e);
+                    }
+                    nextKeyPair = null;
+                    continue;
+                }
+
+                if (nextKeyPair != null) {
+                    nextKeyPairSet = true;
+                    return true;
+                }
+            }
+
+            return false;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/ClassLoadableResourceKeyPairProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/ClassLoadableResourceKeyPairProvider.java b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/ClassLoadableResourceKeyPairProvider.java
new file mode 100644
index 0000000..6e8da54
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/ClassLoadableResourceKeyPairProvider.java
@@ -0,0 +1,113 @@
+/*
+ * 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.keyprovider;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StreamCorruptedException;
+import java.security.KeyPair;
+import java.util.Collection;
+import java.util.Collections;
+
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.threads.ThreadUtils;
+
+/**
+ * This provider loads private keys from the specified resources that
+ * are accessible via {@link ClassLoader#getResourceAsStream(String)}.
+ * If no loader configured via {@link #setResourceLoader(ClassLoader)}, then
+ * {@link ThreadUtils#resolveDefaultClassLoader(Class)} is used
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class ClassLoadableResourceKeyPairProvider extends AbstractResourceKeyPairProvider<String> {
+    private ClassLoader classLoader;
+    private Collection<String> resources;
+
+    public ClassLoadableResourceKeyPairProvider() {
+        this(Collections.emptyList());
+    }
+
+    public ClassLoadableResourceKeyPairProvider(ClassLoader cl) {
+        this(cl, Collections.emptyList());
+    }
+
+    public ClassLoadableResourceKeyPairProvider(String res) {
+        this(Collections.singletonList(ValidateUtils.checkNotNullAndNotEmpty(res, "No resource specified")));
+    }
+
+    public ClassLoadableResourceKeyPairProvider(ClassLoader cl, String res) {
+        this(cl, Collections.singletonList(ValidateUtils.checkNotNullAndNotEmpty(res, "No resource specified")));
+    }
+
+    public ClassLoadableResourceKeyPairProvider(Collection<String> resources) {
+        this.classLoader = ThreadUtils.resolveDefaultClassLoader(getClass());
+        this.resources = (resources == null) ? Collections.emptyList() : resources;
+    }
+
+    public ClassLoadableResourceKeyPairProvider(ClassLoader cl, Collection<String> resources) {
+        this.classLoader = cl;
+        this.resources = (resources == null) ? Collections.emptyList() : resources;
+    }
+
+    public Collection<String> getResources() {
+        return resources;
+    }
+
+    public void setResources(Collection<String> resources) {
+        this.resources = (resources == null) ? Collections.emptyList() : resources;
+    }
+
+    public ClassLoader getResourceLoader() {
+        return classLoader;
+    }
+
+    public void setResourceLoader(ClassLoader classLoader) {
+        this.classLoader = classLoader;
+    }
+
+    @Override
+    public Iterable<KeyPair> loadKeys() {
+        return loadKeys(getResources());
+    }
+
+    @Override
+    protected InputStream openKeyPairResource(String resourceKey, String resource) throws IOException {
+        ClassLoader cl = resolveClassLoader();
+        if (cl == null) {
+            throw new StreamCorruptedException("No resource loader for " + resource);
+        }
+
+        InputStream input = cl.getResourceAsStream(resource);
+        if (input == null) {
+            throw new FileNotFoundException("Cannot find resource " + resource);
+        }
+
+        return input;
+    }
+
+    protected ClassLoader resolveClassLoader() {
+        ClassLoader cl = getResourceLoader();
+        if (cl == null) {
+            cl = ThreadUtils.resolveDefaultClassLoader(getClass());
+        }
+        return cl;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/FileKeyPairProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/FileKeyPairProvider.java b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/FileKeyPairProvider.java
new file mode 100644
index 0000000..c4aae97
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/FileKeyPairProvider.java
@@ -0,0 +1,92 @@
+/*
+ * 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.keyprovider;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Objects;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.io.IoUtils;
+
+/**
+ * This host key provider loads private keys from the specified files. The
+ * loading is <U>lazy</U> - i.e., a file is not loaded until it is actually
+ * required. Once required though, its loaded {@link KeyPair} result is
+ * <U>cached</U> and not re-loaded.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class FileKeyPairProvider extends AbstractResourceKeyPairProvider<Path> {
+    private Collection<? extends Path> files;
+
+    public FileKeyPairProvider() {
+        super();
+    }
+
+    public FileKeyPairProvider(Path path) {
+        this(Collections.singletonList(Objects.requireNonNull(path, "No path provided")));
+    }
+
+    public FileKeyPairProvider(Path... files) {
+        this(Arrays.asList(files));
+    }
+
+    public FileKeyPairProvider(Collection<? extends Path> files) {
+        this.files = files;
+    }
+
+    public Collection<? extends Path> getPaths() {
+        return files;
+    }
+
+    public void setFiles(Collection<File> files) {
+        setPaths(GenericUtils.map(files, File::toPath));
+    }
+
+    public void setPaths(Collection<? extends Path> paths) {
+        // use absolute path in order to have unique cache keys
+        Collection<Path> resolved = GenericUtils.map(paths, Path::toAbsolutePath);
+        resetCacheMap(resolved);
+        files = resolved;
+    }
+
+    @Override
+    public Iterable<KeyPair> loadKeys() {
+        return loadKeys(getPaths());
+    }
+
+    @Override
+    protected KeyPair doLoadKey(Path resource) throws IOException, GeneralSecurityException {
+        return super.doLoadKey((resource == null) ? null : resource.toAbsolutePath());
+    }
+
+    @Override
+    protected InputStream openKeyPairResource(String resourceKey, Path resource) throws IOException {
+        return Files.newInputStream(resource, IoUtils.EMPTY_OPEN_OPTIONS);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyIdentityProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyIdentityProvider.java b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyIdentityProvider.java
new file mode 100644
index 0000000..d0b35d9
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyIdentityProvider.java
@@ -0,0 +1,170 @@
+/*
+ * 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.keyprovider;
+
+import java.security.KeyPair;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FunctionalInterface
+public interface KeyIdentityProvider {
+    /**
+     * An &quot;empty&quot; implementation of {@link KeyIdentityProvider} that
+     * returns an empty group of key pairs
+     */
+    KeyIdentityProvider EMPTY_KEYS_PROVIDER = new KeyIdentityProvider() {
+        @Override
+        public Iterable<KeyPair> loadKeys() {
+            return Collections.emptyList();
+        }
+
+        @Override
+        public String toString() {
+            return "EMPTY";
+        }
+    };
+
+    /**
+     * Invokes {@link KeyIdentityProvider#loadKeys()} and returns the result - ignores
+     * {@code null} providers (i.e., returns an empty iterable instance)
+     */
+    Function<KeyIdentityProvider, Iterable<KeyPair>> LOADER = p ->
+            (p == null) ? Collections.<KeyPair>emptyList() : p.loadKeys();
+
+    /**
+     * Load available keys.
+     *
+     * @return an {@link Iterable} instance of available keys - ignored if {@code null}
+     */
+    Iterable<KeyPair> loadKeys();
+
+    /**
+     * Creates a &quot;unified&quot; {@link Iterator} of {@link KeyPair}s out of 2 possible
+     * {@link KeyIdentityProvider}
+     *
+     * @param identities The registered keys identities
+     * @param keys Extra available key pairs
+     * @return The wrapping iterator
+     * @see #resolveKeyIdentityProvider(KeyIdentityProvider, KeyIdentityProvider)
+     */
+    static Iterator<KeyPair> iteratorOf(KeyIdentityProvider identities, KeyIdentityProvider keys) {
+        return iteratorOf(resolveKeyIdentityProvider(identities, keys));
+    }
+
+    /**
+     * Resolves a non-{@code null} iterator of the available keys
+     *
+     * @param provider The {@link KeyIdentityProvider} - ignored if {@code null}
+     * @return A non-{@code null} iterator - which may be empty if no provider or no keys
+     */
+    static Iterator<KeyPair> iteratorOf(KeyIdentityProvider provider) {
+        return GenericUtils.iteratorOf((provider == null) ? null : provider.loadKeys());
+    }
+
+    /**
+     * <P>Creates a &quot;unified&quot; {@link KeyIdentityProvider} out of 2 possible ones
+     * as follows:</P></BR>
+     * <UL>
+     *      <LI>If both are {@code null} then return {@code null}.</LI>
+     *      <LI>If either one is {@code null} then use the non-{@code null} one.</LI>
+     *      <LI>If both are the same instance then use it.</U>
+     *      <LI>Otherwise, returns a wrapper that groups both providers.</LI>
+     * </UL>
+     * @param identities The registered key pair identities
+     * @param keys The extra available key pairs
+     * @return The resolved provider
+     * @see #multiProvider(KeyIdentityProvider...)
+     */
+    static KeyIdentityProvider resolveKeyIdentityProvider(KeyIdentityProvider identities, KeyIdentityProvider keys) {
+        if ((keys == null) || (identities == keys)) {
+            return identities;
+        } else if (identities == null) {
+            return keys;
+        } else {
+            return multiProvider(identities, keys);
+        }
+    }
+
+    /**
+     * Wraps a group of {@link KeyIdentityProvider} into a single one
+     *
+     * @param providers The providers - ignored if {@code null}/empty (i.e., returns
+     * {@link #EMPTY_KEYS_PROVIDER})
+     * @return The wrapping provider
+     * @see #multiProvider(Collection)
+     */
+    static KeyIdentityProvider multiProvider(KeyIdentityProvider... providers) {
+        return multiProvider(GenericUtils.asList(providers));
+    }
+
+    /**
+     * Wraps a group of {@link KeyIdentityProvider} into a single one
+     *
+     * @param providers The providers - ignored if {@code null}/empty (i.e., returns
+     * {@link #EMPTY_KEYS_PROVIDER})
+     * @return The wrapping provider
+     */
+    static KeyIdentityProvider multiProvider(Collection<? extends KeyIdentityProvider> providers) {
+        return GenericUtils.isEmpty(providers) ? EMPTY_KEYS_PROVIDER : wrapKeyPairs(iterableOf(providers));
+    }
+
+    /**
+     * Wraps a group of {@link KeyIdentityProvider} into an {@link Iterable} of {@link KeyPair}s
+     *
+     * @param providers The group of providers - ignored if {@code null}/empty (i.e., returns an
+     * empty iterable instance)
+     * @return The wrapping iterable
+     */
+    static Iterable<KeyPair> iterableOf(Collection<? extends KeyIdentityProvider> providers) {
+        Iterable<Supplier<Iterable<KeyPair>>> keysSuppliers =
+                GenericUtils.<KeyIdentityProvider, Supplier<Iterable<KeyPair>>>wrapIterable(providers, p -> p::loadKeys);
+        return GenericUtils.multiIterableSuppliers(keysSuppliers);
+    }
+
+    /**
+     * Wraps a group of {@link KeyPair}s into a {@link KeyIdentityProvider}
+     *
+     * @param pairs The key pairs - ignored if {@code null}/empty (i.e., returns
+     * {@link #EMPTY_KEYS_PROVIDER}).
+     * @return The provider wrapper
+     */
+    static KeyIdentityProvider wrapKeyPairs(KeyPair... pairs) {
+        return wrapKeyPairs(GenericUtils.asList(pairs));
+    }
+
+    /**
+     * Wraps a group of {@link KeyPair}s into a {@link KeyIdentityProvider}
+     *
+     * @param pairs The key pairs {@link Iterable} - ignored if {@code null} (i.e., returns
+     * {@link #EMPTY_KEYS_PROVIDER}).
+     * @return The provider wrapper
+     */
+    static KeyIdentityProvider wrapKeyPairs(Iterable<KeyPair> pairs) {
+        return (pairs == null) ? EMPTY_KEYS_PROVIDER : () -> pairs;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProvider.java b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProvider.java
new file mode 100644
index 0000000..a816304
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProvider.java
@@ -0,0 +1,181 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.keyprovider;
+
+import java.security.KeyPair;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import org.apache.sshd.common.cipher.ECCurves;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * Provider for key pairs.  This provider is used on the server side to provide
+ * the host key, or on the client side to provide the user key.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface KeyPairProvider extends KeyIdentityProvider {
+
+    /**
+     * SSH identifier for RSA keys
+     */
+    String SSH_RSA = "ssh-rsa";
+
+    /**
+     * SSH identifier for DSA keys
+     */
+    String SSH_DSS = "ssh-dss";
+
+    /**
+     * SSH identifier for ED25519 elliptic curve keys
+     */
+    String SSH_ED25519 = "ssh-ed25519";
+
+    /**
+     * SSH identifier for EC keys in NIST curve P-256
+     */
+    String ECDSA_SHA2_NISTP256 = ECCurves.nistp256.getKeyType();
+
+    /**
+     * SSH identifier for EC keys in NIST curve P-384
+     */
+    String ECDSA_SHA2_NISTP384 = ECCurves.nistp384.getKeyType();
+
+    /**
+     * SSH identifier for EC keys in NIST curve P-521
+     */
+    String ECDSA_SHA2_NISTP521 = ECCurves.nistp521.getKeyType();
+
+    /**
+     * A {@link KeyPairProvider} that has no keys
+     */
+    KeyPairProvider EMPTY_KEYPAIR_PROVIDER =
+        new KeyPairProvider() {
+            @Override
+            public KeyPair loadKey(String type) {
+                return null;
+            }
+
+            @Override
+            public Iterable<String> getKeyTypes() {
+                return Collections.emptyList();
+            }
+
+            @Override
+            public Iterable<KeyPair> loadKeys() {
+                return Collections.emptyList();
+            }
+
+            @Override
+            public String toString() {
+                return "EMPTY_KEYPAIR_PROVIDER";
+            }
+        };
+
+    /**
+     * Load a key of the specified type which can be &quot;ssh-rsa&quot;, &quot;ssh-dss&quot;,
+     * or &quot;ecdsa-sha2-nistp{256,384,521}&quot;. If there is no key of this type, return
+     * {@code null}
+     *
+     * @param type the type of key to load
+     * @return a valid key pair or {@code null} if this type of key is not available
+     */
+    default KeyPair loadKey(String type) {
+        ValidateUtils.checkNotNullAndNotEmpty(type, "No key type to load");
+        return GenericUtils.stream(loadKeys())
+                .filter(key -> type.equals(KeyUtils.getKeyType(key)))
+                .findFirst()
+                .orElse(null);
+    }
+
+    /**
+     * @return The available {@link Iterable} key types in preferred order - never {@code null}
+     */
+    default Iterable<String> getKeyTypes() {
+        return GenericUtils.stream(loadKeys())
+                .map(KeyUtils::getKeyType)
+                .filter(GenericUtils::isNotEmpty)
+                .collect(Collectors.toSet());
+    }
+
+    /**
+     * Wrap the provided {@link KeyPair}s into a {@link KeyPairProvider}
+     *
+     * @param pairs The available pairs - ignored if {@code null}/empty (i.e.,
+     * returns {@link #EMPTY_KEYPAIR_PROVIDER})
+     * @return The provider wrapper
+     * @see #wrap(Iterable)
+     */
+    static KeyPairProvider wrap(KeyPair... pairs) {
+        return GenericUtils.isEmpty(pairs) ? EMPTY_KEYPAIR_PROVIDER : wrap(Arrays.asList(pairs));
+    }
+
+    /**
+     * Wrap the provided {@link KeyPair}s into a {@link KeyPairProvider}
+     *
+     * @param pairs The available pairs {@link Iterable} - ignored if {@code null} (i.e.,
+     * returns {@link #EMPTY_KEYPAIR_PROVIDER})
+     * @return The provider wrapper
+     */
+    static KeyPairProvider wrap(Iterable<KeyPair> pairs) {
+        return (pairs == null) ? EMPTY_KEYPAIR_PROVIDER : new KeyPairProvider() {
+            @Override
+            public Iterable<KeyPair> loadKeys() {
+                return pairs;
+            }
+
+            @Override
+            public KeyPair loadKey(String type) {
+                for (KeyPair kp : pairs) {
+                    String t = KeyUtils.getKeyType(kp);
+                    if (Objects.equals(type, t)) {
+                        return kp;
+                    }
+                }
+
+                return null;
+            }
+
+            @Override
+            public Iterable<String> getKeyTypes() {
+                // use a LinkedHashSet so as to preserve the order but avoid duplicates
+                Collection<String> types = new LinkedHashSet<>();
+                for (KeyPair kp : pairs) {
+                    String t = KeyUtils.getKeyType(kp);
+                    if (GenericUtils.isEmpty(t)) {
+                        continue;   // avoid unknown key types
+                    }
+
+                    if (!types.add(t)) {
+                        continue;   // debug breakpoint
+                    }
+                }
+
+                return types;
+            }
+        };
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProviderHolder.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProviderHolder.java b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProviderHolder.java
new file mode 100644
index 0000000..553d553
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProviderHolder.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.keyprovider;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface KeyPairProviderHolder {
+    /**
+     * Retrieve the <code>KeyPairProvider</code> that will be used to find
+     * the host key to use on the server side or the user key on the client side.
+     *
+     * @return the <code>KeyPairProvider</code>, never {@code null}
+     */
+    KeyPairProvider getKeyPairProvider();
+
+    void setKeyPairProvider(KeyPairProvider keyPairProvider);
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/MappedKeyPairProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/MappedKeyPairProvider.java b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/MappedKeyPairProvider.java
new file mode 100644
index 0000000..bf3ec8b
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/MappedKeyPairProvider.java
@@ -0,0 +1,97 @@
+/*
+ * 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.keyprovider;
+
+import java.security.KeyPair;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.function.Function;
+
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * Holds a {@link Map} of {@link String}-&gt;{@link KeyPair} where the map key
+ * is the type and value is the associated {@link KeyPair}
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class MappedKeyPairProvider implements KeyPairProvider {
+    /**
+     * Transforms a {@link Map} of {@link String}-&gt;{@link KeyPair} to a
+     * {@link KeyPairProvider} where map key is the type and value is the
+     * associated {@link KeyPair}
+     */
+    public static final Function<Map<String, KeyPair>, KeyPairProvider> MAP_TO_KEY_PAIR_PROVIDER =
+            MappedKeyPairProvider::new;
+
+    private final Map<String, KeyPair> pairsMap;
+
+    public MappedKeyPairProvider(KeyPair... pairs) {
+        this(GenericUtils.isEmpty(pairs) ? Collections.emptyList() : Arrays.asList(pairs));
+    }
+
+    public MappedKeyPairProvider(Collection<? extends KeyPair> pairs) {
+        this(mapUniquePairs(pairs));
+    }
+
+    public MappedKeyPairProvider(Map<String, KeyPair> pairsMap) {
+        this.pairsMap = ValidateUtils.checkNotNullAndNotEmpty(pairsMap, "No pairs map provided");
+    }
+
+    @Override
+    public Iterable<KeyPair> loadKeys() {
+        return pairsMap.values();
+    }
+
+    @Override
+    public KeyPair loadKey(String type) {
+        return pairsMap.get(type);
+    }
+
+    @Override
+    public Iterable<String> getKeyTypes() {
+        return pairsMap.keySet();
+    }
+
+    @Override
+    public String toString() {
+        return String.valueOf(getKeyTypes());
+    }
+
+    public static Map<String, KeyPair> mapUniquePairs(Collection<? extends KeyPair> pairs) {
+        if (GenericUtils.isEmpty(pairs)) {
+            return Collections.emptyMap();
+        }
+
+        Map<String, KeyPair> pairsMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+        for (KeyPair kp : pairs) {
+            String keyType = ValidateUtils.checkNotNullAndNotEmpty(KeyUtils.getKeyType(kp), "Cannot determine key type");
+            KeyPair prev = pairsMap.put(keyType, kp);
+            ValidateUtils.checkTrue(prev == null, "Multiple keys of type=%s", keyType);
+        }
+
+        return pairsMap;
+    }
+}


[24/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common utilities code from sshd-core into sshd-common (new artifact)

Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/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
deleted file mode 100644
index ae39dd3..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/cipher/ECCurves.java
+++ /dev/null
@@ -1,580 +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.cipher;
-
-import java.io.ByteArrayOutputStream;
-import java.io.EOFException;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.StreamCorruptedException;
-import java.math.BigInteger;
-import java.security.interfaces.ECKey;
-import java.security.spec.ECField;
-import java.security.spec.ECFieldFp;
-import java.security.spec.ECParameterSpec;
-import java.security.spec.ECPoint;
-import java.security.spec.EllipticCurve;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.EnumSet;
-import java.util.List;
-import java.util.NavigableSet;
-import java.util.Objects;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import org.apache.sshd.common.NamedResource;
-import org.apache.sshd.common.OptionalFeature;
-import org.apache.sshd.common.config.keys.KeyEntryResolver;
-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.NumberUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * Utilities for working with elliptic curves.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public enum ECCurves implements NamedResource, OptionalFeature {
-    nistp256(Constants.NISTP256, new int[]{1, 2, 840, 10045, 3, 1, 7},
-            new ECParameterSpec(
-                    new EllipticCurve(
-                            new ECFieldFp(new BigInteger("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF", 16)),
-                            new BigInteger("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC", 16),
-                            new BigInteger("5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", 16)),
-                    new ECPoint(
-                            new BigInteger("6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296", 16),
-                            new BigInteger("4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5", 16)),
-                    new BigInteger("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551", 16),
-                    1),
-            32,
-            BuiltinDigests.sha256),
-    nistp384(Constants.NISTP384, new int[]{1, 3, 132, 0, 34},
-            new ECParameterSpec(
-                    new EllipticCurve(
-                            new ECFieldFp(new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF", 16)),
-                            new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC", 16),
-                            new BigInteger("B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF", 16)),
-                    new ECPoint(
-                            new BigInteger("AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7", 16),
-                            new BigInteger("3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F", 16)),
-                    new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973", 16),
-                    1),
-            48,
-            BuiltinDigests.sha384),
-    nistp521(Constants.NISTP521, new int[]{1, 3, 132, 0, 35},
-            new ECParameterSpec(
-                    new EllipticCurve(
-                            new ECFieldFp(new BigInteger("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
-                                                          + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 16)),
-                            new BigInteger("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
-                                                + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC", 16),
-                            new BigInteger("0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951"
-                                            + "EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00", 16)),
-                    new ECPoint(
-                            new BigInteger("00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77"
-                                            + "EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66", 16),
-                            new BigInteger("011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE7299"
-                                            + "5EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650", 16)),
-                    new BigInteger("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B"
-                                    + "7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409", 16),
-                    1),
-            66,
-            BuiltinDigests.sha512);
-
-    /**
-     * A {@link Set} of all the known curves
-     */
-    public static final Set<ECCurves> VALUES =
-            Collections.unmodifiableSet(EnumSet.allOf(ECCurves.class));
-
-    /**
-     * A {@link Set} of all the known curves names
-     */
-    public static final NavigableSet<String> NAMES =
-            Collections.unmodifiableNavigableSet(GenericUtils.mapSort(
-                    VALUES,
-                    ECCurves::getName,
-                    String.CASE_INSENSITIVE_ORDER));
-
-    /**
-     * A {@link Set} of all the known curves key types
-     */
-    public static final NavigableSet<String> KEY_TYPES =
-            Collections.unmodifiableNavigableSet(GenericUtils.mapSort(
-                    VALUES,
-                    ECCurves::getKeyType,
-                    String.CASE_INSENSITIVE_ORDER));
-
-    public static final Comparator<ECCurves> BY_KEY_SIZE = (o1, o2) -> {
-        int k1 = (o1 == null) ? Integer.MAX_VALUE : o1.getKeySize();
-        int k2 = (o2 == null) ? Integer.MAX_VALUE : o2.getKeySize();
-        return Integer.compare(k1, k2);
-    };
-
-    public static final List<ECCurves> SORTED_KEY_SIZE =
-            Collections.unmodifiableList(VALUES.stream()
-                    .sorted(BY_KEY_SIZE)
-                    .collect(Collectors.toList()));
-
-    private final String name;
-    private final String keyType;
-    private final String oidString;
-    private final List<Integer> oidValue;
-    private final ECParameterSpec params;
-    private final int keySize;
-    private final int numOctets;
-    private final DigestFactory digestFactory;
-
-    ECCurves(String name, int[] oid, ECParameterSpec params, int numOctets, DigestFactory digestFactory) {
-        this.name = ValidateUtils.checkNotNullAndNotEmpty(name, "No curve name");
-        this.oidString = NumberUtils.join('.', ValidateUtils.checkNotNullAndNotEmpty(oid, "No OID"));
-        this.oidValue = Collections.unmodifiableList(NumberUtils.asList(oid));
-        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 = Objects.requireNonNull(digestFactory, "No digestFactory");
-    }
-
-    @Override   // The curve name
-    public final String getName() {
-        return name;
-    }
-
-    public final String getOID() {
-        return oidString;
-    }
-
-    public final List<Integer> getOIDValue() {
-        return oidValue;
-    }
-
-    /**
-     * @return The standard key type used to represent this curve
-     */
-    public final String getKeyType() {
-        return keyType;
-    }
-
-    @Override
-    public final boolean isSupported() {
-        return SecurityUtils.isECCSupported() && digestFactory.isSupported();
-    }
-
-    public final ECParameterSpec getParameters() {
-        return params;
-    }
-
-    /**
-     * @return The size (in bits) of the key
-     */
-    public final int getKeySize() {
-        return keySize;
-    }
-
-    /**
-     * @return The number of octets used to represent the point(s) for the curve
-     */
-    public final int getNumPointOctets() {
-        return numOctets;
-    }
-
-    /**
-     * @return The {@link Digest} to use when hashing the curve's parameters
-     */
-    public final Digest getDigestForParams() {
-        return digestFactory.create();
-    }
-
-    /**
-     * @param type The key type value - ignored if {@code null}/empty
-     * @return The matching {@link ECCurves} constant - {@code null} if
-     * no match found case <U>insensitive</U>
-     */
-    public static ECCurves fromKeyType(String type) {
-        if (GenericUtils.isEmpty(type)) {
-            return null;
-        }
-
-        for (ECCurves c : VALUES) {
-            if (type.equalsIgnoreCase(c.getKeyType())) {
-                return c;
-            }
-        }
-
-        return null;
-    }
-
-    /**
-     * @param name The curve name (case <U>insensitive</U> - ignored if
-     *             {@code null}/empty
-     * @return The matching {@link ECCurves} instance - {@code null} if no
-     * match found
-     */
-    public static ECCurves fromCurveName(String name) {
-        return NamedResource.findByName(name, String.CASE_INSENSITIVE_ORDER, VALUES);
-    }
-
-    /**
-     * @param key The {@link ECKey} - ignored if {@code null}
-     * @return The matching {@link ECCurves} instance - {@code null} if no
-     * match found
-     */
-    public static ECCurves fromECKey(ECKey key) {
-        return fromCurveParameters((key == null) ? null : key.getParams());
-    }
-
-    /**
-     * @param params The curve's {@link ECParameterSpec} - ignored if {@code null}
-     * @return The matching {@link ECCurves} value - {@code null} if no match found
-     * @see #getCurveSize(ECParameterSpec)
-     * @see #fromCurveSize(int)
-     */
-    public static ECCurves fromCurveParameters(ECParameterSpec params) {
-        if (params == null) {
-            return null;
-        } else {
-            return fromCurveSize(getCurveSize(params));
-        }
-    }
-
-    /**
-     * @param keySize The key size (in bits)
-     * @return The matching {@link ECCurves} value - {@code null} if no
-     * match found
-     */
-    public static ECCurves fromCurveSize(int keySize) {
-        if (keySize <= 0) {
-            return null;
-        }
-
-        for (ECCurves c : VALUES) {
-            if (keySize == c.getKeySize()) {
-                return c;
-            }
-        }
-
-        return null;
-    }
-
-    public static ECCurves fromOIDValue(List<? extends Number> oid) {
-        if (GenericUtils.isEmpty(oid)) {
-            return null;
-        }
-
-        for (ECCurves c : VALUES) {
-            List<? extends Number> v = c.getOIDValue();
-            if (oid.size() != v.size()) {
-                continue;
-            }
-
-            boolean matches = true;
-            for (int index = 0; index < v.size(); index++) {
-                Number exp = v.get(index);
-                Number act = oid.get(index);
-                if (exp.intValue() != act.intValue()) {
-                    matches = false;
-                    break;
-                }
-            }
-
-            if (matches) {
-                return c;
-            }
-        }
-
-        return null;
-    }
-
-    public static ECCurves fromOID(String oid) {
-        if (GenericUtils.isEmpty(oid)) {
-            return null;
-        }
-
-        for (ECCurves c : VALUES) {
-            if (oid.equalsIgnoreCase(c.getOID())) {
-                return c;
-            }
-        }
-
-        return null;
-    }
-
-    /**
-     * @param params The curve's {@link ECParameterSpec}
-     * @return The curve's key size in bits
-     * @throws IllegalArgumentException if invalid parameters provided
-     */
-    public static int getCurveSize(ECParameterSpec params) {
-        EllipticCurve curve = Objects.requireNonNull(params, "No EC params").getCurve();
-        ECField field = Objects.requireNonNull(curve, "No EC curve").getField();
-        return Objects.requireNonNull(field, "No EC field").getFieldSize();
-    }
-
-    public static byte[] encodeECPoint(ECPoint group, ECParameterSpec params) {
-        return encodeECPoint(group, params.getCurve());
-    }
-
-    public static byte[] encodeECPoint(ECPoint group, EllipticCurve curve) {
-        // M has len 2 ceil(log_2(q)/8) + 1 ?
-        int elementSize = (curve.getField().getFieldSize() + 7) / 8;
-        byte[] m = new byte[2 * elementSize + 1];
-
-        // Uncompressed format
-        m[0] = 0x04;
-
-        byte[] affineX = removeLeadingZeroes(group.getAffineX().toByteArray());
-        System.arraycopy(affineX, 0, m, 1 + elementSize - affineX.length, affineX.length);
-
-        byte[] affineY = removeLeadingZeroes(group.getAffineY().toByteArray());
-        System.arraycopy(affineY, 0, m, 1 + elementSize + elementSize - affineY.length, affineY.length);
-
-        return m;
-    }
-
-    private static byte[] removeLeadingZeroes(byte[] input) {
-        if (input[0] != 0x00) {
-            return input;
-        }
-
-        int pos = 1;
-        while (pos < input.length - 1 && input[pos] == 0x00) {
-            pos++;
-        }
-
-        byte[] output = new byte[input.length - pos];
-        System.arraycopy(input, pos, output, 0, output.length);
-        return output;
-    }
-
-    /**
-     * Converts the given octet string (defined by ASN.1 specifications) to a {@link BigInteger}
-     * As octet strings always represent positive integers, a zero-byte is prepended to
-     * the given array if necessary (if is MSB equal to 1), then this is converted to BigInteger
-     * The conversion is defined in the Section 2.3.8
-     *
-     * @param octets - octet string bytes to be converted
-     * @return The {@link BigInteger} representation of the octet string
-     */
-    public static BigInteger octetStringToInteger(byte... octets) {
-        if (octets == null) {
-            return null;
-        } else if (octets.length == 0) {
-            return BigInteger.ZERO;
-        } else {
-            return new BigInteger(1, octets);
-        }
-    }
-
-    public static ECPoint octetStringToEcPoint(byte... octets) {
-        if (NumberUtils.isEmpty(octets)) {
-            return null;
-        }
-
-        int startIndex = findFirstNonZeroIndex(octets);
-        if (startIndex < 0) {
-            throw new IllegalArgumentException("All zeroes ECPoint N/A");
-        }
-
-        byte indicator = octets[startIndex];
-        ECCurves.ECPointCompression compression = ECCurves.ECPointCompression.fromIndicatorValue(indicator);
-        if (compression == null) {
-            throw new UnsupportedOperationException("Unknown compression indicator value: 0x" + Integer.toHexString(indicator & 0xFF));
-        }
-
-        // The coordinates actually start after the compression indicator
-        return compression.octetStringToEcPoint(octets, startIndex + 1, octets.length - startIndex - 1);
-    }
-
-    private static int findFirstNonZeroIndex(byte... octets) {
-        if (NumberUtils.isEmpty(octets)) {
-            return -1;
-        }
-
-        for (int index = 0; index < octets.length; index++) {
-            if (octets[index] != 0) {
-                return index;
-            }
-        }
-
-        return -1;    // all zeroes
-    }
-
-    public static final class Constants {
-        /**
-         * Standard prefix of NISTP key types when encoded
-         */
-        public static final String ECDSA_SHA2_PREFIX = "ecdsa-sha2-";
-
-        public static final String NISTP256 = "nistp256";
-        public static final String NISTP384 = "nistp384";
-        public static final String NISTP521 = "nistp521";
-
-        private Constants() {
-            throw new UnsupportedOperationException("No instance allowed");
-        }
-    }
-
-    /**
-     * The various {@link ECPoint} representation compression indicators
-     *
-     * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
-     * @see <A HREF="https://www.ietf.org/rfc/rfc5480.txt">RFC-5480 - section 2.2</A>
-     */
-    public enum ECPointCompression {
-        // see http://tools.ietf.org/html/draft-jivsov-ecc-compact-00
-        // see http://crypto.stackexchange.com/questions/8914/ecdsa-compressed-public-key-point-back-to-uncompressed-public-key-point
-        VARIANT2((byte) 0x02) {
-            @Override
-            public ECPoint octetStringToEcPoint(byte[] octets, int startIndex, int len) {
-                byte[] xp = new byte[len];
-                System.arraycopy(octets, startIndex, xp, 0, len);
-                BigInteger x = octetStringToInteger(xp);
-
-                // TODO derive even Y...
-                throw new UnsupportedOperationException("octetStringToEcPoint(" + name() + ")(X=" + x + ") compression support N/A");
-            }
-        },
-        VARIANT3((byte) 0x03) {
-            @Override
-            public ECPoint octetStringToEcPoint(byte[] octets, int startIndex, int len) {
-                byte[] xp = new byte[len];
-                System.arraycopy(octets, startIndex, xp, 0, len);
-                BigInteger x = octetStringToInteger(xp);
-
-                // TODO derive odd Y...
-                throw new UnsupportedOperationException("octetStringToEcPoint(" + name() + ")(X=" + x + ") compression support N/A");
-            }
-        },
-        UNCOMPRESSED((byte) 0x04) {
-            @Override
-            public ECPoint octetStringToEcPoint(byte[] octets, int startIndex, int len) {
-                int numElements = len / 2;    /* x, y */
-                if (len != (numElements * 2)) {    // make sure length is not odd
-                    throw new IllegalArgumentException("octetStringToEcPoint(" + name() + ") "
-                            + " invalid remainder octets representation: "
-                            + " expected=" + (2 * numElements) + ", actual=" + len);
-                }
-
-                byte[] xp = new byte[numElements];
-                byte[] yp = new byte[numElements];
-                System.arraycopy(octets, startIndex, xp, 0, numElements);
-                System.arraycopy(octets, startIndex + numElements, yp, 0, numElements);
-
-                BigInteger x = octetStringToInteger(xp);
-                BigInteger y = octetStringToInteger(yp);
-                return new ECPoint(x, y);
-            }
-
-            @Override
-            public void writeECPoint(OutputStream s, String curveName, ECPoint p) throws IOException {
-                ECCurves curve = fromCurveName(curveName);
-                if (curve == null) {
-                    throw new StreamCorruptedException("writeECPoint(" + name() + ")[" + curveName + "] cannot determine octets count");
-                }
-
-                int numElements = curve.getNumPointOctets();
-                KeyEntryResolver.encodeInt(s, 1 /* the indicator */ + 2 * numElements);
-                s.write(getIndicatorValue());
-                writeCoordinate(s, "X", p.getAffineX(), numElements);
-                writeCoordinate(s, "Y", p.getAffineY(), numElements);
-            }
-        };
-
-        public static final Set<ECPointCompression> VALUES =
-                Collections.unmodifiableSet(EnumSet.allOf(ECPointCompression.class));
-
-        private final byte indicatorValue;
-
-        ECPointCompression(byte indicator) {
-            indicatorValue = indicator;
-        }
-
-        public final byte getIndicatorValue() {
-            return indicatorValue;
-        }
-
-        public abstract ECPoint octetStringToEcPoint(byte[] octets, int startIndex, int len);
-
-        public byte[] ecPointToOctetString(String curveName, ECPoint p) {
-            try (ByteArrayOutputStream baos = new ByteArrayOutputStream((2 * 66) + Long.SIZE)) {
-                writeECPoint(baos, curveName, p);
-                return baos.toByteArray();
-            } catch (IOException e) {
-                throw new RuntimeException("ecPointToOctetString(" + curveName + ")"
-                        + " failed (" + e.getClass().getSimpleName() + ")"
-                        + " to write data: " + e.getMessage(),
-                        e);
-            }
-        }
-
-        public void writeECPoint(OutputStream s, String curveName, ECPoint p) throws IOException {
-            if (s == null) {
-                throw new EOFException("No output stream");
-            }
-
-            throw new StreamCorruptedException("writeECPoint(" + name() + ")[" + p + "] N/A");
-        }
-
-        protected void writeCoordinate(OutputStream s, String n, BigInteger v, int numElements) throws IOException {
-            byte[] vp = v.toByteArray();
-            int startIndex = 0;
-            int vLen = vp.length;
-            if (vLen > numElements) {
-                if (vp[0] == 0) {   // skip artificial positive sign
-                    startIndex++;
-                    vLen--;
-                }
-            }
-
-            if (vLen > numElements) {
-                throw new StreamCorruptedException("writeCoordinate(" + name() + ")[" + n + "]"
-                        + " value length (" + vLen + ") exceeds max. (" + numElements + ")"
-                        + " for " + v);
-            }
-
-            if (vLen < numElements) {
-                byte[] tmp = new byte[numElements];
-                System.arraycopy(vp, startIndex, tmp, numElements - vLen, vLen);
-                vp = tmp;
-            }
-
-            s.write(vp, startIndex, vLen);
-        }
-
-        public static ECPointCompression fromIndicatorValue(int value) {
-            if ((value < 0) || (value > 0xFF)) {
-                return null;    // must be a byte value
-            }
-
-            for (ECPointCompression c : VALUES) {
-                if (value == c.getIndicatorValue()) {
-                    return c;
-                }
-            }
-
-            return null;
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/cipher/package.html
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/cipher/package.html b/sshd-core/src/main/java/org/apache/sshd/common/cipher/package.html
deleted file mode 100644
index 197a89d..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/cipher/package.html
+++ /dev/null
@@ -1,26 +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.
--->
-<html>
-<head>
-</head>
-<body>
-
-<a href="{@docRoot}/org/apache/sshd/common/cipher/Cipher.html"><code>Cipher</code></a>
-implementations.
-
-</body>
-</html>

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/compression/BaseCompression.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/compression/BaseCompression.java b/sshd-core/src/main/java/org/apache/sshd/common/compression/BaseCompression.java
deleted file mode 100644
index ff31947..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/compression/BaseCompression.java
+++ /dev/null
@@ -1,48 +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.compression;
-
-import org.apache.sshd.common.util.ValidateUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class BaseCompression implements Compression {
-    private final String name;
-
-    protected BaseCompression(String name) {
-        this.name = ValidateUtils.checkNotNullAndNotEmpty(name, "No compression name");
-    }
-
-    @Override
-    public final String getName() {
-        return name;
-    }
-
-    @Override
-    public boolean isCompressionExecuted() {
-        return true;
-    }
-
-    @Override
-    public String toString() {
-        return getName();
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/compression/BuiltinCompressions.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/compression/BuiltinCompressions.java b/sshd-core/src/main/java/org/apache/sshd/common/compression/BuiltinCompressions.java
deleted file mode 100644
index 49ad0ab..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/compression/BuiltinCompressions.java
+++ /dev/null
@@ -1,239 +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.compression;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.List;
-import java.util.Map;
-import java.util.NavigableSet;
-import java.util.Objects;
-import java.util.Set;
-import java.util.SortedSet;
-import java.util.TreeMap;
-
-import org.apache.sshd.common.NamedResource;
-import org.apache.sshd.common.config.NamedFactoriesListParseResult;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public enum BuiltinCompressions implements CompressionFactory {
-    none(Constants.NONE) {
-        @Override
-        public Compression create() {
-            return new CompressionNone();
-        }
-
-        @Override
-        public boolean isCompressionExecuted() {
-            return false;
-        }
-    },
-    zlib(Constants.ZLIB) {
-        @Override
-        public Compression create() {
-            return new CompressionZlib();
-        }
-    },
-    delayedZlib(Constants.DELAYED_ZLIB) {
-        @Override
-        public Compression create() {
-            return new CompressionDelayedZlib();
-        }
-
-        @Override
-        public boolean isDelayed() {
-            return true;
-        }
-    };
-
-    public static final Set<BuiltinCompressions> VALUES =
-            Collections.unmodifiableSet(EnumSet.allOf(BuiltinCompressions.class));
-
-    private static final Map<String, CompressionFactory> EXTENSIONS =
-            new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
-
-    private final String name;
-
-    BuiltinCompressions(String n) {
-        name = n;
-    }
-
-    @Override
-    public final String getName() {
-        return name;
-    }
-
-    @Override
-    public boolean isDelayed() {
-        return false;
-    }
-
-    @Override
-    public boolean isCompressionExecuted() {
-        return true;
-    }
-
-    @Override
-    public final String toString() {
-        return getName();
-    }
-
-    @Override
-    public final boolean isSupported() {
-        return true;
-    }
-
-    /**
-     * Registered a {@link org.apache.sshd.common.NamedFactory} to be available besides the built-in
-     * ones when parsing configuration
-     *
-     * @param extension The factory to register
-     * @throws IllegalArgumentException if factory instance is {@code null},
-     * or overrides a built-in one or overrides another registered factory
-     * with the same name (case <U>insensitive</U>).
-     */
-    public static void registerExtension(CompressionFactory extension) {
-        String name = Objects.requireNonNull(extension, "No extension provided").getName();
-        ValidateUtils.checkTrue(fromFactoryName(name) == null, "Extension overrides built-in: %s", name);
-
-        synchronized (EXTENSIONS) {
-            ValidateUtils.checkTrue(!EXTENSIONS.containsKey(name), "Extension overrides existing: %s", name);
-            EXTENSIONS.put(name, extension);
-        }
-    }
-
-    /**
-     * @return A {@link SortedSet} of the currently registered extensions, sorted
-     * according to the factory name (case <U>insensitive</U>)
-     */
-    public static NavigableSet<CompressionFactory> getRegisteredExtensions() {
-        synchronized (EXTENSIONS) {
-            return GenericUtils.asSortedSet(NamedResource.BY_NAME_COMPARATOR, EXTENSIONS.values());
-        }
-    }
-
-    /**
-     * Unregisters specified extension
-     *
-     * @param name The factory name - ignored if {@code null}/empty
-     * @return The registered extension - {@code null} if not found
-     */
-    public static CompressionFactory unregisterExtension(String name) {
-        if (GenericUtils.isEmpty(name)) {
-            return null;
-        }
-
-        synchronized (EXTENSIONS) {
-            return EXTENSIONS.remove(name);
-        }
-    }
-
-    public static BuiltinCompressions fromFactoryName(String name) {
-        return NamedResource.findByName(name, String.CASE_INSENSITIVE_ORDER, VALUES);
-    }
-
-    /**
-     * @param compressions A comma-separated list of Compressions' names - ignored
-     *                     if {@code null}/empty
-     * @return A {@link ParseResult} containing the successfully parsed
-     * factories and the unknown ones. <B>Note:</B> it is up to caller to
-     * ensure that the lists do not contain duplicates
-     */
-    public static ParseResult parseCompressionsList(String compressions) {
-        return parseCompressionsList(GenericUtils.split(compressions, ','));
-    }
-
-    public static ParseResult parseCompressionsList(String... compressions) {
-        return parseCompressionsList(GenericUtils.isEmpty((Object[]) compressions) ? Collections.emptyList() : Arrays.asList(compressions));
-    }
-
-    public static ParseResult parseCompressionsList(Collection<String> compressions) {
-        if (GenericUtils.isEmpty(compressions)) {
-            return ParseResult.EMPTY;
-        }
-
-        List<CompressionFactory> factories = new ArrayList<>(compressions.size());
-        List<String> unknown = Collections.emptyList();
-        for (String name : compressions) {
-            CompressionFactory c = resolveFactory(name);
-            if (c != null) {
-                factories.add(c);
-            } else {
-                // replace the (unmodifiable) empty list with a real one
-                if (unknown.isEmpty()) {
-                    unknown = new ArrayList<>();
-                }
-                unknown.add(name);
-            }
-        }
-
-        return new ParseResult(factories, unknown);
-    }
-
-    /**
-     * @param name The factory name
-     * @return The factory or {@code null} if it is neither a built-in one
-     * or a registered extension
-     */
-    public static CompressionFactory resolveFactory(String name) {
-        if (GenericUtils.isEmpty(name)) {
-            return null;
-        }
-
-        CompressionFactory c = fromFactoryName(name);
-        if (c != null) {
-            return c;
-        }
-
-        synchronized (EXTENSIONS) {
-            return EXTENSIONS.get(name);
-        }
-    }
-
-    /**
-     * Holds the result of {@link BuiltinCompressions#parseCompressionsList(String)}
-     *
-     * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
-     */
-    public static class ParseResult extends NamedFactoriesListParseResult<Compression, CompressionFactory> {
-        public static final ParseResult EMPTY = new ParseResult(Collections.emptyList(), Collections.emptyList());
-
-        public ParseResult(List<CompressionFactory> parsed, List<String> unsupported) {
-            super(parsed, unsupported);
-        }
-    }
-
-    public static final class Constants {
-        public static final String NONE = "none";
-        public static final String ZLIB = "zlib";
-        public static final String DELAYED_ZLIB = "zlib@openssh.com";
-
-        private Constants() {
-            throw new UnsupportedOperationException("No instance allowed");
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/compression/Compression.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/compression/Compression.java b/sshd-core/src/main/java/org/apache/sshd/common/compression/Compression.java
deleted file mode 100644
index 91ac27e..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/compression/Compression.java
+++ /dev/null
@@ -1,71 +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.compression;
-
-import java.io.IOException;
-
-import org.apache.sshd.common.util.buffer.Buffer;
-
-/**
- * Interface used to compress the stream of data between the
- * SSH server and clients.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface Compression extends CompressionInformation {
-
-    /**
-     * Enum identifying if this object will be used to compress
-     * or uncompress data.
-     */
-    enum Type {
-        Inflater,
-        Deflater
-    }
-
-    /**
-     * Initialize this object to either compress or uncompress data.
-     * This method must be called prior to any calls to either
-     * <code>compress</code> or <code>uncompress</code>.
-     * Once the object has been initialized, only one of
-     * <code>compress</code> or <code>uncompress</code> methods can be
-     * called.
-     *
-     * @param type  compression type
-     * @param level compression level
-     */
-    void init(Type type, int level);
-
-    /**
-     * Compress the given buffer in place.
-     *
-     * @param buffer the buffer containing the data to compress
-     * @throws IOException if an error occurs
-     */
-    void compress(Buffer buffer) throws IOException;
-
-    /**
-     * Uncompress the data in a buffer into another buffer.
-     *
-     * @param from the buffer containing the data to uncompress
-     * @param to   the buffer receiving the uncompressed data
-     * @throws IOException if an error occurs
-     */
-    void uncompress(Buffer from, Buffer to) throws IOException;
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionDelayedZlib.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionDelayedZlib.java b/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionDelayedZlib.java
deleted file mode 100644
index c062e38..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionDelayedZlib.java
+++ /dev/null
@@ -1,40 +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.compression;
-
-
-/**
- * ZLib delayed compression.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- * @see Compression#isDelayed()
- */
-public class CompressionDelayedZlib extends CompressionZlib {
-    /**
-     * Create a new instance of a delayed ZLib compression
-     */
-    public CompressionDelayedZlib() {
-        super(BuiltinCompressions.Constants.DELAYED_ZLIB);
-    }
-
-    @Override
-    public boolean isDelayed() {
-        return true;
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionFactory.java
deleted file mode 100644
index 355594a..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionFactory.java
+++ /dev/null
@@ -1,31 +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.compression;
-
-import org.apache.sshd.common.BuiltinFactory;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-// CHECKSTYLE:OFF
-public interface CompressionFactory extends BuiltinFactory<Compression>, CompressionInformation {
-    // nothing extra
-}
-//CHECKSTYLE:ON

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionInformation.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionInformation.java b/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionInformation.java
deleted file mode 100644
index 428689f..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionInformation.java
+++ /dev/null
@@ -1,42 +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.compression;
-
-import org.apache.sshd.common.NamedResource;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface CompressionInformation extends NamedResource {
-    /**
-     * Delayed compression is an Open-SSH specific feature which
-     * informs both the client and server to not compress data before
-     * the session has been authenticated.
-     *
-     * @return if the compression is delayed after authentication or not
-     */
-    boolean isDelayed();
-
-    /**
-     * @return {@code true} if there is any compression executed by
-     * this &quot;compressor&quot; - special case for 'none'
-     */
-    boolean isCompressionExecuted();
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionNone.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionNone.java b/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionNone.java
deleted file mode 100644
index 815e8ce..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionNone.java
+++ /dev/null
@@ -1,76 +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.compression;
-
-import java.io.IOException;
-import java.io.StreamCorruptedException;
-
-import org.apache.sshd.common.util.buffer.Buffer;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class CompressionNone extends BaseCompression {
-    private Type type;
-    private int level;
-
-    public CompressionNone() {
-        super(BuiltinCompressions.Constants.NONE);
-    }
-
-    @Override
-    public void init(Type type, int level) {
-        this.type = type;
-        this.level = level;
-    }
-
-    @Override
-    public boolean isCompressionExecuted() {
-        return false;
-    }
-
-    @Override
-    public void compress(Buffer buffer) throws IOException {
-        if (!Type.Deflater.equals(type)) {
-            throw new StreamCorruptedException("Not set up for compression: " + type);
-        }
-    }
-
-    @Override
-    public void uncompress(Buffer from, Buffer to) throws IOException {
-        if (!Type.Inflater.equals(type)) {
-            throw new StreamCorruptedException("Not set up for de-compression: " + type);
-        }
-
-        if (from != to) {
-            throw new StreamCorruptedException("Separate de-compression buffers provided");
-        }
-    }
-
-    @Override
-    public boolean isDelayed() {
-        return false;
-    }
-
-    @Override
-    public String toString() {
-        return super.toString() + "[" + type + "/" + level + "]";
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionZlib.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionZlib.java b/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionZlib.java
deleted file mode 100644
index e365b91..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionZlib.java
+++ /dev/null
@@ -1,85 +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.compression;
-
-import java.io.IOException;
-import java.util.zip.DataFormatException;
-import java.util.zip.Deflater;
-import java.util.zip.Inflater;
-
-import org.apache.sshd.common.util.buffer.Buffer;
-
-/**
- * ZLib based Compression.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class CompressionZlib extends BaseCompression {
-
-    private static final int BUF_SIZE = 4096;
-
-    private byte[] tmpbuf = new byte[BUF_SIZE];
-    private Deflater compresser;
-    private Inflater decompresser;
-
-    /**
-     * Create a new instance of a ZLib base compression
-     */
-    public CompressionZlib() {
-        this(BuiltinCompressions.Constants.ZLIB);
-    }
-
-    protected CompressionZlib(String name) {
-        super(name);
-    }
-
-    @Override
-    public boolean isDelayed() {
-        return false;
-    }
-
-    @Override
-    public void init(Type type, int level) {
-        compresser = new Deflater(level);
-        decompresser = new Inflater();
-    }
-
-    @Override
-    public void compress(Buffer buffer) throws IOException {
-        compresser.setInput(buffer.array(), buffer.rpos(), buffer.available());
-        buffer.wpos(buffer.rpos());
-        for (int len = compresser.deflate(tmpbuf, 0, tmpbuf.length, Deflater.SYNC_FLUSH);
-                len > 0;
-                len = compresser.deflate(tmpbuf, 0, tmpbuf.length, Deflater.SYNC_FLUSH)) {
-            buffer.putRawBytes(tmpbuf, 0, len);
-        }
-    }
-
-    @Override
-    public void uncompress(Buffer from, Buffer to) throws IOException {
-        decompresser.setInput(from.array(), from.rpos(), from.available());
-        try {
-            for (int len = decompresser.inflate(tmpbuf); len > 0; len = decompresser.inflate(tmpbuf)) {
-                to.putRawBytes(tmpbuf, 0, len);
-            }
-        } catch (DataFormatException e) {
-            throw new IOException("Error decompressing data", e);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/compression/package.html
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/compression/package.html b/sshd-core/src/main/java/org/apache/sshd/common/compression/package.html
deleted file mode 100644
index 9bd4652..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/compression/package.html
+++ /dev/null
@@ -1,25 +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.
--->
-<html>
-<head>
-</head>
-<body>
-
-<a href="{@docRoot}/org/apache/sshd/common/compression/Compression.html"><code>Compression</code></a> implementations.
-
-</body>
-</html>

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/CompressionConfigValue.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/CompressionConfigValue.java b/sshd-core/src/main/java/org/apache/sshd/common/config/CompressionConfigValue.java
deleted file mode 100644
index e153adc..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/CompressionConfigValue.java
+++ /dev/null
@@ -1,94 +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.config;
-
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.Set;
-
-import org.apache.sshd.common.compression.BuiltinCompressions;
-import org.apache.sshd.common.compression.Compression;
-import org.apache.sshd.common.compression.CompressionFactory;
-import org.apache.sshd.common.util.GenericUtils;
-
-/**
- * Provides a &quot;bridge&quot; between the configuration values and the
- * actual {@link org.apache.sshd.common.NamedFactory} for the {@link Compression}.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public enum CompressionConfigValue implements CompressionFactory {
-    YES(BuiltinCompressions.zlib),
-    NO(BuiltinCompressions.none),
-    DELAYED(BuiltinCompressions.delayedZlib);
-
-    public static final Set<CompressionConfigValue> VALUES =
-            Collections.unmodifiableSet(EnumSet.allOf(CompressionConfigValue.class));
-
-    private final CompressionFactory factory;
-
-    CompressionConfigValue(CompressionFactory delegate) {
-        factory = delegate;
-    }
-
-    @Override
-    public final String getName() {
-        return factory.getName();
-    }
-
-    @Override
-    public final Compression create() {
-        return factory.create();
-    }
-
-    @Override
-    public boolean isSupported() {
-        return factory.isSupported();
-    }
-
-    @Override
-    public final String toString() {
-        return getName();
-    }
-
-    @Override
-    public boolean isDelayed() {
-        return factory.isDelayed();
-    }
-
-    @Override
-    public boolean isCompressionExecuted() {
-        return factory.isCompressionExecuted();
-    }
-
-    public static CompressionConfigValue fromName(String n) {
-        if (GenericUtils.isEmpty(n)) {
-            return null;
-        }
-
-        for (CompressionConfigValue v : VALUES) {
-            if (n.equalsIgnoreCase(v.name())) {
-                return v;
-            }
-        }
-
-        return null;
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/FactoriesListParseResult.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/FactoriesListParseResult.java b/sshd-core/src/main/java/org/apache/sshd/common/config/FactoriesListParseResult.java
deleted file mode 100644
index 7a1cee1..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/FactoriesListParseResult.java
+++ /dev/null
@@ -1,51 +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.config;
-
-import java.util.List;
-
-import org.apache.sshd.common.Factory;
-
-/**
- * @param <T> Result type
- * @param <F> Factory type
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class FactoriesListParseResult<T, F extends Factory<T>> extends ListParseResult<F> {
-    protected FactoriesListParseResult(List<F> parsed, List<String> unsupported) {
-        super(parsed, unsupported);
-    }
-
-    /**
-     * @return The {@link List} of successfully parsed {@link Factory} instances
-     * in the <U>same order</U> as they were encountered during parsing
-     */
-    public final List<F> getParsedFactories() {
-        return getParsedValues();
-    }
-
-    /**
-     * @return A {@link List} of unknown/unsupported configuration values for
-     * the factories
-     */
-    public List<String> getUnsupportedFactories() {
-        return getUnsupportedValues();
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/ListParseResult.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/ListParseResult.java b/sshd-core/src/main/java/org/apache/sshd/common/config/ListParseResult.java
deleted file mode 100644
index 97590cb..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/ListParseResult.java
+++ /dev/null
@@ -1,66 +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.config;
-
-import java.util.List;
-
-import org.apache.sshd.common.util.GenericUtils;
-
-/**
- * Used to hold the result of parsing a list of value. Such result contains known
- * and unknown values - which are accessible via the respective {@link #getParsedValues()}
- * and {@link #getUnsupportedValues()} methods. <B>Note:</B> the returned {@link List}s may
- * be un-modifiable, so it is recommended to avoid attempting changing the, returned
- * list(s)
- *
- * @param <E> Type of list item
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class ListParseResult<E> {
-    private final List<E> parsed;
-    private final List<String> unsupported;
-
-    protected ListParseResult(List<E> parsed, List<String> unsupported) {
-        this.parsed = parsed;
-        this.unsupported = unsupported;
-    }
-
-    /**
-     * @return The {@link List} of successfully parsed value instances
-     * in the <U>same order</U> as they were encountered during parsing
-     */
-    public final List<E> getParsedValues() {
-        return parsed;
-    }
-
-    /**
-     * @return A {@link List} of unknown/unsupported configuration values for
-     * the factories
-     */
-    public List<String> getUnsupportedValues() {
-        return unsupported;
-    }
-
-    @Override
-    public String toString() {
-        return "parsed=" + GenericUtils.join(getParsedValues(), ',')
-                + ";unsupported=" + GenericUtils.join(getUnsupportedValues(), ',');
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/LogLevelValue.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/LogLevelValue.java b/sshd-core/src/main/java/org/apache/sshd/common/config/LogLevelValue.java
deleted file mode 100644
index 2fbdc80..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/LogLevelValue.java
+++ /dev/null
@@ -1,56 +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.config;
-
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.Set;
-
-import org.apache.sshd.common.util.GenericUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- * @see <A HREF="http://manpages.ubuntu.com/manpages/precise/en/man5/sshd_config.5.html"><I>LogLevel</I> configuration value</A>
- */
-public enum LogLevelValue {
-    /*
-     * NOTE(s):
-     * 1. DEBUG and DEBUG1 are EQUIVALENT
-     * 2. Order is important (!!!)
-     */
-    QUIET, FATAL, ERROR, INFO, VERBOSE, DEBUG, DEBUG1, DEBUG2, DEBUG3;
-
-    public static final Set<LogLevelValue> VALUES =
-            Collections.unmodifiableSet(EnumSet.allOf(LogLevelValue.class));
-
-    public static LogLevelValue fromName(String n) {
-        if (GenericUtils.isEmpty(n)) {
-            return null;
-        }
-
-        for (LogLevelValue l : VALUES) {
-            if (n.equalsIgnoreCase(l.name())) {
-                return l;
-            }
-        }
-
-        return null;
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/NamedFactoriesListParseResult.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/NamedFactoriesListParseResult.java b/sshd-core/src/main/java/org/apache/sshd/common/config/NamedFactoriesListParseResult.java
deleted file mode 100644
index 246cae0..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/NamedFactoriesListParseResult.java
+++ /dev/null
@@ -1,47 +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.config;
-
-import java.util.List;
-
-import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.NamedResource;
-import org.apache.sshd.common.util.GenericUtils;
-
-/**
- * Holds the result of parsing a list of {@link NamedFactory}ies
- *
- * @param <T> Result type
- * @param <F> Factory type
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class NamedFactoriesListParseResult<T, F extends NamedFactory<T>>
-        extends FactoriesListParseResult<T, F> {
-
-    protected NamedFactoriesListParseResult(List<F> parsed, List<String> unsupported) {
-        super(parsed, unsupported);
-    }
-
-    @Override
-    public String toString() {
-        return "parsed=" + NamedResource.getNames(getParsedFactories())
-                + ";unknown=" + GenericUtils.join(getUnsupportedFactories(), ',');
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/NamedResourceListParseResult.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/NamedResourceListParseResult.java b/sshd-core/src/main/java/org/apache/sshd/common/config/NamedResourceListParseResult.java
deleted file mode 100644
index feb45f4..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/NamedResourceListParseResult.java
+++ /dev/null
@@ -1,57 +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.config;
-
-import java.util.List;
-
-import org.apache.sshd.common.NamedResource;
-import org.apache.sshd.common.util.GenericUtils;
-
-/**
- * @param <R> Type of result {@link NamedResource}
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class NamedResourceListParseResult<R extends NamedResource> extends ListParseResult<R> {
-    protected NamedResourceListParseResult(List<R> parsed, List<String> unsupported) {
-        super(parsed, unsupported);
-    }
-
-    /**
-     * @return The {@link List} of successfully parsed {@link NamedResource} instances
-     * in the <U>same order</U> as they were encountered during parsing
-     */
-    public final List<R> getParsedResources() {
-        return getParsedValues();
-    }
-
-    /**
-     * @return A {@link List} of unknown/unsupported configuration values for
-     * the resources
-     */
-    public List<String> getUnsupportedResources() {
-        return getUnsupportedValues();
-    }
-
-    @Override
-    public String toString() {
-        return "parsed=" + NamedResource.getNames(getParsedResources())
-                + ";unknown=" + GenericUtils.join(getUnsupportedResources(), ',');
-    }
-}


[45/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common utilities code from sshd-core into sshd-common (new artifact)

Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/KeyUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/KeyUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/KeyUtils.java
new file mode 100644
index 0000000..23937c2
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/KeyUtils.java
@@ -0,0 +1,937 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.config.keys;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.Path;
+import java.nio.file.attribute.PosixFilePermission;
+import java.security.GeneralSecurityException;
+import java.security.Key;
+import java.security.KeyFactory;
+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.RSAPrivateCrtKey;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.DSAPublicKeySpec;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.RSAPublicKeySpec;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.sshd.common.Factory;
+import org.apache.sshd.common.cipher.ECCurves;
+import org.apache.sshd.common.config.keys.impl.DSSPublicKeyEntryDecoder;
+import org.apache.sshd.common.config.keys.impl.ECDSAPublicKeyEntryDecoder;
+import org.apache.sshd.common.config.keys.impl.RSAPublicKeyDecoder;
+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.digest.DigestUtils;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.OsUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
+import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * Utility class for keys
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class KeyUtils {
+    /**
+     * Name of algorithm for RSA keys to be used when calling security provider
+     */
+    public static final String RSA_ALGORITHM = "RSA";
+
+    /**
+     * The most commonly used RSA public key exponent
+     */
+    public static final BigInteger DEFAULT_RSA_PUBLIC_EXPONENT = new BigInteger("65537");
+
+    /**
+     * Name of algorithm for DSS keys to be used when calling security provider
+     */
+    public static final String DSS_ALGORITHM = "DSA";
+
+    /**
+     * Name of algorithm for EC keys to be used when calling security provider
+     */
+    public static final String EC_ALGORITHM = "EC";
+
+    /**
+     * The {@link Set} of {@link PosixFilePermission} <U>not</U> allowed if strict
+     * permissions are enforced on key files
+     */
+    public static final Set<PosixFilePermission> STRICTLY_PROHIBITED_FILE_PERMISSION =
+        Collections.unmodifiableSet(
+            EnumSet.of(PosixFilePermission.GROUP_READ, PosixFilePermission.GROUP_WRITE, PosixFilePermission.GROUP_EXECUTE,
+                PosixFilePermission.OTHERS_READ, PosixFilePermission.OTHERS_WRITE, PosixFilePermission.OTHERS_EXECUTE));
+
+    /**
+     * System property that can be used to control the default fingerprint factory used for keys.
+     * If not set the {@link #DEFAULT_FINGERPRINT_DIGEST_FACTORY} is used
+     */
+    public static final String KEY_FINGERPRINT_FACTORY_PROP = "org.apache.sshd.keyFingerprintFactory";
+
+    /**
+     * The default {@link Factory} of {@link Digest}s initialized
+     * as the value of {@link #getDefaultFingerPrintFactory()} if not
+     * overridden by {@link #KEY_FINGERPRINT_FACTORY_PROP} or
+     * {@link #setDefaultFingerPrintFactory(DigestFactory)}
+     */
+    public static final DigestFactory DEFAULT_FINGERPRINT_DIGEST_FACTORY = BuiltinDigests.sha256;
+
+    private static final AtomicReference<DigestFactory> DEFAULT_DIGEST_HOLDER = new AtomicReference<>();
+
+    private static final Map<String, PublicKeyEntryDecoder<?, ?>> BY_KEY_TYPE_DECODERS_MAP =
+            new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+
+    private static final Map<Class<?>, PublicKeyEntryDecoder<?, ?>> BY_KEY_CLASS_DECODERS_MAP =
+            new HashMap<>();
+
+    static {
+        registerPublicKeyEntryDecoder(RSAPublicKeyDecoder.INSTANCE);
+        registerPublicKeyEntryDecoder(DSSPublicKeyEntryDecoder.INSTANCE);
+
+        if (SecurityUtils.isECCSupported()) {
+            registerPublicKeyEntryDecoder(ECDSAPublicKeyEntryDecoder.INSTANCE);
+        }
+        if (SecurityUtils.isEDDSACurveSupported()) {
+            registerPublicKeyEntryDecoder(SecurityUtils.getEDDSAPublicKeyEntryDecoder());
+        }
+    }
+
+    private KeyUtils() {
+        throw new UnsupportedOperationException("No instance");
+    }
+
+    /**
+     * <P>Checks if a path has strict permissions</P>
+     * <UL>
+     * <LI><P>
+     * The path may not have {@link PosixFilePermission#OTHERS_EXECUTE}
+     * permission
+     * </P></LI>
+     *
+     * <LI><P>
+     * (For {@code Unix}) The path may not have group or others permissions
+     * </P></LI>
+     *
+     * <LI><P>
+     * (For {@code Unix}) If the path is a file, then its folder may not have
+     * group or others permissions
+     * </P></LI>
+     *
+     * <LI><P>
+     * The path must be owned by current user.
+     * </P></LI>
+     *
+     * <LI><P>
+     * (For {@code Unix}) The path may be owned by root.
+     * </P></LI>
+     *
+     * <LI><P>
+     * (For {@code Unix}) If the path is a file, then its folder must also
+     * have valid owner.
+     * </P></LI>
+     *
+     * </UL>
+     *
+     * @param path    The {@link Path} to be checked - ignored if {@code null}
+     *                or does not exist
+     * @param options The {@link LinkOption}s to use to query the file's permissions
+     * @return The violated permission as {@link SimpleImmutableEntry} where key is a message and
+     * value is the offending object {@link PosixFilePermission} or {@link String} for owner - {@code null}
+     * if no violations detected
+     * @throws IOException If failed to retrieve the permissions
+     * @see #STRICTLY_PROHIBITED_FILE_PERMISSION
+     */
+    public static SimpleImmutableEntry<String, Object> validateStrictKeyFilePermissions(Path path, LinkOption... options) throws IOException {
+        if ((path == null) || (!Files.exists(path, options))) {
+            return null;
+        }
+
+        Collection<PosixFilePermission> perms = IoUtils.getPermissions(path, options);
+        if (GenericUtils.isEmpty(perms)) {
+            return null;
+        }
+
+        if (perms.contains(PosixFilePermission.OTHERS_EXECUTE)) {
+            PosixFilePermission p = PosixFilePermission.OTHERS_EXECUTE;
+            return new SimpleImmutableEntry<>(String.format("Permissions violation (%s)", p), p);
+        }
+
+        if (OsUtils.isUNIX()) {
+            PosixFilePermission p = IoUtils.validateExcludedPermissions(perms, STRICTLY_PROHIBITED_FILE_PERMISSION);
+            if (p != null) {
+                return new SimpleImmutableEntry<>(String.format("Permissions violation (%s)", p), p);
+            }
+
+            if (Files.isRegularFile(path, options)) {
+                Path parent = path.getParent();
+                p = IoUtils.validateExcludedPermissions(IoUtils.getPermissions(parent, options), STRICTLY_PROHIBITED_FILE_PERMISSION);
+                if (p != null) {
+                    return new SimpleImmutableEntry<>(String.format("Parent permissions violation (%s)", p), p);
+                }
+            }
+        }
+
+        String owner = IoUtils.getFileOwner(path, options);
+        if (GenericUtils.isEmpty(owner)) {
+            // we cannot get owner
+            // general issue: jvm does not support permissions
+            // security issue: specific filesystem does not support permissions
+            return null;
+        }
+
+        String current = OsUtils.getCurrentUser();
+        Set<String> expected = new HashSet<>();
+        expected.add(current);
+        if (OsUtils.isUNIX()) {
+            // Windows "Administrator" was considered however in Windows most likely a group is used.
+            expected.add(OsUtils.ROOT_USER);
+        }
+
+        if (!expected.contains(owner)) {
+            return new SimpleImmutableEntry<>(String.format("Owner violation (%s)", owner), owner);
+        }
+
+        if (OsUtils.isUNIX()) {
+            if (Files.isRegularFile(path, options)) {
+                String parentOwner = IoUtils.getFileOwner(path.getParent(), options);
+                if ((!GenericUtils.isEmpty(parentOwner)) && (!expected.contains(parentOwner))) {
+                    return new SimpleImmutableEntry<>(String.format("Parent owner violation (%s)", parentOwner), parentOwner);
+                }
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * @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) {
+        Objects.requireNonNull(decoder, "No decoder specified");
+
+        Class<?> pubType = Objects.requireNonNull(decoder.getPublicKeyType(), "No public key type declared");
+        Class<?> prvType = Objects.requireNonNull(decoder.getPrivateKeyType(), "No private key type declared");
+        synchronized (BY_KEY_CLASS_DECODERS_MAP) {
+            BY_KEY_CLASS_DECODERS_MAP.put(pubType, decoder);
+            BY_KEY_CLASS_DECODERS_MAP.put(prvType, decoder);
+        }
+
+        Collection<String> names = ValidateUtils.checkNotNullAndNotEmpty(decoder.getSupportedTypeNames(), "No supported key type");
+        synchronized (BY_KEY_TYPE_DECODERS_MAP) {
+            for (String n : names) {
+                PublicKeyEntryDecoder<?, ?> prev = BY_KEY_TYPE_DECODERS_MAP.put(n, decoder);
+                if (prev != null) {
+                    //noinspection UnnecessaryContinue
+                    continue;   // debug breakpoint
+                }
+            }
+        }
+    }
+
+    /**
+     * @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<?, ?> getPublicKeyEntryDecoder(String keyType) {
+        if (GenericUtils.isEmpty(keyType)) {
+            return null;
+        }
+
+        synchronized (BY_KEY_TYPE_DECODERS_MAP) {
+            return BY_KEY_TYPE_DECODERS_MAP.get(keyType);
+        }
+    }
+
+    /**
+     * @param kp The {@link KeyPair} to examine - ignored if {@code null}
+     * @return The matching {@link PublicKeyEntryDecoder} provided <U>both</U>
+     * the public and private keys have the same decoder - {@code null} if no
+     * match found
+     * @see #getPublicKeyEntryDecoder(Key)
+     */
+    public static PublicKeyEntryDecoder<?, ?> getPublicKeyEntryDecoder(KeyPair kp) {
+        if (kp == null) {
+            return null;
+        }
+
+        PublicKeyEntryDecoder<?, ?> d1 = getPublicKeyEntryDecoder(kp.getPublic());
+        PublicKeyEntryDecoder<?, ?> d2 = getPublicKeyEntryDecoder(kp.getPrivate());
+        if (d1 == d2) {
+            return d1;
+        } else {
+            return null;    // some kind of mixed keys...
+        }
+    }
+
+    /**
+     * @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<?, ?> getPublicKeyEntryDecoder(Key key) {
+        if (key == null) {
+            return null;
+        } else {
+            return getPublicKeyEntryDecoder(key.getClass());
+        }
+    }
+
+    /**
+     * @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<?, ?> getPublicKeyEntryDecoder(Class<?> keyType) {
+        if ((keyType == null) || (!Key.class.isAssignableFrom(keyType))) {
+            return null;
+        }
+
+        synchronized (BY_KEY_TYPE_DECODERS_MAP) {
+            PublicKeyEntryDecoder<?, ?> decoder = BY_KEY_CLASS_DECODERS_MAP.get(keyType);
+            if (decoder != null) {
+                return decoder;
+            }
+
+            // in case it is a derived class
+            for (PublicKeyEntryDecoder<?, ?> dec : BY_KEY_CLASS_DECODERS_MAP.values()) {
+                Class<?> pubType = dec.getPublicKeyType();
+                Class<?> prvType = dec.getPrivateKeyType();
+                if (pubType.isAssignableFrom(keyType) || prvType.isAssignableFrom(keyType)) {
+                    return dec;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * @return The default {@link DigestFactory}
+     * by the {@link #getFingerPrint(PublicKey)} and {@link #getFingerPrint(String)}
+     * methods
+     * @see #KEY_FINGERPRINT_FACTORY_PROP
+     * @see #setDefaultFingerPrintFactory(DigestFactory)
+     */
+    public static DigestFactory getDefaultFingerPrintFactory() {
+        DigestFactory factory = null;
+        synchronized (DEFAULT_DIGEST_HOLDER) {
+            factory = DEFAULT_DIGEST_HOLDER.get();
+            if (factory != null) {
+                return factory;
+            }
+
+            String propVal = System.getProperty(KEY_FINGERPRINT_FACTORY_PROP);
+            if (GenericUtils.isEmpty(propVal)) {
+                factory = DEFAULT_FINGERPRINT_DIGEST_FACTORY;
+            } else {
+                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);
+        }
+
+        return factory;
+    }
+
+    /**
+     * @param f The {@link DigestFactory} of {@link Digest}s to be used - may
+     *          not be {@code null}
+     */
+    public static void setDefaultFingerPrintFactory(DigestFactory f) {
+        synchronized (DEFAULT_DIGEST_HOLDER) {
+            DEFAULT_DIGEST_HOLDER.set(Objects.requireNonNull(f, "No digest factory"));
+        }
+    }
+
+    /**
+     * @param key the public key - ignored if {@code null}
+     * @return the fingerprint or {@code null} if no key.
+     * <B>Note:</B> if exception encountered then returns the exception's simple class name
+     * @see #getFingerPrint(Factory, PublicKey)
+     */
+    public static String getFingerPrint(PublicKey key) {
+        return getFingerPrint(getDefaultFingerPrintFactory(), key);
+    }
+
+    /**
+     * @param password The {@link String} to digest - ignored if {@code null}/empty,
+     *                 otherwise its UTF-8 representation is used as input for the fingerprint
+     * @return The fingerprint - {@code null} if {@code null}/empty input.
+     * <B>Note:</B> if exception encountered then returns the exception's simple class name
+     * @see #getFingerPrint(String, Charset)
+     */
+    public static String getFingerPrint(String password) {
+        return getFingerPrint(password, StandardCharsets.UTF_8);
+    }
+
+    /**
+     * @param password The {@link String} to digest - ignored if {@code null}/empty
+     * @param charset  The {@link Charset} to use in order to convert the
+     *                 string to its byte representation to use as input for the fingerprint
+     * @return The fingerprint - {@code null} if {@code null}/empty input.
+     * <B>Note:</B> if exception encountered then returns the exception's simple class name
+     * @see #getFingerPrint(Factory, String, Charset)
+     * @see #getDefaultFingerPrintFactory()
+     */
+    public static String getFingerPrint(String password, Charset charset) {
+        return getFingerPrint(getDefaultFingerPrintFactory(), password, charset);
+    }
+
+    /**
+     * @param f   The {@link Factory} to create the {@link Digest} to use
+     * @param key the public key - ignored if {@code null}
+     * @return the fingerprint or {@code null} if no key.
+     * <B>Note:</B> if exception encountered then returns the exception's simple class name
+     * @see #getFingerPrint(Digest, PublicKey)
+     */
+    public static String getFingerPrint(Factory<? extends Digest> f, PublicKey key) {
+        return (key == null) ? null : getFingerPrint(Objects.requireNonNull(f, "No digest factory").create(), key);
+    }
+
+    /**
+     * @param d   The {@link Digest} to use
+     * @param key the public key - ignored if {@code null}
+     * @return the fingerprint or {@code null} if no key.
+     * <B>Note:</B> if exception encountered then returns the exception's simple class name
+     * @see DigestUtils#getFingerPrint(Digest, byte[], int, int)
+     */
+    public static String getFingerPrint(Digest d, PublicKey key) {
+        if (key == null) {
+            return null;
+        }
+
+        try {
+            Buffer buffer = new ByteArrayBuffer();
+            buffer.putRawPublicKey(key);
+            return DigestUtils.getFingerPrint(d, buffer.array(), 0, buffer.wpos());
+        } catch (Exception e) {
+            return e.getClass().getSimpleName();
+        }
+    }
+
+    public static byte[] getRawFingerprint(PublicKey key) throws Exception {
+        return getRawFingerprint(getDefaultFingerPrintFactory(), key);
+    }
+
+    public static byte[] getRawFingerprint(Factory<? extends Digest> f, PublicKey key) throws Exception {
+        return (key == null) ? null : getRawFingerprint(Objects.requireNonNull(f, "No digest factory").create(), key);
+    }
+
+    public static byte[] getRawFingerprint(Digest d, PublicKey key) throws Exception {
+        if (key == null) {
+            return null;
+        }
+
+        Buffer buffer = new ByteArrayBuffer();
+        buffer.putRawPublicKey(key);
+        return DigestUtils.getRawFingerprint(d, buffer.array(), 0, buffer.wpos());
+    }
+
+    /**
+     * @param f The {@link Factory} to create the {@link Digest} to use
+     * @param s The {@link String} to digest - ignored if {@code null}/empty,
+     *          otherwise its UTF-8 representation is used as input for the fingerprint
+     * @return The fingerprint - {@code null} if {@code null}/empty input.
+     * <B>Note:</B> if exception encountered then returns the exception's simple class name
+     * @see #getFingerPrint(Digest, String, Charset)
+     */
+    public static String getFingerPrint(Factory<? extends Digest> f, String s) {
+        return getFingerPrint(f, s, StandardCharsets.UTF_8);
+    }
+
+    /**
+     * @param f       The {@link Factory} to create the {@link Digest} to use
+     * @param s       The {@link String} to digest - ignored if {@code null}/empty
+     * @param charset The {@link Charset} to use in order to convert the
+     *                string to its byte representation to use as input for the fingerprint
+     * @return The fingerprint - {@code null} if {@code null}/empty input
+     * <B>Note:</B> if exception encountered then returns the exception's simple class name
+     * @see DigestUtils#getFingerPrint(Digest, String, Charset)
+     */
+    public static String getFingerPrint(Factory<? extends Digest> f, String s, Charset charset) {
+        return getFingerPrint(f.create(), s, charset);
+    }
+
+    /**
+     * @param d The {@link Digest} to use
+     * @param s The {@link String} to digest - ignored if {@code null}/empty,
+     *          otherwise its UTF-8 representation is used as input for the fingerprint
+     * @return The fingerprint - {@code null} if {@code null}/empty input.
+     * <B>Note:</B> if exception encountered then returns the exception's simple class name
+     * @see DigestUtils#getFingerPrint(Digest, String, Charset)
+     */
+    public static String getFingerPrint(Digest d, String s) {
+        return getFingerPrint(d, s, StandardCharsets.UTF_8);
+    }
+
+    /**
+     * @param d       The {@link Digest} to use to calculate the fingerprint
+     * @param s       The string to digest - ignored if {@code null}/empty
+     * @param charset The {@link Charset} to use in order to convert the
+     *                string to its byte representation to use as input for the fingerprint
+     * @return The fingerprint - {@code null} if {@code null}/empty input.
+     * <B>Note:</B> if exception encountered then returns the exception's simple class name
+     * @see DigestUtils#getFingerPrint(Digest, String, Charset)
+     */
+    public static String getFingerPrint(Digest d, String s, Charset charset) {
+        if (GenericUtils.isEmpty(s)) {
+            return null;
+        }
+
+        try {
+            return DigestUtils.getFingerPrint(d, s, charset);
+        } catch (Exception e) {
+            return e.getClass().getSimpleName();
+        }
+    }
+
+    /**
+     * @param expected The expected fingerprint if {@code null} or empty then returns a failure
+     * with the default fingerprint.
+     * @param key the {@link PublicKey} - if {@code null} then returns null.
+     * @return SimpleImmutableEntry<Boolean, String> - key is success indicator, value is actual fingerprint,
+     * {@code null} if no key.
+     * @see #getDefaultFingerPrintFactory()
+     * @see #checkFingerPrint(String, Factory, PublicKey)
+     */
+    public static SimpleImmutableEntry<Boolean, String> checkFingerPrint(String expected, PublicKey key) {
+        return checkFingerPrint(expected, getDefaultFingerPrintFactory(), key);
+    }
+
+    /**
+     * @param expected The expected fingerprint if {@code null} or empty then returns a failure
+     * with the default fingerprint.
+     * @param f The {@link Factory} to be used to generate the default {@link Digest} for the key
+     * @param key the {@link PublicKey} - if {@code null} then returns null.
+     * @return SimpleImmutableEntry<Boolean, String> - key is success indicator, value is actual fingerprint,
+     * {@code null} if no key.
+     */
+    public static SimpleImmutableEntry<Boolean, String> checkFingerPrint(String expected, Factory<? extends Digest> f, PublicKey key) {
+        return checkFingerPrint(expected, Objects.requireNonNull(f, "No digest factory").create(), key);
+    }
+
+    /**
+     * @param expected The expected fingerprint if {@code null} or empty then returns a failure
+     * with the default fingerprint.
+     * @param d The {@link Digest} to be used to generate the default fingerprint for the key
+     * @param key the {@link PublicKey} - if {@code null} then returns null.
+     * @return SimpleImmutableEntry<Boolean, String> - key is success indicator, value is actual fingerprint,
+     * {@code null} if no key.
+     */
+    public static SimpleImmutableEntry<Boolean, String> checkFingerPrint(String expected, Digest d, PublicKey key) {
+        if (key == null) {
+            return null;
+        }
+
+        if (GenericUtils.isEmpty(expected)) {
+            return new SimpleImmutableEntry<>(false, getFingerPrint(d, key));
+        }
+
+        // de-construct fingerprint
+        int pos = expected.indexOf(':');
+        if ((pos < 0) || (pos >= (expected.length() - 1))) {
+            return new SimpleImmutableEntry<>(false, getFingerPrint(d, key));
+        }
+
+        String name = expected.substring(0, pos);
+        String value = expected.substring(pos + 1);
+        DigestFactory expectedFactory;
+        // We know that all digest names have a length > 2 - if 2 (or less) then assume a pure HEX value
+        if (name.length() > 2) {
+            expectedFactory = BuiltinDigests.fromFactoryName(name);
+            if (expectedFactory == null) {
+                return new SimpleImmutableEntry<>(false, getFingerPrint(d, key));
+            }
+
+            expected = name.toUpperCase() + ":" + value;
+        } else {
+            expectedFactory = BuiltinDigests.md5;
+            expected = expectedFactory.getName().toUpperCase() + ":" + expected;
+        }
+
+        String fingerprint = getFingerPrint(expectedFactory, key);
+        boolean matches = BuiltinDigests.md5.getName().equals(expectedFactory.getName())
+                        ? expected.equalsIgnoreCase(fingerprint)    // HEX is case insensitive
+                        : expected.equals(fingerprint);
+        return new SimpleImmutableEntry<>(matches, fingerprint);
+    }
+
+    /**
+     * @param kp a key pair - ignored if {@code null}. If the private
+     *           key is non-{@code null} then it is used to determine the type,
+     *           otherwise the public one is used.
+     * @return the key type or {@code null} if cannot determine it
+     * @see #getKeyType(Key)
+     */
+    public static String getKeyType(KeyPair kp) {
+        if (kp == null) {
+            return null;
+        }
+        PrivateKey key = kp.getPrivate();
+        if (key != null) {
+            return getKeyType(key);
+        } else {
+            return getKeyType(kp.getPublic());
+        }
+    }
+
+    /**
+     * @param key a public or private key
+     * @return the key type or {@code null} if cannot determine it
+     */
+    public static String getKeyType(Key key) {
+        if (key == null) {
+            return null;
+        } else if (key instanceof DSAKey) {
+            return KeyPairProvider.SSH_DSS;
+        } else if (key instanceof RSAKey) {
+            return KeyPairProvider.SSH_RSA;
+        } else if (key instanceof ECKey) {
+            ECKey ecKey = (ECKey) key;
+            ECParameterSpec ecSpec = ecKey.getParams();
+            ECCurves curve = ECCurves.fromCurveParameters(ecSpec);
+            if (curve == null) {
+                return null;    // debug breakpoint
+            } else {
+                return curve.getKeyType();
+            }
+        } else if (SecurityUtils.EDDSA.equalsIgnoreCase(key.getAlgorithm())) {
+            return KeyPairProvider.SSH_ED25519;
+        }
+
+        return null;
+    }
+
+    /**
+     * Determines the key size in bits
+     *
+     * @param key The {@link Key} to examine - ignored if {@code null}
+     * @return The key size - non-positive value if cannot determine it
+     */
+    public static int getKeySize(Key key) {
+        if (key == null) {
+            return -1;
+        } else if (key instanceof RSAKey) {
+            BigInteger n = ((RSAKey) key).getModulus();
+            return n.bitLength();
+        } else if (key instanceof DSAKey) {
+            DSAParams params = ((DSAKey) key).getParams();
+            BigInteger p = params.getP();
+            return p.bitLength();
+        } else if (key instanceof ECKey) {
+            ECParameterSpec ecSpec = ((ECKey) key).getParams();
+            ECCurves curve = ECCurves.fromCurveParameters(ecSpec);
+            if (curve != null) {
+                return curve.getKeySize();
+            }
+        } else if (SecurityUtils.EDDSA.equalsIgnoreCase(key.getAlgorithm())) {
+            return SecurityUtils.getEDDSAKeySize(key);
+        }
+
+        return -1;
+    }
+
+    /**
+     * @param key    The {@link PublicKey} to be checked - ignored if {@code null}
+     * @param keySet The keys to be searched - ignored if {@code null}/empty
+     * @return The matching {@link PublicKey} from the keys or {@code null} if
+     * no match found
+     * @see #compareKeys(PublicKey, PublicKey)
+     */
+    public static PublicKey findMatchingKey(PublicKey key, PublicKey... keySet) {
+        if (key == null || GenericUtils.isEmpty(keySet)) {
+            return null;
+        } else {
+            return findMatchingKey(key, Arrays.asList(keySet));
+        }
+    }
+
+    /**
+     * @param key    The {@link PublicKey} to be checked - ignored if {@code null}
+     * @param keySet The keys to be searched - ignored if {@code null}/empty
+     * @return The matching {@link PublicKey} from the keys or {@code null} if
+     * no match found
+     * @see #compareKeys(PublicKey, PublicKey)
+     */
+    public static PublicKey findMatchingKey(PublicKey key, Collection<? extends PublicKey> keySet) {
+        if (key == null || GenericUtils.isEmpty(keySet)) {
+            return null;
+        }
+        for (PublicKey k : keySet) {
+            if (compareKeys(key, k)) {
+                return k;
+            }
+        }
+        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
+        } else {
+            return compareKeys(k1.getPublic(), k2.getPublic())
+                && compareKeys(k1.getPrivate(), k2.getPrivate());
+        }
+    }
+
+    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));
+        } else if ((k1 instanceof DSAPublicKey) && (k2 instanceof DSAPublicKey)) {
+            return compareDSAKeys(DSAPublicKey.class.cast(k1), DSAPublicKey.class.cast(k2));
+        } else if ((k1 instanceof ECPublicKey) && (k2 instanceof ECPublicKey)) {
+            return compareECKeys(ECPublicKey.class.cast(k1), ECPublicKey.class.cast(k2));
+        } else if ((k1 != null) && SecurityUtils.EDDSA.equalsIgnoreCase(k1.getAlgorithm())
+                    && (k2 != null) && SecurityUtils.EDDSA.equalsIgnoreCase(k2.getAlgorithm())) {
+            return SecurityUtils.compareEDDSAPPublicKeys(k1, k2);
+        } else {
+            return false;   // either key is null or not of same class
+        }
+    }
+
+    public static PublicKey recoverPublicKey(PrivateKey key) throws GeneralSecurityException {
+        if (key instanceof RSAPrivateKey) {
+            return recoverRSAPublicKey((RSAPrivateKey) key);
+        } else if (key instanceof DSAPrivateKey) {
+            return recoverDSAPublicKey((DSAPrivateKey) key);
+        } else if ((key != null) && SecurityUtils.EDDSA.equalsIgnoreCase(key.getAlgorithm())) {
+            return SecurityUtils.recoverEDDSAPublicKey(key);
+        } else {
+            return null;
+        }
+    }
+
+    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 if ((k1 != null) && SecurityUtils.EDDSA.equalsIgnoreCase(k1.getAlgorithm())
+                    && (k2 != null) && SecurityUtils.EDDSA.equalsIgnoreCase(k2.getAlgorithm())) {
+            return SecurityUtils.compareEDDSAPrivateKeys(k1, k2);
+        } else {
+            return false;   // either key is null or not of same class
+        }
+    }
+
+    public static boolean compareRSAKeys(RSAPublicKey k1, RSAPublicKey k2) {
+        if (Objects.equals(k1, k2)) {
+            return true;
+        } else if (k1 == null || k2 == null) {
+            return false;   // both null is covered by Objects#equals
+        } else {
+            return Objects.equals(k1.getPublicExponent(), k2.getPublicExponent())
+                && Objects.equals(k1.getModulus(), k2.getModulus());
+        }
+    }
+
+    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 {
+            return Objects.equals(k1.getModulus(), k2.getModulus())
+                && Objects.equals(k1.getPrivateExponent(), k2.getPrivateExponent());
+        }
+    }
+
+    public static RSAPublicKey recoverRSAPublicKey(RSAPrivateKey privateKey) throws GeneralSecurityException {
+        if (privateKey instanceof RSAPrivateCrtKey) {
+            return recoverFromRSAPrivateCrtKey((RSAPrivateCrtKey) privateKey);
+        } else {
+            // Not ideal, but best we can do under the circumstances
+            return recoverRSAPublicKey(privateKey.getModulus(), DEFAULT_RSA_PUBLIC_EXPONENT);
+        }
+    }
+
+    public static RSAPublicKey recoverFromRSAPrivateCrtKey(RSAPrivateCrtKey rsaKey) throws GeneralSecurityException {
+        return recoverRSAPublicKey(rsaKey.getPrimeP(), rsaKey.getPrimeQ(), rsaKey.getPublicExponent());
+    }
+
+    public static RSAPublicKey recoverRSAPublicKey(BigInteger p, BigInteger q, BigInteger publicExponent) throws GeneralSecurityException {
+        return recoverRSAPublicKey(p.multiply(q), publicExponent);
+    }
+
+    public static RSAPublicKey recoverRSAPublicKey(BigInteger modulus, BigInteger publicExponent) throws GeneralSecurityException {
+        KeyFactory kf = SecurityUtils.getKeyFactory(RSA_ALGORITHM);
+        return (RSAPublicKey) kf.generatePublic(new RSAPublicKeySpec(modulus, publicExponent));
+    }
+
+    public static boolean compareDSAKeys(DSAPublicKey k1, DSAPublicKey k2) {
+        if (Objects.equals(k1, k2)) {
+            return true;
+        } else if (k1 == null || k2 == null) {
+            return false;   // both null is covered by Objects#equals
+        } else {
+            return Objects.equals(k1.getY(), k2.getY())
+                && compareDSAParams(k1.getParams(), k2.getParams());
+        }
+    }
+
+    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 {
+            return Objects.equals(k1.getX(), k2.getX())
+                && compareDSAParams(k1.getParams(), k2.getParams());
+        }
+    }
+
+    public static boolean compareDSAParams(DSAParams p1, DSAParams p2) {
+        if (Objects.equals(p1, p2)) {
+            return true;
+        } else if (p1 == null || p2 == null) {
+            return false;   // both null is covered by Objects#equals
+        } else {
+            return Objects.equals(p1.getG(), p2.getG())
+                && Objects.equals(p1.getP(), p2.getP())
+                && Objects.equals(p1.getQ(), p2.getQ());
+        }
+    }
+
+    // based on code from https://github.com/alexo/SAML-2.0/blob/master/java-opensaml/opensaml-security-api/src/main/java/org/opensaml/xml/security/SecurityHelper.java
+    public static DSAPublicKey recoverDSAPublicKey(DSAPrivateKey privateKey) throws GeneralSecurityException {
+        DSAParams keyParams = privateKey.getParams();
+        BigInteger p = keyParams.getP();
+        BigInteger x = privateKey.getX();
+        BigInteger q = keyParams.getQ();
+        BigInteger g = keyParams.getG();
+        BigInteger y = g.modPow(x, p);
+        KeyFactory kf = SecurityUtils.getKeyFactory(DSS_ALGORITHM);
+        return (DSAPublicKey) kf.generatePublic(new DSAPublicKeySpec(y, p, q, g));
+    }
+
+    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 {
+            return Objects.equals(k1.getS(), k2.getS())
+                && compareECParams(k1.getParams(), k2.getParams());
+        }
+    }
+
+    public static boolean compareECKeys(ECPublicKey k1, ECPublicKey k2) {
+        if (Objects.equals(k1, k2)) {
+            return true;
+        } else if (k1 == null || k2 == null) {
+            return false;   // both null is covered by Objects#equals
+        } else {
+            return Objects.equals(k1.getW(), k2.getW())
+                && compareECParams(k1.getParams(), k2.getParams());
+        }
+    }
+
+    public static boolean compareECParams(ECParameterSpec s1, ECParameterSpec s2) {
+        if (Objects.equals(s1, s2)) {
+            return true;
+        } else if (s1 == null || s2 == null) {
+            return false;   // both null is covered by Objects#equals
+        } else {
+            return Objects.equals(s1.getOrder(), s2.getOrder())
+                && (s1.getCofactor() == s2.getCofactor())
+                && Objects.equals(s1.getGenerator(), s2.getGenerator())
+                && Objects.equals(s1.getCurve(), s2.getCurve());
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PrivateKeyEntryDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PrivateKeyEntryDecoder.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PrivateKeyEntryDecoder.java
new file mode 100644
index 0000000..6dbeee9
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PrivateKeyEntryDecoder.java
@@ -0,0 +1,142 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config.keys;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.StreamCorruptedException;
+import java.security.GeneralSecurityException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.util.Collection;
+import java.util.Objects;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.NumberUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * @param <PUB> Type of {@link PublicKey}
+ * @param <PRV> Type of {@link PrivateKey}
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface PrivateKeyEntryDecoder<PUB extends PublicKey, PRV extends PrivateKey>
+            extends KeyEntryResolver<PUB, PRV>, PrivateKeyEntryResolver {
+
+    @Override
+    default PrivateKey resolve(String keyType, byte[] keyData) throws IOException, GeneralSecurityException {
+        ValidateUtils.checkNotNullAndNotEmpty(keyType, "No key type provided");
+        Collection<String> supported = getSupportedTypeNames();
+        if ((GenericUtils.size(supported) > 0) && supported.contains(keyType)) {
+            return decodePrivateKey(FilePasswordProvider.EMPTY, keyData);
+        }
+
+        throw new InvalidKeySpecException("resolve(" + keyType + ") not in listed supported types: " + supported);
+    }
+
+    /**
+     * @param passwordProvider The {@link FilePasswordProvider} to use
+     * in case the data is encrypted - may be {@code null} if no encrypted
+     * data is expected
+     * @param keyData The key data bytes in {@code OpenSSH} format (after
+     *                BASE64 decoding) - ignored if {@code null}/empty
+     * @return The decoded {@link PrivateKey} - or {@code null} if no data
+     * @throws IOException              If failed to decode the key
+     * @throws GeneralSecurityException If failed to generate the key
+     */
+    default PRV decodePrivateKey(FilePasswordProvider passwordProvider, byte... keyData)
+            throws IOException, GeneralSecurityException {
+        return decodePrivateKey(passwordProvider, keyData, 0, NumberUtils.length(keyData));
+    }
+
+    default PRV decodePrivateKey(FilePasswordProvider passwordProvider, byte[] keyData, int offset, int length)
+            throws IOException, GeneralSecurityException {
+        if (length <= 0) {
+            return null;
+        }
+
+        try (InputStream stream = new ByteArrayInputStream(keyData, offset, length)) {
+            return decodePrivateKey(passwordProvider, stream);
+        }
+    }
+
+    default PRV decodePrivateKey(FilePasswordProvider passwordProvider, InputStream keyData)
+            throws IOException, GeneralSecurityException {
+        // the actual data is preceded by a string that repeats the key type
+        String type = KeyEntryResolver.decodeString(keyData);
+        if (GenericUtils.isEmpty(type)) {
+            throw new StreamCorruptedException("Missing key type string");
+        }
+
+        Collection<String> supported = getSupportedTypeNames();
+        if (GenericUtils.isEmpty(supported) || (!supported.contains(type))) {
+            throw new InvalidKeySpecException("Reported key type (" + type + ") not in supported list: " + supported);
+        }
+
+        return decodePrivateKey(type, passwordProvider, keyData);
+    }
+
+    /**
+     * @param keyType The reported / encode key type
+     * @param passwordProvider The {@link FilePasswordProvider} to use
+     * in case the data is encrypted - may be {@code null} if no encrypted
+     * data is expected
+     * @param keyData The key data bytes stream positioned after the key type decoding
+     *                and making sure it is one of the supported types
+     * @return The decoded {@link PrivateKey}
+     * @throws IOException              If failed to read from the data stream
+     * @throws GeneralSecurityException If failed to generate the key
+     */
+    PRV decodePrivateKey(String keyType, FilePasswordProvider passwordProvider, InputStream keyData)
+            throws IOException, GeneralSecurityException;
+
+    /**
+     * Encodes the {@link PrivateKey} using the {@code OpenSSH} format - same
+     * one used by the {@code decodePublicKey} method(s)
+     *
+     * @param s   The {@link OutputStream} to write the data to
+     * @param key The {@link PrivateKey} - may not be {@code null}
+     * @return The key type value - one of the {@link #getSupportedTypeNames()} or
+     * {@code null} if encoding not supported
+     * @throws IOException If failed to generate the encoding
+     */
+    default String encodePrivateKey(OutputStream s, PRV key) throws IOException {
+        Objects.requireNonNull(key, "No private key provided");
+        return null;
+    }
+
+    default boolean isPublicKeyRecoverySupported() {
+        return false;
+    }
+
+    /**
+     * Attempts to recover the public key given the private one
+     *
+     * @param prvKey The {@link PrivateKey}
+     * @return The recovered {@link PublicKey} - {@code null} if cannot recover it
+     * @throws GeneralSecurityException If failed to generate the public key
+     */
+    default PUB recoverPublicKey(PRV prvKey) throws GeneralSecurityException {
+        return null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PrivateKeyEntryResolver.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PrivateKeyEntryResolver.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PrivateKeyEntryResolver.java
new file mode 100644
index 0000000..1e4c91e
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PrivateKeyEntryResolver.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config.keys;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.PrivateKey;
+import java.security.spec.InvalidKeySpecException;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FunctionalInterface
+public interface PrivateKeyEntryResolver {
+    /**
+     * A resolver that ignores all input
+     */
+    PrivateKeyEntryResolver IGNORING = new PrivateKeyEntryResolver() {
+        @Override
+        public PrivateKey resolve(String keyType, byte[] keyData) throws IOException, GeneralSecurityException {
+            return null;
+        }
+
+        @Override
+        public String toString() {
+            return "IGNORING";
+        }
+    };
+
+    /**
+     * A resolver that fails on all input
+     */
+    PrivateKeyEntryResolver FAILING = new PrivateKeyEntryResolver() {
+        @Override
+        public PrivateKey resolve(String keyType, byte[] keyData) throws IOException, GeneralSecurityException {
+            throw new InvalidKeySpecException("Failing resolver on key type=" + keyType);
+        }
+
+        @Override
+        public String toString() {
+            return "FAILING";
+        }
+    };
+
+    /**
+     * @param keyType The {@code OpenSSH} reported key type
+     * @param keyData The {@code OpenSSH} encoded key data
+     * @return The extracted {@link PrivateKey} - ignored if {@code null}
+     * @throws IOException If failed to parse the key data
+     * @throws GeneralSecurityException If failed to generate the key
+     */
+    PrivateKey resolve(String keyType, byte[] keyData) throws IOException, GeneralSecurityException;
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntry.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntry.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntry.java
new file mode 100644
index 0000000..1db3d2b
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntry.java
@@ -0,0 +1,286 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config.keys;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.Serializable;
+import java.io.StreamCorruptedException;
+import java.nio.file.Path;
+import java.security.GeneralSecurityException;
+import java.security.PublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.util.Arrays;
+import java.util.Base64;
+import java.util.Objects;
+
+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
+ * the <A HREF="http://en.wikibooks.org/wiki/OpenSSH">OpenSSH</A> format:</P>
+ *
+ * <PRE>
+ * &lt;key-type&gt; &lt;base64-encoded-public-key-data&gt;
+ * </PRE>
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class PublicKeyEntry implements Serializable {
+
+    /**
+     * Character used to denote a comment line in the keys file
+     */
+    public static final char COMMENT_CHAR = '#';
+
+
+    /**
+     * Standard folder name used by OpenSSH to hold key files
+     */
+    public static final String STD_KEYFILE_FOLDER_NAME = ".ssh";
+
+    private static final long serialVersionUID = -585506072687602760L;
+
+    private String keyType;
+    private byte[] keyData;
+
+    public PublicKeyEntry() {
+        super();
+    }
+
+    public PublicKeyEntry(String keyType, byte... keyData) {
+        this.keyType = keyType;
+        this.keyData = keyData;
+    }
+
+    public String getKeyType() {
+        return keyType;
+    }
+
+    public void setKeyType(String value) {
+        this.keyType = value;
+    }
+
+    public byte[] getKeyData() {
+        return keyData;
+    }
+
+    public void setKeyData(byte[] value) {
+        this.keyData = value;
+    }
+
+    /**
+     * @param fallbackResolver The {@link PublicKeyEntryResolver} to consult if
+     * none of the built-in ones can be used. If {@code null} and no built-in
+     * resolver can be used then an {@link InvalidKeySpecException} is thrown.
+     * @return The resolved {@link PublicKey} - or {@code null} if could not be
+     * resolved. <B>Note:</B> may be called only after key type and data bytes
+     * have been set or exception(s) may be thrown
+     * @throws IOException              If failed to decode the key
+     * @throws GeneralSecurityException If failed to generate the key
+     */
+    public PublicKey resolvePublicKey(PublicKeyEntryResolver fallbackResolver) throws IOException, GeneralSecurityException {
+        String kt = getKeyType();
+        PublicKeyEntryResolver decoder = KeyUtils.getPublicKeyEntryDecoder(kt);
+        if (decoder == null) {
+            decoder = fallbackResolver;
+        }
+        if (decoder == null) {
+            throw new InvalidKeySpecException("No decoder available for key type=" + kt);
+        }
+
+        return decoder.resolve(kt, getKeyData());
+    }
+
+    /**
+     * @param sb The {@link Appendable} instance to encode the data into
+     * @param fallbackResolver The {@link PublicKeyEntryResolver} to consult if
+     * none of the built-in ones can be used. If {@code null} and no built-in
+     * resolver can be used then an {@link InvalidKeySpecException} is thrown.
+     * @return The {@link PublicKey} or {@code null} if could not resolve it
+     * @throws IOException              If failed to decode/encode the key
+     * @throws GeneralSecurityException If failed to generate the key
+     * @see #resolvePublicKey(PublicKeyEntryResolver)
+     */
+    public PublicKey appendPublicKey(Appendable sb, PublicKeyEntryResolver fallbackResolver) throws IOException, GeneralSecurityException {
+        PublicKey key = resolvePublicKey(fallbackResolver);
+        if (key != null) {
+            appendPublicKeyEntry(sb, key);
+        }
+        return key;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(getKeyType()) + Arrays.hashCode(getKeyData());
+    }
+
+    /*
+     * In case some derived class wants to define some "extended" equality
+     * without having to repeat this code
+     */
+    protected boolean isEquivalent(PublicKeyEntry e) {
+        if (this == e) {
+            return true;
+        }
+        return Objects.equals(getKeyType(), e.getKeyType())
+            && Arrays.equals(getKeyData(), e.getKeyData());
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (this == obj) {
+            return true;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        return isEquivalent((PublicKeyEntry) obj);
+    }
+
+    @Override
+    public String toString() {
+        byte[] data = getKeyData();
+        Base64.Encoder encoder = Base64.getEncoder();
+        return getKeyType() + " " + (NumberUtils.isEmpty(data) ? "<no-key>" : encoder.encodeToString(data));
+    }
+
+    /**
+     * @param encData Assumed to contain at least {@code key-type base64-data}
+     * (anything beyond the BASE64 data is ignored) - ignored if {@code null}/empty
+     * @return A {@link PublicKeyEntry} or {@code null} if no data
+     * @throws IllegalArgumentException if bad format found
+     * @see #parsePublicKeyEntry(PublicKeyEntry, String)
+     */
+    public static PublicKeyEntry parsePublicKeyEntry(String encData) throws IllegalArgumentException {
+        String data = GenericUtils.replaceWhitespaceAndTrim(encData);
+        if (GenericUtils.isEmpty(data)) {
+            return null;
+        } else {
+            return parsePublicKeyEntry(new PublicKeyEntry(), data);
+        }
+    }
+
+    /**
+     * @param <E>   The generic entry type
+     * @param entry The {@link PublicKeyEntry} whose contents are to be
+     *              updated - ignored if {@code null}
+     * @param encData Assumed to contain at least {@code key-type base64-data} (anything
+     *              beyond the BASE64 data is ignored) - ignored if {@code null}/empty
+     * @return The updated entry instance
+     * @throws IllegalArgumentException if bad format found
+     */
+    public static <E extends PublicKeyEntry> E parsePublicKeyEntry(E entry, String encData) throws IllegalArgumentException {
+        String data = GenericUtils.replaceWhitespaceAndTrim(encData);
+        if (GenericUtils.isEmpty(data) || (entry == null)) {
+            return entry;
+        }
+
+        int startPos = data.indexOf(' ');
+        if (startPos <= 0) {
+            throw new IllegalArgumentException("Bad format (no key data delimiter): " + data);
+        }
+
+        int endPos = data.indexOf(' ', startPos + 1);
+        if (endPos <= startPos) {   // OK if no continuation beyond the BASE64 encoded data
+            endPos = data.length();
+        }
+
+        String keyType = data.substring(0, startPos);
+        String b64Data = data.substring(startPos + 1, endPos).trim();
+        Base64.Decoder decoder = Base64.getDecoder();
+        byte[] keyData = decoder.decode(b64Data);
+        if (NumberUtils.isEmpty(keyData)) {
+            throw new IllegalArgumentException("Bad format (no BASE64 key data): " + data);
+        }
+
+        entry.setKeyType(keyType);
+        entry.setKeyData(keyData);
+        return entry;
+    }
+
+    /**
+     * @param key The {@link PublicKey}
+     * @return The {@code OpenSSH} encoded data
+     * @throws IllegalArgumentException If failed to encode
+     * @see #appendPublicKeyEntry(Appendable, PublicKey)
+     */
+    public static String toString(PublicKey key) throws IllegalArgumentException {
+        try {
+            return appendPublicKeyEntry(new StringBuilder(Byte.MAX_VALUE), key).toString();
+        } catch (IOException e) {
+            throw new IllegalArgumentException("Failed (" + e.getClass().getSimpleName() + ") to encode: " + e.getMessage(), e);
+        }
+    }
+
+    /**
+     * Encodes a public key data the same way as the {@link #parsePublicKeyEntry(String)} expects it
+     *
+     * @param <A> The generic appendable class
+     * @param sb  The {@link Appendable} instance to encode the data into
+     * @param key The {@link PublicKey} - ignored if {@code null}
+     * @return The updated appendable instance
+     * @throws IOException If failed to append the data
+     */
+    public static <A extends Appendable> A appendPublicKeyEntry(A sb, PublicKey key) throws IOException {
+        if (key == null) {
+            return sb;
+        }
+
+        @SuppressWarnings("unchecked")
+        PublicKeyEntryDecoder<PublicKey, ?> decoder =
+            (PublicKeyEntryDecoder<PublicKey, ?>) KeyUtils.getPublicKeyEntryDecoder(key);
+        if (decoder == null) {
+            throw new StreamCorruptedException("Cannot retrieve decoder for key=" + key.getAlgorithm());
+        }
+
+        try (ByteArrayOutputStream s = new ByteArrayOutputStream(Byte.MAX_VALUE)) {
+            String keyType = decoder.encodePublicKey(s, key);
+            byte[] bytes = s.toByteArray();
+            Base64.Encoder encoder = Base64.getEncoder();
+            String b64Data = encoder.encodeToString(bytes);
+            sb.append(keyType).append(' ').append(b64Data);
+        }
+
+        return sb;
+    }
+
+    private static final class LazyDefaultKeysFolderHolder {
+        private static final Path PATH =
+            IdentityUtils.getUserHomeFolder().resolve(STD_KEYFILE_FOLDER_NAME);
+
+        private LazyDefaultKeysFolderHolder() {
+            throw new UnsupportedOperationException("No instance allowed");
+        }
+    }
+
+    /**
+     * @return The default OpenSSH folder used to hold key files - e.g.,
+     * {@code known_hosts}, {@code authorized_keys}, etc.
+     */
+    @SuppressWarnings("synthetic-access")
+    public static Path getDefaultKeysFolderPath() {
+        return LazyDefaultKeysFolderHolder.PATH;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntryDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntryDecoder.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntryDecoder.java
new file mode 100644
index 0000000..7462e4a
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntryDecoder.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config.keys;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.StreamCorruptedException;
+import java.security.GeneralSecurityException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.spec.InvalidKeySpecException;
+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;
+
+/**
+ * Represents a decoder of an {@code OpenSSH} encoded key data
+ *
+ * @param <PUB> Type of {@link PublicKey}
+ * @param <PRV> Type of {@link PrivateKey}
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface PublicKeyEntryDecoder<PUB extends PublicKey, PRV extends PrivateKey>
+        extends KeyEntryResolver<PUB, PRV>, PublicKeyEntryResolver {
+
+    @Override
+    default PublicKey resolve(String keyType, byte[] keyData) throws IOException, GeneralSecurityException {
+        ValidateUtils.checkNotNullAndNotEmpty(keyType, "No key type provided");
+        Collection<String> supported = getSupportedTypeNames();
+        if ((GenericUtils.size(supported) > 0) && supported.contains(keyType)) {
+            return decodePublicKey(keyData);
+        }
+
+        throw new InvalidKeySpecException("resolve(" + keyType + ") not in listed supported types: " + supported);
+    }
+
+    /**
+     * @param keyData The key data bytes in {@code OpenSSH} format (after
+     *                BASE64 decoding) - ignored if {@code null}/empty
+     * @return The decoded {@link PublicKey} - or {@code null} if no data
+     * @throws IOException              If failed to decode the key
+     * @throws GeneralSecurityException If failed to generate the key
+     */
+    default PUB decodePublicKey(byte... keyData) throws IOException, GeneralSecurityException {
+        return decodePublicKey(keyData, 0, NumberUtils.length(keyData));
+    }
+
+    default PUB decodePublicKey(byte[] keyData, int offset, int length) throws IOException, GeneralSecurityException {
+        if (length <= 0) {
+            return null;
+        }
+
+        try (InputStream stream = new ByteArrayInputStream(keyData, offset, length)) {
+            return decodePublicKey(stream);
+        }
+    }
+
+    default PUB decodePublicKey(InputStream keyData) throws IOException, GeneralSecurityException {
+        // the actual data is preceded by a string that repeats the key type
+        String type = KeyEntryResolver.decodeString(keyData);
+        if (GenericUtils.isEmpty(type)) {
+            throw new StreamCorruptedException("Missing key type string");
+        }
+
+        Collection<String> supported = getSupportedTypeNames();
+        if (GenericUtils.isEmpty(supported) || (!supported.contains(type))) {
+            throw new InvalidKeySpecException("Reported key type (" + type + ") not in supported list: " + supported);
+        }
+
+        return decodePublicKey(type, keyData);
+    }
+
+    /**
+     * @param keyType The reported / encode key type
+     * @param keyData The key data bytes stream positioned after the key type decoding
+     *                and making sure it is one of the supported types
+     * @return The decoded {@link PublicKey}
+     * @throws IOException              If failed to read from the data stream
+     * @throws GeneralSecurityException If failed to generate the key
+     */
+    PUB decodePublicKey(String keyType, InputStream keyData) throws IOException, GeneralSecurityException;
+
+    /**
+     * Encodes the {@link PublicKey} using the {@code OpenSSH} format - same
+     * one used by the {@code decodePublicKey} method(s)
+     *
+     * @param s   The {@link OutputStream} to write the data to
+     * @param key The {@link PublicKey} - may not be {@code null}
+     * @return The key type value - one of the {@link #getSupportedTypeNames()}
+     * @throws IOException If failed to generate the encoding
+     */
+    String encodePublicKey(OutputStream s, PUB key) throws IOException;
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntryResolver.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntryResolver.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntryResolver.java
new file mode 100644
index 0000000..e5eb0ed
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntryResolver.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config.keys;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.PublicKey;
+import java.security.spec.InvalidKeySpecException;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FunctionalInterface
+public interface PublicKeyEntryResolver {
+    /**
+     * A resolver that ignores all input
+     */
+    PublicKeyEntryResolver IGNORING = new PublicKeyEntryResolver() {
+        @Override
+        public PublicKey resolve(String keyType, byte[] keyData) throws IOException, GeneralSecurityException {
+            return null;
+        }
+
+        @Override
+        public String toString() {
+            return "IGNORING";
+        }
+    };
+
+    /**
+     * A resolver that fails on all input
+     */
+    PublicKeyEntryResolver FAILING = new PublicKeyEntryResolver() {
+        @Override
+        public PublicKey resolve(String keyType, byte[] keyData) throws IOException, GeneralSecurityException {
+            throw new InvalidKeySpecException("Failing resolver on key type=" + keyType);
+        }
+
+        @Override
+        public String toString() {
+            return "FAILING";
+        }
+    };
+
+    /**
+     * @param keyType The {@code OpenSSH} reported key type
+     * @param keyData The {@code OpenSSH} encoded key data
+     * @return The extracted {@link PublicKey} - ignored if {@code null}
+     * @throws IOException If failed to parse the key data
+     * @throws GeneralSecurityException If failed to generate the key
+     */
+    PublicKey resolve(String keyType, byte[] keyData) throws IOException, GeneralSecurityException;
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractIdentityResourceLoader.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractIdentityResourceLoader.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractIdentityResourceLoader.java
new file mode 100644
index 0000000..a2412bd
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractIdentityResourceLoader.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.config.keys.impl;
+
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.util.Collection;
+import java.util.Objects;
+
+import org.apache.sshd.common.config.keys.IdentityResourceLoader;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
+
+/**
+ * @param <PUB> Generic public key type
+ * @param <PRV> Generic private key type
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractIdentityResourceLoader<PUB extends PublicKey, PRV extends PrivateKey>
+        extends AbstractLoggingBean
+        implements IdentityResourceLoader<PUB, PRV> {
+    private final Class<PUB> pubType;
+    private final Class<PRV> prvType;
+    private final Collection<String> names;
+
+    protected AbstractIdentityResourceLoader(Class<PUB> pubType, Class<PRV> prvType, Collection<String> names) {
+        this.pubType = Objects.requireNonNull(pubType, "No public key type specified");
+        this.prvType = Objects.requireNonNull(prvType, "No private key type specified");
+        this.names = ValidateUtils.checkNotNullAndNotEmpty(names, "No type names provided");
+    }
+
+    @Override
+    public final Class<PUB> getPublicKeyType() {
+        return pubType;
+    }
+
+    @Override
+    public final Class<PRV> getPrivateKeyType() {
+        return prvType;
+    }
+
+    @Override
+    public Collection<String> getSupportedTypeNames() {
+        return names;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractKeyEntryResolver.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractKeyEntryResolver.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractKeyEntryResolver.java
new file mode 100644
index 0000000..7afa3a6
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractKeyEntryResolver.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config.keys.impl;
+
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.spec.KeySpec;
+import java.util.Collection;
+
+import org.apache.sshd.common.config.keys.KeyEntryResolver;
+
+/**
+ * @param <PUB> Type of {@link PublicKey}
+ * @param <PRV> Type of {@link PrivateKey}
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractKeyEntryResolver<PUB extends PublicKey, PRV extends PrivateKey>
+            extends AbstractIdentityResourceLoader<PUB, PRV>
+            implements KeyEntryResolver<PUB, PRV>  {
+    protected AbstractKeyEntryResolver(Class<PUB> pubType, Class<PRV> prvType, Collection<String> names) {
+        super(pubType, prvType, names);
+    }
+
+    public PUB generatePublicKey(KeySpec keySpec) throws GeneralSecurityException {
+        KeyFactory factory = getKeyFactoryInstance();
+        Class<PUB> keyType = getPublicKeyType();
+        return keyType.cast(factory.generatePublic(keySpec));
+    }
+
+    public PRV generatePrivateKey(KeySpec keySpec) throws GeneralSecurityException {
+        KeyFactory factory = getKeyFactoryInstance();
+        Class<PRV> keyType = getPrivateKeyType();
+        return keyType.cast(factory.generatePrivate(keySpec));
+    }
+
+    @Override
+    public String toString() {
+        return getPublicKeyType().getSimpleName() + ": " + getSupportedTypeNames();
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractPrivateKeyEntryDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractPrivateKeyEntryDecoder.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractPrivateKeyEntryDecoder.java
new file mode 100644
index 0000000..574412e
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractPrivateKeyEntryDecoder.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config.keys.impl;
+
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.util.Collection;
+
+import org.apache.sshd.common.config.keys.PrivateKeyEntryDecoder;
+
+/**
+ * @param <PUB> Type of {@link PublicKey}
+ * @param <PRV> Type of {@link PrivateKey}
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractPrivateKeyEntryDecoder<PUB extends PublicKey, PRV extends PrivateKey>
+            extends AbstractKeyEntryResolver<PUB, PRV>
+            implements PrivateKeyEntryDecoder<PUB, PRV> {
+    protected AbstractPrivateKeyEntryDecoder(Class<PUB> pubType, Class<PRV> prvType, Collection<String> names) {
+        super(pubType, prvType, names);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractPublicKeyEntryDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractPublicKeyEntryDecoder.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractPublicKeyEntryDecoder.java
new file mode 100644
index 0000000..59cbf3a
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractPublicKeyEntryDecoder.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config.keys.impl;
+
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.util.Collection;
+
+import org.apache.sshd.common.config.keys.PublicKeyEntryDecoder;
+
+/**
+ * Useful base class implementation for a decoder of an {@code OpenSSH} encoded key data
+ *
+ * @param <PUB> Type of {@link PublicKey}
+ * @param <PRV> Type of {@link PrivateKey}
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractPublicKeyEntryDecoder<PUB extends PublicKey, PRV extends PrivateKey>
+            extends AbstractKeyEntryResolver<PUB, PRV>
+            implements PublicKeyEntryDecoder<PUB, PRV> {
+    protected AbstractPublicKeyEntryDecoder(Class<PUB> pubType, Class<PRV> prvType, Collection<String> names) {
+        super(pubType, prvType, names);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/DSSPublicKeyEntryDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/DSSPublicKeyEntryDecoder.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/DSSPublicKeyEntryDecoder.java
new file mode 100644
index 0000000..464d2b0
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/DSSPublicKeyEntryDecoder.java
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config.keys.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.KeyPairGenerator;
+import java.security.interfaces.DSAParams;
+import java.security.interfaces.DSAPrivateKey;
+import java.security.interfaces.DSAPublicKey;
+import java.security.spec.DSAPrivateKeySpec;
+import java.security.spec.DSAPublicKeySpec;
+import java.security.spec.InvalidKeySpecException;
+import java.util.Collections;
+import java.util.Objects;
+
+import org.apache.sshd.common.config.keys.KeyEntryResolver;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class DSSPublicKeyEntryDecoder extends AbstractPublicKeyEntryDecoder<DSAPublicKey, DSAPrivateKey> {
+    public static final DSSPublicKeyEntryDecoder INSTANCE = new DSSPublicKeyEntryDecoder();
+
+    public DSSPublicKeyEntryDecoder() {
+        super(DSAPublicKey.class, DSAPrivateKey.class, Collections.unmodifiableList(Collections.singletonList(KeyPairProvider.SSH_DSS)));
+    }
+
+    @Override
+    public DSAPublicKey decodePublicKey(String keyType, InputStream keyData) throws IOException, GeneralSecurityException {
+        if (!KeyPairProvider.SSH_DSS.equals(keyType)) { // just in case we were invoked directly
+            throw new InvalidKeySpecException("Unexpected key type: " + keyType);
+        }
+
+        BigInteger p = KeyEntryResolver.decodeBigInt(keyData);
+        BigInteger q = KeyEntryResolver.decodeBigInt(keyData);
+        BigInteger g = KeyEntryResolver.decodeBigInt(keyData);
+        BigInteger y = KeyEntryResolver.decodeBigInt(keyData);
+
+        return generatePublicKey(new DSAPublicKeySpec(y, p, q, g));
+    }
+
+    @Override
+    public String encodePublicKey(OutputStream s, DSAPublicKey key) throws IOException {
+        Objects.requireNonNull(key, "No public key provided");
+
+        DSAParams keyParams = Objects.requireNonNull(key.getParams(), "No DSA params available");
+        KeyEntryResolver.encodeString(s, KeyPairProvider.SSH_DSS);
+        KeyEntryResolver.encodeBigInt(s, keyParams.getP());
+        KeyEntryResolver.encodeBigInt(s, keyParams.getQ());
+        KeyEntryResolver.encodeBigInt(s, keyParams.getG());
+        KeyEntryResolver.encodeBigInt(s, key.getY());
+
+        return KeyPairProvider.SSH_DSS;
+    }
+
+    @Override
+    public DSAPublicKey clonePublicKey(DSAPublicKey key) throws GeneralSecurityException {
+        if (key == null) {
+            return null;
+        }
+
+        DSAParams params = key.getParams();
+        if (params == null) {
+            throw new InvalidKeyException("Missing parameters in key");
+        }
+
+        return generatePublicKey(new DSAPublicKeySpec(key.getY(), params.getP(), params.getQ(), params.getG()));
+    }
+
+    @Override
+    public DSAPrivateKey clonePrivateKey(DSAPrivateKey key) throws GeneralSecurityException {
+        if (key == null) {
+            return null;
+        }
+
+        DSAParams params = key.getParams();
+        if (params == null) {
+            throw new InvalidKeyException("Missing parameters in key");
+        }
+
+        return generatePrivateKey(new DSAPrivateKeySpec(key.getX(), params.getP(), params.getQ(), params.getG()));
+    }
+
+    @Override
+    public KeyPairGenerator getKeyPairGenerator() throws GeneralSecurityException {
+        return SecurityUtils.getKeyPairGenerator(KeyUtils.DSS_ALGORITHM);
+    }
+
+    @Override
+    public KeyFactory getKeyFactoryInstance() throws GeneralSecurityException {
+        return SecurityUtils.getKeyFactory(KeyUtils.DSS_ALGORITHM);
+    }
+}


[15/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common utilities code from sshd-core into sshd-common (new artifact)

Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/BufferUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/BufferUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/BufferUtils.java
deleted file mode 100644
index c13cd9e..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/BufferUtils.java
+++ /dev/null
@@ -1,604 +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.buffer;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.StreamCorruptedException;
-import java.util.function.IntUnaryOperator;
-import java.util.logging.Level;
-
-import org.apache.sshd.common.PropertyResolver;
-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;
-import org.apache.sshd.common.util.logging.SimplifiedLog;
-
-/**
- * TODO Add javadoc
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public final class BufferUtils {
-    public static final char DEFAULT_HEX_SEPARATOR = ' ';
-    public static final char EMPTY_HEX_SEPARATOR = '\0';
-    public static final String HEX_DIGITS = "0123456789abcdef";
-
-    public static final String HEXDUMP_CHUNK_SIZE = "sshd-hexdump-chunk-size";
-    public static final int DEFAULT_HEXDUMP_CHUNK_SIZE = 64;
-    public static final Level DEFAULT_HEXDUMP_LEVEL = Level.FINEST;
-
-    public static final IntUnaryOperator DEFAULT_BUFFER_GROWTH_FACTOR = BufferUtils::getNextPowerOf2;
-
-    /**
-     * Maximum value of a {@code uint32} field
-     */
-    public static final long MAX_UINT32_VALUE = 0x0FFFFFFFFL;
-
-    /**
-     * Maximum value of a {@code uint8} field
-     */
-    public static final int MAX_UINT8_VALUE = 0x0FF;
-
-    /**
-     * Private Constructor
-     */
-    private BufferUtils() {
-        throw new UnsupportedOperationException("No instance allowed");
-    }
-
-    public static void dumpHex(SimplifiedLog logger, Level level, String prefix, PropertyResolver resolver, char sep, byte... data) {
-        dumpHex(logger, level, prefix, resolver, sep, data, 0, NumberUtils.length(data));
-    }
-
-    public static void dumpHex(SimplifiedLog logger, Level level, String prefix, PropertyResolver resolver, char sep, byte[] data, int offset, int len) {
-        dumpHex(logger, level, prefix, sep, resolver.getIntProperty(HEXDUMP_CHUNK_SIZE, DEFAULT_HEXDUMP_CHUNK_SIZE), data, offset, len);
-    }
-
-    public static void dumpHex(SimplifiedLog logger, Level level, String prefix, char sep, int chunkSize, byte... data) {
-        dumpHex(logger, level, prefix, sep, chunkSize, data, 0, NumberUtils.length(data));
-    }
-
-    public static void dumpHex(SimplifiedLog logger, Level level, String prefix, char sep, int chunkSize, byte[] data, int offset, int len) {
-        if ((logger == null) || (level == null) || (!logger.isEnabled(level))) {
-            return;
-        }
-
-        StringBuilder sb = new StringBuilder(chunkSize * 3 /* HEX */ + prefix.length() + Long.SIZE /* some extra */);
-        sb.append(prefix);
-        for (int remainLen = len, chunkIndex = 1, curOffset = offset, totalLen = 0; remainLen > 0; chunkIndex++) {
-            sb.setLength(prefix.length());    // reset for next chunk
-
-            sb.append(" [chunk #").append(chunkIndex).append(']');
-
-            int dumpSize = Math.min(chunkSize, remainLen);
-            totalLen += dumpSize;
-            sb.append('(').append(totalLen).append('/').append(len).append(')');
-
-            try {
-                appendHex(sb.append(' '), data, curOffset, dumpSize, sep);
-            } catch (IOException e) {   // unexpected
-                sb.append(e.getClass().getSimpleName()).append(": ").append(e.getMessage());
-            }
-
-            // Pad the last (incomplete) line to align its data view
-            for (int index = dumpSize; index < chunkSize; index++) {
-                if (sep != EMPTY_HEX_SEPARATOR) {
-                    sb.append(' ');
-                }
-                sb.append("  ");
-            }
-
-            sb.append("    ");
-            for (int pos = curOffset, l = 0; l < dumpSize; pos++, l++) {
-                int b = data[pos] & 0xFF;
-                if ((b > ' ') && (b < 0x7E)) {
-                    sb.append((char) b);
-                } else {
-                    sb.append('.');
-                }
-            }
-
-            logger.log(level, sb.toString());
-            remainLen -= dumpSize;
-            curOffset += dumpSize;
-        }
-    }
-
-    public static String toHex(byte... array) {
-        return toHex(array, 0, NumberUtils.length(array));
-    }
-
-    public static String toHex(char sep, byte... array) {
-        return toHex(array, 0, NumberUtils.length(array), sep);
-    }
-
-    public static String toHex(byte[] array, int offset, int len) {
-        return toHex(array, offset, len, DEFAULT_HEX_SEPARATOR);
-    }
-
-    public static String toHex(byte[] array, int offset, int len, char sep) {
-        if (len <= 0) {
-            return "";
-        }
-
-        try {
-            return appendHex(new StringBuilder(len * 3 /* 2 HEX + sep */), array, offset, len, sep).toString();
-        } catch (IOException e) {   // unexpected
-            return e.getClass().getSimpleName() + ": " + e.getMessage();
-        }
-    }
-
-    public static <A extends Appendable> A appendHex(A sb, char sep, byte... array) throws IOException {
-        return appendHex(sb, array, 0, NumberUtils.length(array), sep);
-    }
-
-    public static <A extends Appendable> A appendHex(A sb, byte[] array, int offset, int len, char sep) throws IOException {
-        if (len <= 0) {
-            return sb;
-        }
-
-        for (int curOffset = offset, maxOffset = offset + len; curOffset < maxOffset; curOffset++) {
-            byte b = array[curOffset];
-            if ((curOffset > offset) && (sep != EMPTY_HEX_SEPARATOR)) {
-                sb.append(sep);
-            }
-            sb.append(HEX_DIGITS.charAt((b >> 4) & 0x0F));
-            sb.append(HEX_DIGITS.charAt(b & 0x0F));
-        }
-
-        return sb;
-    }
-
-    /**
-     * @param separator The separator between the HEX values - may be {@link #EMPTY_HEX_SEPARATOR}
-     * @param csq The {@link CharSequence} containing the HEX encoded bytes
-     * @return The decoded bytes
-     * @throws IllegalArgumentException If invalid HEX sequence length
-     * @throws NumberFormatException If invalid HEX characters found
-     * @see #decodeHex(char, CharSequence, int, int)
-     */
-    public static byte[] decodeHex(char separator, CharSequence csq) {
-        return decodeHex(separator, csq, 0, GenericUtils.length(csq));
-    }
-
-    /**
-     * @param separator The separator between the HEX values - may be {@link #EMPTY_HEX_SEPARATOR}
-     * @param csq The {@link CharSequence} containing the HEX encoded bytes
-     * @param start Start offset of the HEX sequence (inclusive)
-     * @param end End offset of the HEX sequence (exclusive)
-     * @return The decoded bytes
-     * @throws IllegalArgumentException If invalid HEX sequence length
-     * @throws NumberFormatException If invalid HEX characters found
-     */
-    public static byte[] decodeHex(char separator, CharSequence csq, int start, int end) {
-        int len = end - start;
-        ValidateUtils.checkTrue(len >= 0, "Bad HEX sequence length: %d", len);
-        if (len == 0) {
-            return GenericUtils.EMPTY_BYTE_ARRAY;
-        }
-
-        int delta = 2;
-        byte[] bytes;
-        if (separator != EMPTY_HEX_SEPARATOR) {
-            // last character cannot be the separator
-            ValidateUtils.checkTrue((len % 3) == 2, "Invalid separated HEX sequence length: %d", len);
-            bytes = new byte[(len + 1) / 3];
-            delta++;
-        } else {
-            ValidateUtils.checkTrue((len & 0x01) == 0, "Invalid contiguous HEX sequence length: %d", len);
-            bytes = new byte[len >>> 1];
-        }
-
-        int writeLen = 0;
-        for (int curPos = start; curPos < end; curPos += delta, writeLen++) {
-            bytes[writeLen] = fromHex(csq.charAt(curPos), csq.charAt(curPos + 1));
-        }
-        assert writeLen == bytes.length;
-
-        return bytes;
-    }
-
-    /**
-     * @param <S> The {@link OutputStream} generic type
-     * @param stream The target {@link OutputStream}
-     * @param separator The separator between the HEX values - may be {@link #EMPTY_HEX_SEPARATOR}
-     * @param csq The {@link CharSequence} containing the HEX encoded bytes
-     * @return The number of bytes written to the stream
-     * @throws IOException If failed to write
-     * @throws IllegalArgumentException If invalid HEX sequence length
-     * @throws NumberFormatException If invalid HEX characters found
-     * @see #decodeHex(OutputStream, char, CharSequence, int, int)
-     */
-    public static <S extends OutputStream> int decodeHex(S stream, char separator, CharSequence csq) throws IOException {
-        return decodeHex(stream, separator, csq, 0, GenericUtils.length(csq));
-    }
-
-    /**
-     * @param <S> The {@link OutputStream} generic type
-     * @param stream The target {@link OutputStream}
-     * @param separator The separator between the HEX values - may be {@link #EMPTY_HEX_SEPARATOR}
-     * @param csq The {@link CharSequence} containing the HEX encoded bytes
-     * @param start Start offset of the HEX sequence (inclusive)
-     * @param end End offset of the HEX sequence (exclusive)
-     * @return The number of bytes written to the stream
-     * @throws IOException If failed to write
-     * @throws IllegalArgumentException If invalid HEX sequence length
-     * @throws NumberFormatException If invalid HEX characters found
-     */
-    public static <S extends OutputStream> int decodeHex(S stream, char separator, CharSequence csq, int start, int end) throws IOException {
-        int len = end - start;
-        ValidateUtils.checkTrue(len >= 0, "Bad HEX sequence length: %d", len);
-
-        int delta = 2;
-        if (separator != EMPTY_HEX_SEPARATOR) {
-            // last character cannot be the separator
-            ValidateUtils.checkTrue((len % 3) == 2, "Invalid separated HEX sequence length: %d", len);
-            delta++;
-        } else {
-            ValidateUtils.checkTrue((len & 0x01) == 0, "Invalid contiguous HEX sequence length: %d", len);
-        }
-
-        int writeLen = 0;
-        for (int curPos = start; curPos < end; curPos += delta, writeLen++) {
-            stream.write(fromHex(csq.charAt(curPos), csq.charAt(curPos + 1)) & 0xFF);
-        }
-
-        return writeLen;
-    }
-
-    public static byte fromHex(char hi, char lo) throws NumberFormatException {
-        int hiValue = HEX_DIGITS.indexOf(((hi >= 'A') && (hi <= 'F')) ? ('a' + (hi - 'A')) : hi);
-        int loValue = HEX_DIGITS.indexOf(((lo >= 'A') && (lo <= 'F')) ? ('a' + (lo - 'A')) : lo);
-        if ((hiValue < 0) || (loValue < 0)) {
-            throw new NumberFormatException("fromHex(" + new String(new char[]{hi, lo}) + ") non-HEX characters");
-        }
-
-        return (byte) ((hiValue << 4) + loValue);
-    }
-
-    /**
-     * Read a 32-bit value in network order
-     *
-     * @param input The {@link InputStream}
-     * @param buf   Work buffer to use
-     * @return The read 32-bit value
-     * @throws IOException If failed to read 4 bytes or not enough room in
-     * @see #readInt(InputStream, byte[], int, int)
-     */
-    public static int readInt(InputStream input, byte[] buf) throws IOException {
-        return readInt(input, buf, 0, NumberUtils.length(buf));
-    }
-
-    /**
-     * Read a 32-bit value in network order
-     *
-     * @param input  The {@link InputStream}
-     * @param buf    Work buffer to use
-     * @param offset Offset in buffer to us
-     * @param len    Available length - must have at least 4 bytes available
-     * @return The read 32-bit value
-     * @throws IOException If failed to read 4 bytes or not enough room in
-     *                     work buffer
-     * @see #readUInt(InputStream, byte[], int, int)
-     */
-    public static int readInt(InputStream input, byte[] buf, int offset, int len) throws IOException {
-        return (int) readUInt(input, buf, offset, len);
-    }
-
-    /**
-     * Read a 32-bit value in network order
-     *
-     * @param input The {@link InputStream}
-     * @param buf   Work buffer to use
-     * @return The read 32-bit value
-     * @throws IOException If failed to read 4 bytes or not enough room in
-     * @see #readUInt(InputStream, byte[], int, int)
-     */
-    public static long readUInt(InputStream input, byte[] buf) throws IOException {
-        return readUInt(input, buf, 0, NumberUtils.length(buf));
-    }
-
-    /**
-     * Read a 32-bit value in network order
-     *
-     * @param input  The {@link InputStream}
-     * @param buf    Work buffer to use
-     * @param offset Offset in buffer to us
-     * @param len    Available length - must have at least 4 bytes available
-     * @return The read 32-bit value
-     * @throws IOException If failed to read 4 bytes or not enough room in
-     *                     work buffer
-     * @see #getUInt(byte[], int, int)
-     */
-    public static long readUInt(InputStream input, byte[] buf, int offset, int len) throws IOException {
-        try {
-            if (len < Integer.BYTES) {
-                throw new IllegalArgumentException("Not enough data for a UINT: required=" + Integer.BYTES + ", available=" + len);
-            }
-
-            IoUtils.readFully(input, buf, offset, Integer.BYTES);
-            return getUInt(buf, offset, len);
-        } catch (RuntimeException | Error e) {
-            throw new StreamCorruptedException("Failed (" + e.getClass().getSimpleName() + ")"
-                    + " to read UINT value: " + e.getMessage());
-        }
-    }
-
-    /**
-     * @param buf A buffer holding a 32-bit unsigned integer in <B>big endian</B>
-     *            format. <B>Note:</B> if more than 4 bytes are available, then only the
-     *            <U>first</U> 4 bytes in the buffer will be used
-     * @return The result as a {@code long} whose 32 high-order bits are zero
-     * @see #getUInt(byte[], int, int)
-     */
-    public static long getUInt(byte... buf) {
-        return getUInt(buf, 0, NumberUtils.length(buf));
-    }
-
-    /**
-     * @param buf A buffer holding a 32-bit unsigned integer in <B>big endian</B>
-     *            format.
-     * @param off The offset of the data in the buffer
-     * @param len The available data length. <B>Note:</B> if more than 4 bytes
-     *            are available, then only the <U>first</U> 4 bytes in the buffer will be
-     *            used (starting at the specified <tt>offset</tt>)
-     * @return The result as a {@code long} whose 32 high-order bits are zero
-     */
-    public static long getUInt(byte[] buf, int off, int len) {
-        if (len < Integer.BYTES) {
-            throw new IllegalArgumentException("Not enough data for a UINT: required=" + Integer.BYTES + ", available=" + len);
-        }
-
-        long l = (buf[off] << 24) & 0xff000000L;
-        l |= (buf[off + 1] << 16) & 0x00ff0000L;
-        l |= (buf[off + 2] << 8) & 0x0000ff00L;
-        l |= (buf[off + 3]) & 0x000000ffL;
-        return l;
-    }
-
-    /**
-     * Writes a 32-bit value in network order (i.e., MSB 1st)
-     *
-     * @param output The {@link OutputStream} to write the value
-     * @param value  The 32-bit value
-     * @param buf    A work buffer to use - must have enough space to contain 4 bytes
-     * @throws IOException If failed to write the value or work buffer to small
-     * @see #writeInt(OutputStream, int, byte[], int, int)
-     */
-    public static void writeInt(OutputStream output, int value, byte[] buf) throws IOException {
-        writeUInt(output, value, buf, 0, NumberUtils.length(buf));
-    }
-
-    /**
-     * Writes a 32-bit value in network order (i.e., MSB 1st)
-     *
-     * @param output The {@link OutputStream} to write the value
-     * @param value  The 32-bit value
-     * @param buf    A work buffer to use - must have enough space to contain 4 bytes
-     * @param off    The offset to write the value
-     * @param len    The available space
-     * @throws IOException If failed to write the value or work buffer to small
-     * @see #writeUInt(OutputStream, long, byte[], int, int)
-     */
-    public static void writeInt(OutputStream output, int value, byte[] buf, int off, int len) throws IOException {
-        writeUInt(output, value & 0xFFFFFFFFL, buf, off, len);
-    }
-
-    /**
-     * Writes a 32-bit value in network order (i.e., MSB 1st)
-     *
-     * @param output The {@link OutputStream} to write the value
-     * @param value  The 32-bit value
-     * @param buf    A work buffer to use - must have enough space to contain 4 bytes
-     * @throws IOException If failed to write the value or work buffer to small
-     * @see #writeUInt(OutputStream, long, byte[], int, int)
-     */
-    public static void writeUInt(OutputStream output, long value, byte[] buf) throws IOException {
-        writeUInt(output, value, buf, 0, NumberUtils.length(buf));
-    }
-
-    /**
-     * Writes a 32-bit value in network order (i.e., MSB 1st)
-     *
-     * @param output The {@link OutputStream} to write the value
-     * @param value  The 32-bit value
-     * @param buf    A work buffer to use - must have enough space to contain 4 bytes
-     * @param off    The offset to write the value
-     * @param len    The available space
-     * @throws IOException If failed to write the value or work buffer to small
-     * @see #putUInt(long, byte[], int, int)
-     */
-    public static void writeUInt(OutputStream output, long value, byte[] buf, int off, int len) throws IOException {
-        try {
-            int writeLen = putUInt(value, buf, off, len);
-            output.write(buf, off, writeLen);
-        } catch (RuntimeException | Error e) {
-            throw new StreamCorruptedException("Failed (" + e.getClass().getSimpleName() + ")"
-                    + " to write UINT value=" + value + ": " + e.getMessage());
-        }
-    }
-
-    /**
-     * Writes a 32-bit value in network order (i.e., MSB 1st)
-     *
-     * @param value The 32-bit value
-     * @param buf   The buffer
-     * @return The number of bytes used in the buffer
-     * @throws IllegalArgumentException if not enough space available
-     * @see #putUInt(long, byte[], int, int)
-     */
-    public static int putUInt(long value, byte[] buf) {
-        return putUInt(value, buf, 0, NumberUtils.length(buf));
-    }
-
-    /**
-     * Writes a 32-bit value in network order (i.e., MSB 1st)
-     *
-     * @param value The 32-bit value
-     * @param buf   The buffer
-     * @param off   The offset to write the value
-     * @param len   The available space
-     * @return The number of bytes used in the buffer
-     * @throws IllegalArgumentException if not enough space available
-     */
-    public static int putUInt(long value, byte[] buf, int off, int len) {
-        if (len < Integer.BYTES) {
-            throw new IllegalArgumentException("Not enough data for a UINT: required=" + Integer.BYTES + ", available=" + len);
-        }
-
-        buf[off] = (byte) ((value >> 24) & 0xFF);
-        buf[off + 1] = (byte) ((value >> 16) & 0xFF);
-        buf[off + 2] = (byte) ((value >> 8) & 0xFF);
-        buf[off + 3] = (byte) (value & 0xFF);
-
-        return Integer.BYTES;
-    }
-
-    public static boolean equals(byte[] a1, byte[] a2) {
-        int len1 = NumberUtils.length(a1);
-        int len2 = NumberUtils.length(a2);
-        if (len1 != len2) {
-            return false;
-        } else {
-            return equals(a1, 0, a2, 0, len1);
-        }
-    }
-
-    @SuppressWarnings("PMD.AssignmentInOperand")
-    public static boolean equals(byte[] a1, int a1Offset, byte[] a2, int a2Offset, int length) {
-        int len1 = NumberUtils.length(a1);
-        int len2 = NumberUtils.length(a2);
-        if ((len1 < (a1Offset + length)) || (len2 < (a2Offset + length))) {
-            return false;
-        }
-
-        while (length-- > 0) {
-            if (a1[a1Offset++] != a2[a2Offset++]) {
-                return false;
-            }
-        }
-
-        return true;
-    }
-
-    public static int getNextPowerOf2(int value) {
-        // for 0-7 return 8
-        return (value < Byte.SIZE) ? Byte.SIZE : NumberUtils.getNextPowerOf2(value);
-    }
-
-    /**
-     * Used for encodings where we don't know the data length before adding it
-     * to the buffer. The idea is to place a 32-bit &quot;placeholder&quot;,
-     * encode the data and then return back to the placeholder and update the
-     * length. The method calculates the encoded data length, moves the write
-     * position to the specified placeholder position, updates the length value
-     * and then moves the write position it back to its original value.
-     *
-     * @param buffer The {@link Buffer}
-     * @param lenPos The offset in the buffer where the length placeholder is
-     *               to be update - <B>Note:</B> assumption is that the encoded data starts
-     *               <U>immediately</U> after the placeholder
-     * @return The amount of data that has been encoded
-     */
-    public static int updateLengthPlaceholder(Buffer buffer, int lenPos) {
-        int startPos = lenPos + Integer.BYTES;
-        int endPos = buffer.wpos();
-        int dataLength = endPos - startPos;
-        // NOTE: although data length is defined as UINT32, we do not expected sizes above Integer.MAX_VALUE
-        ValidateUtils.checkTrue(dataLength >= 0, "Illegal data length: %d", dataLength);
-        buffer.wpos(lenPos);
-        buffer.putInt(dataLength);
-        buffer.wpos(endPos);
-        return dataLength;
-    }
-
-    /**
-     * Updates a 32-bit &quot;placeholder&quot; location for data length - moves
-     * the write position to the specified placeholder position, updates the length
-     * value and then moves the write position it back to its original value.
-     *
-     * @param buffer     The {@link Buffer}
-     * @param lenPos     The offset in the buffer where the length placeholder is
-     *                   to be update - <B>Note:</B> assumption is that the encoded data starts
-     *                   <U>immediately</U> after the placeholder
-     * @param dataLength The length to update
-     */
-    public static void updateLengthPlaceholder(Buffer buffer, int lenPos, int dataLength) {
-        int curPos = buffer.wpos();
-        buffer.wpos(lenPos);
-        buffer.putInt(dataLength);
-        buffer.wpos(curPos);
-    }
-
-    /**
-     * Invokes {@link Buffer#clear()}
-     *
-     * @param <B>    The generic buffer type
-     * @param buffer A {@link Buffer} instance - ignored if {@code null}
-     * @return The same as the input instance
-     */
-    public static <B extends Buffer> B clear(B buffer) {
-        if (buffer != null) {
-            buffer.clear();
-        }
-
-        return buffer;
-    }
-
-    public static long validateInt32Value(long value, String message) {
-        ValidateUtils.checkTrue(isValidInt32Value(value), message, value);
-        return value;
-    }
-
-    public static long validateInt32Value(long value, String format, Object arg) {
-        ValidateUtils.checkTrue(isValidInt32Value(value), format, arg);
-        return value;
-    }
-
-    public static long validateInt32Value(long value, String format, Object... args) {
-        ValidateUtils.checkTrue(isValidInt32Value(value), format, args);
-        return value;
-    }
-
-    public static boolean isValidInt32Value(long value) {
-        return (value >= Integer.MIN_VALUE) && (value <= Integer.MAX_VALUE);
-    }
-
-    public static long validateUint32Value(long value, String message) {
-        ValidateUtils.checkTrue(isValidUint32Value(value), message, value);
-        return value;
-    }
-
-    public static long validateUint32Value(long value, String format, Object arg) {
-        ValidateUtils.checkTrue(isValidUint32Value(value), format, arg);
-        return value;
-    }
-
-    public static long validateUint32Value(long value, String format, Object... args) {
-        ValidateUtils.checkTrue(isValidUint32Value(value), format, args);
-        return value;
-    }
-
-    public static boolean isValidUint32Value(long value) {
-        return (value >= 0L) && (value <= MAX_UINT32_VALUE);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/ByteArrayBuffer.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/ByteArrayBuffer.java b/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/ByteArrayBuffer.java
deleted file mode 100644
index 6655ec1..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/ByteArrayBuffer.java
+++ /dev/null
@@ -1,253 +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.buffer;
-
-import java.nio.ByteBuffer;
-import java.nio.charset.Charset;
-import java.util.Arrays;
-import java.util.Objects;
-import java.util.function.IntUnaryOperator;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.NumberUtils;
-import org.apache.sshd.common.util.Readable;
-import org.apache.sshd.common.util.ValidateUtils;
-
-/**
- * Provides an implementation of {@link Buffer} using a backing byte array
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class ByteArrayBuffer extends Buffer {
-    public static final int DEFAULT_SIZE = 256;
-    public static final int MAX_LEN = 65536;
-
-    private byte[] data;
-    private int rpos;
-    private int wpos;
-
-    public ByteArrayBuffer() {
-        this(DEFAULT_SIZE);
-    }
-
-    public ByteArrayBuffer(int size) {
-        this(size, true);
-    }
-
-    public ByteArrayBuffer(int size, boolean roundOff) {
-        this(new byte[roundOff ? BufferUtils.getNextPowerOf2(size) : size], false);
-    }
-
-    public ByteArrayBuffer(byte[] data) {
-        this(data, 0, data.length, true);
-    }
-
-    public ByteArrayBuffer(byte[] data, boolean read) {
-        this(data, 0, data.length, read);
-    }
-
-    public ByteArrayBuffer(byte[] data, int off, int len) {
-        this(data, off, len, true);
-    }
-
-    public ByteArrayBuffer(byte[] data, int off, int len, boolean read) {
-        this.data = data;
-        this.rpos = off;
-        this.wpos = (read ? len : 0) + off;
-    }
-
-    @Override
-    public int rpos() {
-        return rpos;
-    }
-
-    @Override
-    public void rpos(int rpos) {
-        this.rpos = rpos;
-    }
-
-    @Override
-    public int wpos() {
-        return wpos;
-    }
-
-    @Override
-    public void wpos(int wpos) {
-        if (wpos > this.wpos) {
-            ensureCapacity(wpos - this.wpos);
-        }
-        this.wpos = wpos;
-    }
-
-    @Override
-    public int available() {
-        return wpos - rpos;
-    }
-
-    @Override
-    public int capacity() {
-        return data.length - wpos;
-    }
-
-    @Override
-    public byte[] array() {
-        return data;
-    }
-
-    @Override
-    public void compact() {
-        int avail = available();
-        if (avail > 0) {
-            System.arraycopy(data, rpos, data, 0, avail);
-        }
-        wpos -= rpos;
-        rpos = 0;
-    }
-
-    @Override
-    public void clear(boolean wipeData) {
-        rpos = 0;
-        wpos = 0;
-
-        if (wipeData) {
-            Arrays.fill(data, (byte) 0);
-        }
-    }
-
-    @Override
-    public byte getByte() {
-        ensureAvailable(Byte.BYTES);
-        return data[rpos++];
-    }
-
-    @Override
-    public void putByte(byte b) {
-        ensureCapacity(Byte.BYTES);
-        data[wpos++] = b;
-    }
-
-    @Override
-    public int putBuffer(Readable buffer, boolean expand) {
-        int r = expand ? buffer.available() : Math.min(buffer.available(), capacity());
-        ensureCapacity(r);
-        buffer.getRawBytes(data, wpos, r);
-        wpos += r;
-        return r;
-    }
-
-    @Override
-    public void putBuffer(ByteBuffer buffer) {
-        int required = buffer.remaining();
-        ensureCapacity(required + Integer.SIZE);
-        putInt(required);
-        buffer.get(data, wpos, required);
-        wpos += required;
-    }
-
-    @Override
-    public void putRawBytes(byte[] d, int off, int len) {
-        ValidateUtils.checkTrue(len >= 0, "Negative raw bytes length: %d", len);
-        ensureCapacity(len);
-        System.arraycopy(d, off, data, wpos, len);
-        wpos += len;
-    }
-
-    @Override
-    public String getString(Charset charset) {
-        int len = getInt();
-        if (len < 0) {
-            throw new BufferException("Bad item length: " + len);
-        }
-        ensureAvailable(len);
-
-        Objects.requireNonNull(charset, "No charset specified");
-        String s = new String(data, rpos, len, charset);
-        rpos += len;
-        return s;
-    }
-
-    @Override
-    public void getRawBytes(byte[] buf, int off, int len) {
-        ensureAvailable(len);
-        copyRawBytes(0, buf, off, len);
-        rpos += len;
-    }
-
-    @Override
-    protected void copyRawBytes(int offset, byte[] buf, int pos, int len) {
-        System.arraycopy(data, rpos + offset, buf, pos, len);
-    }
-
-    @Override
-    public void ensureCapacity(int capacity, IntUnaryOperator growthFactor) {
-        ValidateUtils.checkTrue(capacity >= 0, "Negative capacity requested: %d", capacity);
-
-        int maxSize = size();
-        int curPos = wpos();
-        int remaining = maxSize - curPos;
-        if (remaining < capacity) {
-            int minimum = curPos + capacity;
-            int actual = growthFactor.applyAsInt(minimum);
-            if (actual < minimum) {
-                throw new IllegalStateException("ensureCapacity(" + capacity + ") actual (" + actual + ") below min. (" + minimum + ")");
-            }
-            byte[] tmp = new byte[actual];
-            System.arraycopy(data, 0, tmp, 0, data.length);
-            data = tmp;
-        }
-    }
-
-    @Override
-    protected int size() {
-        return data.length;
-    }
-
-    /**
-     * Creates a compact buffer (i.e., one that starts at offset zero) containing a <U>copy</U>
-     * of the original data
-     *
-     * @param data   The original data buffer
-     * @return A {@link ByteArrayBuffer} containing a <U>copy</U> of the original data
-     * starting at zero read position
-     * @see #getCompactClone(byte[], int, int)
-     */
-    public static ByteArrayBuffer getCompactClone(byte[] data) {
-        return getCompactClone(data, 0, NumberUtils.length(data));
-    }
-
-    /**
-     * Creates a compact buffer (i.e., one that starts at offset zero) containing a <U>copy</U>
-     * of the original data
-     *
-     * @param data   The original data buffer
-     * @param offset The offset of the valid data in the buffer
-     * @param len    The size (in bytes) of of the valid data in the buffer
-     * @return A {@link ByteArrayBuffer} containing a <U>copy</U> of the original data
-     * starting at zero read position
-     */
-    public static ByteArrayBuffer getCompactClone(byte[] data, int offset, int len) {
-        byte[] clone = (len > 0) ? new byte[len] : GenericUtils.EMPTY_BYTE_ARRAY;
-        if (len > 0) {
-            System.arraycopy(data, offset, clone, 0, len);
-        }
-
-        return new ByteArrayBuffer(clone, true);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/keys/AbstractBufferPublicKeyParser.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/keys/AbstractBufferPublicKeyParser.java b/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/keys/AbstractBufferPublicKeyParser.java
deleted file mode 100644
index aaf9641..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/keys/AbstractBufferPublicKeyParser.java
+++ /dev/null
@@ -1,88 +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.buffer.keys;
-
-import java.security.GeneralSecurityException;
-import java.security.KeyFactory;
-import java.security.PublicKey;
-import java.security.spec.InvalidKeySpecException;
-import java.security.spec.KeySpec;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Objects;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * @param <PUB> Type of {@link PublicKey} being extracted
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class AbstractBufferPublicKeyParser<PUB extends PublicKey> implements BufferPublicKeyParser<PUB> {
-    private final Class<PUB> keyClass;
-    private final Collection<String> supported;
-
-    protected AbstractBufferPublicKeyParser(Class<PUB> keyClass, String... supported) {
-        this(keyClass, GenericUtils.isEmpty(supported) ? Collections.emptyList() : Arrays.asList(supported));
-    }
-
-    protected AbstractBufferPublicKeyParser(Class<PUB> keyClass, Collection<String> supported) {
-        this.keyClass = Objects.requireNonNull(keyClass, "No key class");
-        this.supported = ValidateUtils.checkNotNullAndNotEmpty(supported, "No supported types for %s", keyClass.getSimpleName());
-    }
-
-    public Collection<String> getSupportedKeyTypes() {
-        return supported;
-    }
-
-    public final Class<PUB> getKeyClass() {
-        return keyClass;
-    }
-
-    @Override
-    public boolean isKeyTypeSupported(String keyType) {
-        Collection<String> keys = getSupportedKeyTypes();
-        return (GenericUtils.length(keyType) > 0)
-            && (GenericUtils.size(keys) > 0)
-            && keys.contains(keyType);
-    }
-
-    protected <S extends KeySpec> PUB generatePublicKey(String algorithm, S keySpec) throws GeneralSecurityException {
-        KeyFactory keyFactory = getKeyFactory(algorithm);
-        PublicKey key = keyFactory.generatePublic(keySpec);
-        Class<PUB> kc = getKeyClass();
-        if (!kc.isInstance(key)) {
-            throw new InvalidKeySpecException("Mismatched generated key types: expected=" + kc.getSimpleName() + ", actual=" + key);
-        }
-
-        return kc.cast(key);
-    }
-
-    protected KeyFactory getKeyFactory(String algorithm) throws GeneralSecurityException {
-        return SecurityUtils.getKeyFactory(algorithm);
-    }
-
-    @Override
-    public String toString() {
-        return getClass().getSimpleName() + " - supported=" + getSupportedKeyTypes();
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/keys/BufferPublicKeyParser.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/keys/BufferPublicKeyParser.java b/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/keys/BufferPublicKeyParser.java
deleted file mode 100644
index 6f7cde6..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/keys/BufferPublicKeyParser.java
+++ /dev/null
@@ -1,111 +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.buffer.keys;
-
-import java.security.GeneralSecurityException;
-import java.security.NoSuchAlgorithmException;
-import java.security.PublicKey;
-import java.util.Arrays;
-import java.util.Collection;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.buffer.Buffer;
-
-/**
- * Parses a raw {@link PublicKey} from a {@link Buffer}
- *
- * @param <PUB> Type of {@link PublicKey} being extracted
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface BufferPublicKeyParser<PUB extends PublicKey> {
-
-    BufferPublicKeyParser<PublicKey> EMPTY = new BufferPublicKeyParser<PublicKey>() {
-        @Override
-        public boolean isKeyTypeSupported(String keyType) {
-            return false;
-        }
-
-        @Override
-        public PublicKey getRawPublicKey(String keyType, Buffer buffer) throws GeneralSecurityException {
-            throw new NoSuchAlgorithmException(keyType);
-        }
-
-        @Override
-        public String toString() {
-            return "EMPTY";
-        }
-    };
-
-    BufferPublicKeyParser<PublicKey> DEFAULT = aggregate(
-            Arrays.asList(
-                    RSABufferPublicKeyParser.INSTANCE,
-                    DSSBufferPublicKeyParser.INSTANCE,
-                    ECBufferPublicKeyParser.INSTANCE,
-                    ED25519BufferPublicKeyParser.INSTANCE));
-
-    /**
-     * @param keyType The key type - e.g., &quot;ssh-rsa&quot, &quot;ssh-dss&quot;
-     * @return {@code true} if this key type is supported by the parser
-     */
-    boolean isKeyTypeSupported(String keyType);
-
-    /**
-     * @param keyType The key type - e.g., &quot;ssh-rsa&quot, &quot;ssh-dss&quot;
-     * @param buffer The {@link Buffer} containing the encoded raw public key
-     * @return The decoded {@link PublicKey}
-     * @throws GeneralSecurityException If failed to generate the key
-     */
-    PUB getRawPublicKey(String keyType, Buffer buffer) throws GeneralSecurityException;
-
-    static BufferPublicKeyParser<PublicKey> aggregate(Collection<? extends BufferPublicKeyParser<? extends PublicKey>> parsers) {
-        if (GenericUtils.isEmpty(parsers)) {
-            return EMPTY;
-        }
-
-        return new BufferPublicKeyParser<PublicKey>() {
-            @Override
-            public boolean isKeyTypeSupported(String keyType) {
-                for (BufferPublicKeyParser<? extends PublicKey> p : parsers) {
-                    if (p.isKeyTypeSupported(keyType)) {
-                        return true;
-                    }
-                }
-
-                return false;
-            }
-
-            @Override
-            public PublicKey getRawPublicKey(String keyType, Buffer buffer) throws GeneralSecurityException {
-                for (BufferPublicKeyParser<? extends PublicKey> p : parsers) {
-                    if (p.isKeyTypeSupported(keyType)) {
-                        return p.getRawPublicKey(keyType, buffer);
-                    }
-                }
-
-                throw new NoSuchAlgorithmException("No aggregate matcher for " + keyType);
-            }
-
-            @Override
-            public String toString() {
-                return String.valueOf(parsers);
-            }
-        };
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/keys/DSSBufferPublicKeyParser.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/keys/DSSBufferPublicKeyParser.java b/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/keys/DSSBufferPublicKeyParser.java
deleted file mode 100644
index 4eec49a..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/keys/DSSBufferPublicKeyParser.java
+++ /dev/null
@@ -1,52 +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.buffer.keys;
-
-import java.math.BigInteger;
-import java.security.GeneralSecurityException;
-import java.security.interfaces.DSAPublicKey;
-import java.security.spec.DSAPublicKeySpec;
-
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.buffer.Buffer;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class DSSBufferPublicKeyParser extends AbstractBufferPublicKeyParser<DSAPublicKey> {
-    public static final DSSBufferPublicKeyParser INSTANCE = new DSSBufferPublicKeyParser();
-
-    public DSSBufferPublicKeyParser() {
-        super(DSAPublicKey.class, KeyPairProvider.SSH_DSS);
-    }
-
-    @Override
-    public DSAPublicKey getRawPublicKey(String keyType, Buffer buffer) throws GeneralSecurityException {
-        ValidateUtils.checkTrue(isKeyTypeSupported(keyType), "Unsupported key type: %s", keyType);
-        BigInteger p = buffer.getMPInt();
-        BigInteger q = buffer.getMPInt();
-        BigInteger g = buffer.getMPInt();
-        BigInteger y = buffer.getMPInt();
-
-        return generatePublicKey(KeyUtils.DSS_ALGORITHM, new DSAPublicKeySpec(y, p, q, g));
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/keys/ECBufferPublicKeyParser.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/keys/ECBufferPublicKeyParser.java b/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/keys/ECBufferPublicKeyParser.java
deleted file mode 100644
index df0115a..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/keys/ECBufferPublicKeyParser.java
+++ /dev/null
@@ -1,81 +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.buffer.keys;
-
-import java.security.GeneralSecurityException;
-import java.security.NoSuchAlgorithmException;
-import java.security.interfaces.ECPublicKey;
-import java.security.spec.ECParameterSpec;
-import java.security.spec.ECPoint;
-import java.security.spec.ECPublicKeySpec;
-import java.security.spec.InvalidKeySpecException;
-
-import org.apache.sshd.common.cipher.ECCurves;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.buffer.Buffer;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class ECBufferPublicKeyParser extends AbstractBufferPublicKeyParser<ECPublicKey> {
-    public static final ECBufferPublicKeyParser INSTANCE = new ECBufferPublicKeyParser();
-
-    public ECBufferPublicKeyParser() {
-        super(ECPublicKey.class, ECCurves.KEY_TYPES);
-    }
-
-    @Override
-    public ECPublicKey getRawPublicKey(String keyType, Buffer buffer) throws GeneralSecurityException {
-        ValidateUtils.checkTrue(isKeyTypeSupported(keyType), "Unsupported key type: %s", keyType);
-        ECCurves curve = ECCurves.fromKeyType(keyType);
-        if (curve == null) {
-            throw new NoSuchAlgorithmException("Unsupported raw public algorithm: " + keyType);
-        }
-
-        String curveName = curve.getName();
-        ECParameterSpec params = curve.getParameters();
-        return getRawECKey(curveName, params, buffer);
-    }
-
-    protected ECPublicKey getRawECKey(String expectedCurve, ECParameterSpec spec, Buffer buffer) throws GeneralSecurityException {
-        String curveName = buffer.getString();
-        if (!expectedCurve.equals(curveName)) {
-            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 = buffer.getBytes();
-        ECPoint w;
-        try {
-            w = ECCurves.octetStringToEcPoint(octets);
-        } catch (RuntimeException e) {
-            throw new InvalidKeySpecException("getRawECKey(" + expectedCurve + ")"
-                    + " cannot (" + e.getClass().getSimpleName() + ")"
-                    + " retrieve W value: " + e.getMessage(),
-                    e);
-        }
-
-        return generatePublicKey(KeyUtils.EC_ALGORITHM, new ECPublicKeySpec(w, spec));
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/keys/ED25519BufferPublicKeyParser.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/keys/ED25519BufferPublicKeyParser.java b/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/keys/ED25519BufferPublicKeyParser.java
deleted file mode 100644
index 61ce6ab..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/keys/ED25519BufferPublicKeyParser.java
+++ /dev/null
@@ -1,47 +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.buffer.keys;
-
-import java.security.GeneralSecurityException;
-import java.security.PublicKey;
-
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.buffer.Buffer;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * TODO complete this when SSHD-440 is done
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class ED25519BufferPublicKeyParser extends AbstractBufferPublicKeyParser<PublicKey> {
-    public static final ED25519BufferPublicKeyParser INSTANCE = new ED25519BufferPublicKeyParser();
-
-    public ED25519BufferPublicKeyParser() {
-        super(PublicKey.class, KeyPairProvider.SSH_ED25519);
-    }
-
-    @Override
-    public PublicKey getRawPublicKey(String keyType, Buffer buffer) throws GeneralSecurityException {
-        ValidateUtils.checkTrue(isKeyTypeSupported(keyType), "Unsupported key type: %s", keyType);
-        byte[] seed = buffer.getBytes();
-        return SecurityUtils.generateEDDSAPublicKey(keyType, seed);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/keys/RSABufferPublicKeyParser.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/keys/RSABufferPublicKeyParser.java b/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/keys/RSABufferPublicKeyParser.java
deleted file mode 100644
index 363b07e..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/keys/RSABufferPublicKeyParser.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.buffer.keys;
-
-import java.math.BigInteger;
-import java.security.GeneralSecurityException;
-import java.security.interfaces.RSAPublicKey;
-import java.security.spec.RSAPublicKeySpec;
-
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.buffer.Buffer;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class RSABufferPublicKeyParser extends AbstractBufferPublicKeyParser<RSAPublicKey> {
-    public static final RSABufferPublicKeyParser INSTANCE = new RSABufferPublicKeyParser();
-
-    public RSABufferPublicKeyParser() {
-        super(RSAPublicKey.class, KeyPairProvider.SSH_RSA);
-    }
-
-    @Override
-    public RSAPublicKey getRawPublicKey(String keyType, Buffer buffer) throws GeneralSecurityException {
-        ValidateUtils.checkTrue(isKeyTypeSupported(keyType), "Unsupported key type: %s", keyType);
-        BigInteger e = buffer.getMPInt();
-        BigInteger n = buffer.getMPInt();
-        return generatePublicKey(KeyUtils.RSA_ALGORITHM, new RSAPublicKeySpec(n, e));
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/AbstractCloseable.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/AbstractCloseable.java b/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/AbstractCloseable.java
deleted file mode 100644
index 6413ebb..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/AbstractCloseable.java
+++ /dev/null
@@ -1,162 +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.closeable;
-
-import java.util.concurrent.atomic.AtomicReference;
-
-import org.apache.sshd.common.future.CloseFuture;
-import org.apache.sshd.common.future.DefaultCloseFuture;
-import org.apache.sshd.common.future.SshFuture;
-import org.apache.sshd.common.future.SshFutureListener;
-
-/**
- * Provides some default implementations
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class AbstractCloseable extends IoBaseCloseable {
-
-    public enum State {
-        Opened, Graceful, Immediate, Closed
-    }
-
-    /**
-     * Lock object for this session state
-     */
-    protected final Object lock = new Object();
-
-    /**
-     * State of this object
-     */
-    protected final AtomicReference<AbstractCloseable.State> state = new AtomicReference<>(State.Opened);
-
-    /**
-     * A future that will be set 'closed' when the object is actually closed
-     */
-    protected final CloseFuture closeFuture;
-
-    protected AbstractCloseable() {
-        this("");
-    }
-
-    protected AbstractCloseable(String discriminator) {
-        super(discriminator);
-        closeFuture = new DefaultCloseFuture(discriminator, lock);
-    }
-
-    @Override
-    public void addCloseFutureListener(SshFutureListener<CloseFuture> listener) {
-        closeFuture.addListener(listener);
-    }
-
-    @Override
-    public void removeCloseFutureListener(SshFutureListener<CloseFuture> listener) {
-        closeFuture.removeListener(listener);
-    }
-
-    @Override
-    public final CloseFuture close(boolean immediately) {
-        boolean debugEnabled = log.isDebugEnabled();
-        if (immediately) {
-            if (state.compareAndSet(State.Opened, State.Immediate)
-                    || state.compareAndSet(State.Graceful, State.Immediate)) {
-                if (debugEnabled) {
-                    log.debug("close({}) Closing immediately", this);
-                }
-                preClose();
-                doCloseImmediately();
-                if (debugEnabled) {
-                    log.debug("close({})[Immediately] closed", this);
-                }
-            } else {
-                if (debugEnabled) {
-                    log.debug("close({})[Immediately] state already {}", this, state.get());
-                }
-            }
-        } else {
-            if (state.compareAndSet(State.Opened, State.Graceful)) {
-                if (debugEnabled) {
-                    log.debug("close({}) Closing gracefully", this);
-                }
-                preClose();
-                SshFuture<CloseFuture> grace = doCloseGracefully();
-                if (grace != null) {
-                    grace.addListener(future -> {
-                        if (state.compareAndSet(State.Graceful, State.Immediate)) {
-                            doCloseImmediately();
-                            if (debugEnabled) {
-                                log.debug("close({}][Graceful] - operationComplete() closed", AbstractCloseable.this);
-                            }
-                        }
-                    });
-                } else {
-                    if (state.compareAndSet(State.Graceful, State.Immediate)) {
-                        doCloseImmediately();
-                        if (debugEnabled) {
-                            log.debug("close({})[Graceful] closed", this);
-                        }
-                    }
-                }
-            } else {
-                if (debugEnabled) {
-                    log.debug("close({})[Graceful] state already {}", this, state.get());
-                }
-            }
-        }
-        return closeFuture;
-    }
-
-    @Override
-    public final boolean isClosed() {
-        return state.get() == State.Closed;
-    }
-
-    @Override
-    public final boolean isClosing() {
-        return state.get() != State.Opened;
-    }
-
-    /**
-     * preClose is guaranteed to be called before doCloseGracefully or doCloseImmediately.
-     * When preClose() is called, isClosing() == true
-     */
-    protected void preClose() {
-        // nothing
-    }
-
-    protected CloseFuture doCloseGracefully() {
-        return null;
-    }
-
-    /**
-     * <P>doCloseImmediately is called once and only once
-     * with state == Immediate</P>
-     *
-     * <P>Overriding methods should always call the base implementation.
-     * It may be called concurrently while preClose() or doCloseGracefully is executing</P>
-     */
-    protected void doCloseImmediately() {
-        closeFuture.setClosed();
-        state.set(State.Closed);
-    }
-
-    protected Builder builder() {
-        return new Builder(lock);
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/AbstractInnerCloseable.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/AbstractInnerCloseable.java b/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/AbstractInnerCloseable.java
deleted file mode 100644
index 6518d23..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/AbstractInnerCloseable.java
+++ /dev/null
@@ -1,48 +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.closeable;
-
-import org.apache.sshd.common.Closeable;
-import org.apache.sshd.common.future.CloseFuture;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class AbstractInnerCloseable extends AbstractCloseable {
-    protected AbstractInnerCloseable() {
-        this("");
-    }
-
-    protected AbstractInnerCloseable(String discriminator) {
-        super(discriminator);
-    }
-
-    protected abstract Closeable getInnerCloseable();
-
-    @Override
-    protected final CloseFuture doCloseGracefully() {
-        return getInnerCloseable().close(false);
-    }
-
-    @Override
-    @SuppressWarnings("synthetic-access")
-    protected final void doCloseImmediately() {
-        getInnerCloseable().close(true).addListener(future -> AbstractInnerCloseable.super.doCloseImmediately());
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/Builder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/Builder.java b/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/Builder.java
deleted file mode 100644
index 847d49c..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/Builder.java
+++ /dev/null
@@ -1,115 +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.closeable;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
-
-import org.apache.sshd.common.Closeable;
-import org.apache.sshd.common.future.SshFuture;
-import org.apache.sshd.common.util.ObjectBuilder;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public final class Builder implements ObjectBuilder<Closeable> {
-    private final Object lock;
-    private final List<Closeable> closeables = new ArrayList<>();
-
-    public Builder(Object lock) {
-        this.lock = Objects.requireNonNull(lock, "No lock");
-    }
-
-    public Builder run(Object id, Runnable r) {
-        return close(new SimpleCloseable(id, lock) {
-            @Override
-            protected void doClose(boolean immediately) {
-                try {
-                    r.run();
-                } finally {
-                    super.doClose(immediately);
-                }
-            }
-        });
-    }
-
-    @SuppressWarnings("rawtypes")
-    public <T extends SshFuture> Builder when(SshFuture<T> future) {
-        if (future != null) {
-            when(future.getId(), Collections.singleton(future));
-        }
-        return this;
-    }
-
-    @SuppressWarnings("rawtypes")
-    @SafeVarargs
-    public final <T extends SshFuture> Builder when(SshFuture<T>... futures) {
-        return when(getClass().getSimpleName(), Arrays.asList(futures));
-    }
-
-    @SuppressWarnings("rawtypes")
-    public <T extends SshFuture> Builder when(Object id, Iterable<? extends SshFuture<T>> futures) {
-        return close(new FuturesCloseable<>(id, lock, futures));
-    }
-
-    public Builder sequential(Closeable... closeables) {
-        for (Closeable closeable : closeables) {
-            close(closeable);
-        }
-        return this;
-    }
-
-    public Builder sequential(Object id, Iterable<Closeable> closeables) {
-        return close(new SequentialCloseable(id, lock, closeables));
-    }
-
-    public Builder parallel(Closeable... closeables) {
-        if (closeables.length == 1) {
-            close(closeables[0]);
-        } else if (closeables.length > 0) {
-            parallel(getClass().getSimpleName(), Arrays.asList(closeables));
-        }
-        return this;
-    }
-
-    public Builder parallel(Object id, Iterable<? extends Closeable> closeables) {
-        return close(new ParallelCloseable(id, lock, closeables));
-    }
-
-    public Builder close(Closeable c) {
-        if (c != null) {
-            closeables.add(c);
-        }
-        return this;
-    }
-
-    @Override
-    public Closeable build() {
-        if (closeables.isEmpty()) {
-            return new SimpleCloseable(getClass().getSimpleName(), lock);
-        } else if (closeables.size() == 1) {
-            return closeables.get(0);
-        } else {
-            return new SequentialCloseable(getClass().getSimpleName(), lock, closeables);
-        }
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/FuturesCloseable.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/FuturesCloseable.java b/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/FuturesCloseable.java
deleted file mode 100644
index af765b7..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/FuturesCloseable.java
+++ /dev/null
@@ -1,76 +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.closeable;
-
-import java.util.Collections;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import org.apache.sshd.common.SshException;
-import org.apache.sshd.common.future.DefaultSshFuture;
-import org.apache.sshd.common.future.SshFuture;
-import org.apache.sshd.common.future.SshFutureListener;
-
-/**
- * @param <T> Type of future
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class FuturesCloseable<T extends SshFuture> extends SimpleCloseable {
-
-    private final Iterable<? extends SshFuture<T>> futures;
-
-    public FuturesCloseable(Object id, Object lock, Iterable<? extends SshFuture<T>> futures) {
-        super(id, lock);
-        this.futures = (futures == null) ? Collections.emptyList() : futures;
-    }
-
-    @Override
-    protected void doClose(boolean immediately) {
-        if (immediately) {
-            for (SshFuture<?> f : futures) {
-                if (f instanceof DefaultSshFuture) {
-                    ((DefaultSshFuture<?>) f).setValue(new SshException("Closed"));
-                }
-            }
-            future.setClosed();
-        } else {
-            AtomicInteger count = new AtomicInteger(1);
-            boolean traceEnabled = log.isTraceEnabled();
-            SshFutureListener<T> listener = f -> {
-                int pendingCount = count.decrementAndGet();
-                if (traceEnabled) {
-                    log.trace("doClose(" + immediately + ") complete pending: " + pendingCount);
-                }
-                if (pendingCount == 0) {
-                    future.setClosed();
-                }
-            };
-
-            for (SshFuture<T> f : futures) {
-                if (f != null) {
-                    int pendingCount = count.incrementAndGet();
-                    if (traceEnabled) {
-                        log.trace("doClose(" + immediately + ") future pending: " + pendingCount);
-                    }
-                    f.addListener(listener);
-                }
-            }
-            listener.operationComplete(null);
-        }
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/IoBaseCloseable.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/IoBaseCloseable.java b/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/IoBaseCloseable.java
deleted file mode 100644
index f4c6d1a..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/IoBaseCloseable.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sshd.common.util.closeable;
-
-import org.apache.sshd.common.Closeable;
-import org.apache.sshd.common.util.logging.AbstractLoggingBean;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class IoBaseCloseable extends AbstractLoggingBean implements Closeable {
-    protected IoBaseCloseable() {
-        this("");
-    }
-
-    protected IoBaseCloseable(String discriminator) {
-        super(discriminator);
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/ParallelCloseable.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/ParallelCloseable.java b/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/ParallelCloseable.java
deleted file mode 100644
index 0900cd7..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/ParallelCloseable.java
+++ /dev/null
@@ -1,73 +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.closeable;
-
-import java.util.Collections;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import org.apache.sshd.common.Closeable;
-import org.apache.sshd.common.future.CloseFuture;
-import org.apache.sshd.common.future.SshFutureListener;
-
-/**
- * Waits for a group of {@link Closeable}s to complete in any order, then
- * signals the completion by setting the &quot;parent&quot; future as closed
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class ParallelCloseable extends SimpleCloseable {
-    private final Iterable<? extends Closeable> closeables;
-
-    public ParallelCloseable(Object id, Object lock, Iterable<? extends Closeable> closeables) {
-        super(id, lock);
-        this.closeables = (closeables == null) ? Collections.emptyList() : closeables;
-    }
-
-    @Override
-    protected void doClose(boolean immediately) {
-        AtomicInteger count = new AtomicInteger(1);
-        boolean traceEnabled = log.isTraceEnabled();
-        SshFutureListener<CloseFuture> listener = f -> {
-            int pendingCount = count.decrementAndGet();
-            if (traceEnabled) {
-                log.trace("doClose(" + immediately + ") completed pending: " + pendingCount);
-            }
-            if (pendingCount == 0) {
-                future.setClosed();
-            }
-        };
-
-        for (Closeable c : closeables) {
-            if (c == null) {
-                continue;
-            }
-
-            int pendingCount = count.incrementAndGet();
-            if (traceEnabled) {
-                log.trace("doClose(" + immediately + ") pending closeables: " + pendingCount);
-            }
-            c.close(immediately).addListener(listener);
-        }
-        /*
-         * Trigger the last "decrementAndGet" so that the future is marked as closed
-         * when last "operationComplete" is invoked (which could be this call...)
-         */
-        listener.operationComplete(null);
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/SequentialCloseable.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/SequentialCloseable.java b/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/SequentialCloseable.java
deleted file mode 100644
index 6af51b8..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/SequentialCloseable.java
+++ /dev/null
@@ -1,71 +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.closeable;
-
-import java.util.Collections;
-import java.util.Iterator;
-
-import org.apache.sshd.common.Closeable;
-import org.apache.sshd.common.future.CloseFuture;
-import org.apache.sshd.common.future.SshFutureListener;
-
-/**
- * Waits for a group of {@link Closeable}s to complete in the given order, then
- * signals the completion by setting the &quot;parent&quot; future as closed
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class SequentialCloseable extends SimpleCloseable {
-    private final Iterable<? extends Closeable> closeables;
-
-    public SequentialCloseable(Object id, Object lock, Iterable<? extends Closeable> closeables) {
-        super(id, lock);
-        this.closeables = (closeables == null) ? Collections.emptyList() : closeables;
-    }
-
-    @Override
-    protected void doClose(boolean immediately) {
-        Iterator<? extends Closeable> iterator = closeables.iterator();
-        SshFutureListener<CloseFuture> listener = new SshFutureListener<CloseFuture>() {
-            @SuppressWarnings("synthetic-access")
-            @Override
-            public void operationComplete(CloseFuture previousFuture) {
-                boolean traceEnabled = log.isTraceEnabled();
-                while (iterator.hasNext()) {
-                    Closeable c = iterator.next();
-                    if (c != null) {
-                        if (traceEnabled) {
-                            log.trace("doClose(" + immediately + ") closing " + c);
-                        }
-                        CloseFuture nextFuture = c.close(immediately);
-                        nextFuture.addListener(this);
-                        return;
-                    }
-                }
-                if (!iterator.hasNext()) {
-                    if (log.isDebugEnabled()) {
-                        log.debug("doClose(" + immediately + ") signal close complete");
-                    }
-                    future.setClosed();
-                }
-            }
-        };
-        listener.operationComplete(null);
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/SimpleCloseable.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/SimpleCloseable.java b/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/SimpleCloseable.java
deleted file mode 100644
index e360f13..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/SimpleCloseable.java
+++ /dev/null
@@ -1,71 +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.closeable;
-
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import org.apache.sshd.common.future.CloseFuture;
-import org.apache.sshd.common.future.DefaultCloseFuture;
-import org.apache.sshd.common.future.SshFutureListener;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class SimpleCloseable extends IoBaseCloseable {
-
-    protected final DefaultCloseFuture future;
-    protected final AtomicBoolean closing;
-
-    public SimpleCloseable(Object id, Object lock) {
-        future = new DefaultCloseFuture(id, lock);
-        closing = new AtomicBoolean(false);
-    }
-
-    @Override
-    public boolean isClosed() {
-        return future.isClosed();
-    }
-
-    @Override
-    public boolean isClosing() {
-        return closing.get();
-    }
-
-    @Override
-    public void addCloseFutureListener(SshFutureListener<CloseFuture> listener) {
-        future.addListener(listener);
-    }
-
-    @Override
-    public void removeCloseFutureListener(SshFutureListener<CloseFuture> listener) {
-        future.removeListener(listener);
-    }
-
-    @Override
-    public CloseFuture close(boolean immediately) {
-        if (closing.compareAndSet(false, true)) {
-            doClose(immediately);
-        }
-        return future;
-    }
-
-    protected void doClose(boolean immediately) {
-        future.setClosed();
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/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
deleted file mode 100644
index c9eca70..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/io/CloseableEmptyInputStream.java
+++ /dev/null
@@ -1,96 +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.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)) {
-            //noinspection UnnecessaryReturnStatement
-            return; // debug breakpoint
-        }
-    }
-}


[10/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common utilities code from sshd-core into sshd-common (new artifact)

Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/client/config/hosts/ConfigFileHostEntryResolverTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/config/hosts/ConfigFileHostEntryResolverTest.java b/sshd-core/src/test/java/org/apache/sshd/client/config/hosts/ConfigFileHostEntryResolverTest.java
deleted file mode 100644
index 78994cc..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/client/config/hosts/ConfigFileHostEntryResolverTest.java
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.client.config.hosts;
-
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class ConfigFileHostEntryResolverTest extends BaseTestSupport {
-    public ConfigFileHostEntryResolverTest() {
-        super();
-    }
-
-    @Test
-    public void testConfigFileReload() throws IOException {
-        Path dir = getTempTargetRelativeFile(getClass().getSimpleName());
-        AtomicInteger reloadCount = new AtomicInteger();
-        ConfigFileHostEntryResolver resolver = new ConfigFileHostEntryResolver(assertHierarchyTargetFolderExists(dir).resolve(getCurrentTestName() + ".config.txt")) {
-            @Override
-            protected List<HostConfigEntry> reloadHostConfigEntries(Path path, String host, int port, String username)
-                    throws IOException {
-                reloadCount.incrementAndGet();
-                return super.reloadHostConfigEntries(path, host, port, username);
-            }
-        };
-        Path path = resolver.getPath();
-
-        HostConfigEntry expected = new HostConfigEntry(getCurrentTestName(), getCurrentTestName(), 7365, getCurrentTestName());
-        testConfigFileReload("Non-existing", path, reloadCount, null, resolver, expected, null);
-        testConfigFileReload("Empty", path, reloadCount, Collections.emptyList(), resolver, expected, null);
-        testConfigFileReload("Global", path, reloadCount,
-                Collections.singletonList(new HostConfigEntry(HostPatternsHolder.ALL_HOSTS_PATTERN, expected.getHost(), expected.getPort(), expected.getUsername())),
-                resolver, expected, expected);
-        testConfigFileReload("Wildcard", path, reloadCount,
-                Arrays.asList(
-                        new HostConfigEntry(
-                                HostPatternsHolder.ALL_HOSTS_PATTERN,
-                                getClass().getSimpleName(),
-                                1234,
-                                getClass().getSimpleName()),
-                        new HostConfigEntry(
-                                expected.getHost() + Character.toString(HostPatternsHolder.WILDCARD_PATTERN),
-                                expected.getHost(),
-                                expected.getPort(),
-                                expected.getUsername())),
-                resolver, expected, expected);
-        testConfigFileReload("Specific", path, reloadCount,
-                Arrays.asList(
-                        new HostConfigEntry(
-                                HostPatternsHolder.ALL_HOSTS_PATTERN,
-                                getClass().getSimpleName(),
-                                1234,
-                                getClass().getSimpleName()),
-                        new HostConfigEntry(
-                                getClass().getSimpleName() + Character.toString(HostPatternsHolder.WILDCARD_PATTERN),
-                                getClass().getSimpleName(),
-                                1234,
-                                getClass().getSimpleName()),
-                        expected),
-                resolver, expected, expected);
-    }
-
-    private static void testConfigFileReload(
-            String phase, Path path, AtomicInteger reloadCount,
-            Collection<? extends HostConfigEntry> entries,
-            HostConfigEntryResolver resolver,
-            HostConfigEntry query,
-            HostConfigEntry expected)
-                    throws IOException {
-        if (entries == null) {
-            if (Files.exists(path)) {
-                Files.delete(path);
-            }
-        } else {
-            HostConfigEntry.writeHostConfigEntries(path, entries, IoUtils.EMPTY_OPEN_OPTIONS);
-        }
-
-        reloadCount.set(0);
-
-        for (int index = 1; index < Byte.SIZE; index++) {
-            HostConfigEntry actual =
-                    resolver.resolveEffectiveHost(query.getHostName(), query.getPort(), query.getUsername());
-
-            if (entries == null) {
-                assertEquals(phase + "[" + index + "]: mismatched reload count", 0, reloadCount.get());
-            } else {
-                assertEquals(phase + "[" + index + "]: mismatched reload count", 1, reloadCount.get());
-            }
-
-            if (expected == null) {
-                assertNull(phase + "[" + index + "]: Unexpected success for " + query, actual);
-            } else {
-                assertNotNull(phase + "[" + index + "]: No result for " + query, actual);
-                assertNotSame(phase + "[" + index + "]: No cloned result for " + query, expected, actual);
-                assertEquals(phase + "[" + index + "]: Mismatched host for " + query,
-                        expected.getHostName(), actual.getHostName());
-                assertEquals(phase + "[" + index + "]: Mismatched port for " + query,
-                        expected.getPort(), actual.getPort());
-                assertEquals(phase + "[" + index + "]: Mismatched user for " + query,
-                        expected.getUsername(), actual.getUsername());
-            }
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/client/config/hosts/HostConfigEntryResolverTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/config/hosts/HostConfigEntryResolverTest.java b/sshd-core/src/test/java/org/apache/sshd/client/config/hosts/HostConfigEntryResolverTest.java
index ab3c591..0679fa3 100644
--- a/sshd-core/src/test/java/org/apache/sshd/client/config/hosts/HostConfigEntryResolverTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/client/config/hosts/HostConfigEntryResolverTest.java
@@ -48,7 +48,7 @@ import org.apache.sshd.common.util.net.SshdSocketAddress;
 import org.apache.sshd.server.SshServer;
 import org.apache.sshd.server.auth.password.RejectAllPasswordAuthenticator;
 import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.Utils;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.FixMethodOrder;
@@ -132,7 +132,7 @@ public class HostConfigEntryResolverTest extends BaseTestSupport {
 
     @Test
     public void testPreloadedIdentities() throws Exception {
-        KeyPair identity = Utils.getFirstKeyPair(sshd);
+        KeyPair identity = CommonTestSupportUtils.getFirstKeyPair(sshd);
         String user = getCurrentTestName();
         // make sure authentication is achieved only via the identity public key
         sshd.setPublickeyAuthenticator((username, key, session) -> {
@@ -182,10 +182,9 @@ public class HostConfigEntryResolverTest extends BaseTestSupport {
     public void testUseIdentitiesOnly() throws Exception {
         Path clientIdFile = assertHierarchyTargetFolderExists(getTempTargetRelativeFile(getClass().getSimpleName()));
         KeyPairProvider clientIdProvider =
-                Utils.createTestHostKeyProvider(clientIdFile.resolve(getCurrentTestName() + ".pem"));
-
-        KeyPair specificIdentity = Utils.getFirstKeyPair(sshd);
-        KeyPair defaultIdentity = Utils.getFirstKeyPair(clientIdProvider);
+            CommonTestSupportUtils.createTestHostKeyProvider(clientIdFile.resolve(getCurrentTestName() + ".pem"));
+        KeyPair specificIdentity = CommonTestSupportUtils.getFirstKeyPair(sshd);
+        KeyPair defaultIdentity = CommonTestSupportUtils.getFirstKeyPair(clientIdProvider);
         ValidateUtils.checkTrue(!KeyUtils.compareKeyPairs(specificIdentity, defaultIdentity),
                 "client identity not different then entry one");
         client.setKeyPairProvider(clientIdProvider);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/client/config/hosts/HostConfigEntryTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/config/hosts/HostConfigEntryTest.java b/sshd-core/src/test/java/org/apache/sshd/client/config/hosts/HostConfigEntryTest.java
deleted file mode 100644
index c306800..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/client/config/hosts/HostConfigEntryTest.java
+++ /dev/null
@@ -1,325 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.client.config.hosts;
-
-import java.io.IOException;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.regex.Pattern;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class HostConfigEntryTest extends BaseTestSupport {
-    public HostConfigEntryTest() {
-        super();
-    }
-
-    @Test
-    public void testNegatingPatternOverridesAll() {
-        String testHost = "37.77.34.7";
-        String[] elements = GenericUtils.split(testHost, '.');
-        StringBuilder sb = new StringBuilder(testHost.length() + Byte.SIZE);
-        List<HostPatternValue> patterns = new ArrayList<>(elements.length + 1);
-        // all wildcard patterns are not negated - only the actual host
-        patterns.add(HostPatternsHolder.toPattern(Character.toString(HostPatternsHolder.NEGATION_CHAR_PATTERN) + testHost));
-
-        for (int i = 0; i < elements.length; i++) {
-            sb.setLength(0);
-
-            for (int j = 0; j < elements.length; j++) {
-                if (j > 0) {
-                    sb.append('.');
-                }
-                if (i == j) {
-                    sb.append(HostPatternsHolder.WILDCARD_PATTERN);
-                } else {
-                    sb.append(elements[j]);
-                }
-            }
-
-            patterns.add(HostPatternsHolder.toPattern(sb));
-        }
-
-        for (int index = 0; index < patterns.size(); index++) {
-            assertFalse("Unexpected match for " + patterns, HostPatternsHolder.isHostMatch(testHost, 0, patterns));
-            Collections.shuffle(patterns);
-        }
-    }
-
-    @Test
-    public void testHostWildcardPatternMatching() {
-        String pkgName = getClass().getPackage().getName();
-        String[] elements = GenericUtils.split(pkgName, '.');
-        StringBuilder sb = new StringBuilder(pkgName.length() + Long.SIZE + 1).append(HostPatternsHolder.WILDCARD_PATTERN);
-        for (int index = elements.length - 1; index >= 0; index--) {
-            sb.append('.').append(elements[index]);
-        }
-
-        String value = sb.toString();
-        HostPatternValue pp = HostPatternsHolder.toPattern(value);
-        Pattern pattern = pp.getPattern();
-        String domain = value.substring(1); // chomp the wildcard prefix
-        for (String host : new String[] {
-                getClass().getSimpleName(),
-                getCurrentTestName(),
-                getClass().getSimpleName() + "-" + getCurrentTestName(),
-                getClass().getSimpleName() + "." + getCurrentTestName(),
-        }) {
-            sb.setLength(0); // start from scratch
-            sb.append(host).append(domain);
-
-            testCaseInsensitivePatternMatching(sb.toString(), pattern, true);
-        }
-    }
-
-    @Test
-    public void testIPAddressWildcardPatternMatching() {
-        StringBuilder sb = new StringBuilder().append("10.0.0.");
-        int sbLen = sb.length();
-
-        Pattern pattern = HostPatternsHolder.toPattern(sb.append(HostPatternsHolder.WILDCARD_PATTERN)).getPattern();
-        for (int v = 0; v <= 255; v++) {
-            sb.setLength(sbLen);    // start from scratch
-            sb.append(v);
-
-            String address = sb.toString();
-            assertTrue("No match for " + address, HostPatternsHolder.isHostMatch(address, pattern));
-        }
-    }
-
-    @Test
-    public void testHostSingleCharPatternMatching() {
-        String value = getCurrentTestName();
-        StringBuilder sb = new StringBuilder(value);
-        for (boolean restoreOriginal : new boolean[] {true, false}) {
-            for (int index = 0; index < value.length(); index++) {
-                sb.setCharAt(index, HostPatternsHolder.SINGLE_CHAR_PATTERN);
-                testCaseInsensitivePatternMatching(value, HostPatternsHolder.toPattern(sb.toString()).getPattern(), true);
-                if (restoreOriginal) {
-                    sb.setCharAt(index, value.charAt(index));
-                }
-            }
-        }
-    }
-
-    @Test
-    public void testIPAddressSingleCharPatternMatching() {
-        StringBuilder sb = new StringBuilder().append("10.0.0.");
-        int sbLen = sb.length();
-
-        for (int v = 0; v <= 255; v++) {
-            sb.setLength(sbLen);    // start from scratch
-            sb.append(v);
-
-            String address = sb.toString();
-            // replace the added digits with single char pattern
-            for (int index = sbLen; index < sb.length(); index++) {
-                sb.setCharAt(index, HostPatternsHolder.SINGLE_CHAR_PATTERN);
-            }
-
-            String pattern = sb.toString();
-            HostPatternValue pp = HostPatternsHolder.toPattern(pattern);
-            assertTrue("No match for " + address + " on pattern=" + pattern, HostPatternsHolder.isHostMatch(address, 0, Collections.singletonList(pp)));
-        }
-    }
-
-    @Test
-    public void testIsValidPatternChar() {
-        for (char ch = '\0'; ch <= ' '; ch++) {
-            assertFalse("Unexpected valid character (0x" + Integer.toHexString(ch & 0xFF) + ")", HostPatternsHolder.isValidPatternChar(ch));
-        }
-
-        for (char ch = 'a'; ch <= 'z'; ch++) {
-            assertTrue("Valid character not recognized: " + Character.toString(ch), HostPatternsHolder.isValidPatternChar(ch));
-        }
-
-        for (char ch = 'A'; ch <= 'Z'; ch++) {
-            assertTrue("Valid character not recognized: " + Character.toString(ch), HostPatternsHolder.isValidPatternChar(ch));
-        }
-
-        for (char ch = '0'; ch <= '9'; ch++) {
-            assertTrue("Valid character not recognized: " + Character.toString(ch), HostPatternsHolder.isValidPatternChar(ch));
-        }
-
-        for (char ch : new char[] {'-', '_', '.', HostPatternsHolder.SINGLE_CHAR_PATTERN, HostPatternsHolder.WILDCARD_PATTERN}) {
-            assertTrue("Valid character not recognized: " + Character.toString(ch), HostPatternsHolder.isValidPatternChar(ch));
-        }
-
-        for (char ch : new char[] {
-            '(', ')', '{', '}', '[', ']', '@',
-            '#', '$', '^', '&', '%', '~', '<', '>',
-            ',', '/', '\\', '\'', '"', ':', ';'
-        }) {
-            assertFalse("Unexpected valid character: " + Character.toString(ch), HostPatternsHolder.isValidPatternChar(ch));
-        }
-
-        for (char ch = 0x7E; ch <= 0xFF; ch++) {
-            assertFalse("Unexpected valid character (0x" + Integer.toHexString(ch & 0xFF) + ")", HostPatternsHolder.isValidPatternChar(ch));
-        }
-    }
-
-    @Test
-    public void testResolvePort() {
-        final int originalPort = Short.MAX_VALUE;
-        final int preferredPort = 7365;
-        assertEquals("Mismatched entry port preference",
-            preferredPort, HostConfigEntry.resolvePort(originalPort, preferredPort));
-
-        for (int entryPort : new int[] {-1, 0}) {
-            assertEquals("Non-preferred original port for entry port=" + entryPort,
-                originalPort, HostConfigEntry.resolvePort(originalPort, entryPort));
-        }
-    }
-
-    @Test
-    public void testResolveUsername() {
-        final String originalUser = getCurrentTestName();
-        final String preferredUser = getClass().getSimpleName();
-        assertSame("Mismatched entry user preference",
-                preferredUser, HostConfigEntry.resolveUsername(originalUser, preferredUser));
-
-        for (String entryUser : new String[] {null, ""}) {
-            assertSame("Non-preferred original user for entry user='" + entryUser + "'",
-                originalUser, HostConfigEntry.resolveUsername(originalUser, entryUser));
-        }
-    }
-
-    @Test
-    public void testReadSimpleHostsConfigEntries() throws IOException {
-        validateHostConfigEntries(readHostConfigEntries());
-    }
-
-    @Test
-    public void testReadGlobalHostsConfigEntries() throws IOException {
-        List<HostConfigEntry> entries = validateHostConfigEntries(readHostConfigEntries());
-        assertTrue("Not enough entries read", GenericUtils.size(entries) > 1);
-
-        // global entry MUST be 1st one
-        HostConfigEntry globalEntry = entries.get(0);
-        assertEquals("Mismatched global entry pattern", HostPatternsHolder.ALL_HOSTS_PATTERN, globalEntry.getHost());
-
-        for (int index = 1; index < entries.size(); index++) {
-            HostConfigEntry entry = entries.get(index);
-            assertFalse("No target host for " + entry, GenericUtils.isEmpty(entry.getHostName()));
-            assertTrue("No target port for " + entry, entry.getPort() > 0);
-            assertFalse("No username for " + entry, GenericUtils.isEmpty(entry.getUsername()));
-            assertFalse("No identities for " + entry, GenericUtils.isEmpty(entry.getIdentities()));
-            assertFalse("No properties for " + entry, GenericUtils.isEmpty(entry.getProperties()));
-        }
-    }
-
-    @Test
-    public void testReadMultipleHostPatterns() throws IOException {
-        List<HostConfigEntry> entries = validateHostConfigEntries(readHostConfigEntries());
-        assertEquals("Mismatched number of entries", 1, GenericUtils.size(entries));
-        assertEquals("Mismatched number of patterns", 3, GenericUtils.size(entries.get(0).getPatterns()));
-    }
-
-    @Test
-    public void testResolveIdentityFilePath() throws Exception {
-        final String hostValue = getClass().getSimpleName();
-        final int portValue = 7365;
-        final String userValue = getCurrentTestName();
-
-        Exception err = null;
-        for (String pattern : new String[] {
-            "~/.ssh/%h.key",
-            "%d/.ssh/%h.key",
-            "/home/%u/.ssh/id_rsa_%p",
-            "/home/%u/.ssh/id_%r_rsa",
-            "/home/%u/.ssh/%h/%l.key"
-        }) {
-            try {
-                String result = HostConfigEntry.resolveIdentityFilePath(pattern, hostValue, portValue, userValue);
-                System.out.append('\t').append(pattern).append(" => ").println(result);
-            } catch (Exception e) {
-                System.err.append("Failed (").append(e.getClass().getSimpleName())
-                          .append(") to process pattern=").append(pattern)
-                          .append(": ").println(e.getMessage());
-                err = e;
-            }
-        }
-
-        if (err != null) {
-            throw err;
-        }
-    }
-
-    @Test
-    public void testFindBestMatch() {
-        final String hostValue = getCurrentTestName();
-        HostConfigEntry expected = new HostConfigEntry(hostValue, hostValue, 7365, hostValue);
-        List<HostConfigEntry> matches = new ArrayList<>();
-        matches.add(new HostConfigEntry(HostPatternsHolder.ALL_HOSTS_PATTERN,
-            getClass().getSimpleName(), Short.MAX_VALUE, getClass().getSimpleName()));
-        matches.add(new HostConfigEntry(hostValue + Character.toString(HostPatternsHolder.WILDCARD_PATTERN),
-            getClass().getSimpleName(), Byte.MAX_VALUE, getClass().getSimpleName()));
-        matches.add(expected);
-
-        for (int index = 0; index < matches.size(); index++) {
-            HostConfigEntry actual = HostConfigEntry.findBestMatch(matches);
-            assertSame("Mismatched best match for " + matches, expected, actual);
-            Collections.shuffle(matches);
-        }
-    }
-
-    private static <C extends Collection<HostConfigEntry>> C validateHostConfigEntries(C entries) {
-        assertFalse("No entries", GenericUtils.isEmpty(entries));
-
-        for (HostConfigEntry entry : entries) {
-            assertFalse("No pattern for " + entry, GenericUtils.isEmpty(entry.getHost()));
-            assertFalse("No extra properties for " + entry, GenericUtils.isEmpty(entry.getProperties()));
-        }
-
-        return entries;
-    }
-
-    private List<HostConfigEntry> readHostConfigEntries() throws IOException {
-        return readHostConfigEntries(getCurrentTestName() + ".config.txt");
-    }
-
-    private List<HostConfigEntry> readHostConfigEntries(String resourceName) throws IOException {
-        URL url = getClass().getResource(resourceName);
-        assertNotNull("Missing resource " + resourceName, url);
-        return HostConfigEntry.readHostConfigEntries(url);
-    }
-
-    private static void testCaseInsensitivePatternMatching(String value, Pattern pattern, boolean expected) {
-        for (int index = 0; index < value.length(); index++) {
-            boolean actual = HostPatternsHolder.isHostMatch(value, pattern);
-            assertEquals("Mismatched match result for " + value + " on pattern=" + pattern.pattern(), expected, actual);
-            value = shuffleCase(value);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/client/config/hosts/KnownHostHashValueTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/config/hosts/KnownHostHashValueTest.java b/sshd-core/src/test/java/org/apache/sshd/client/config/hosts/KnownHostHashValueTest.java
deleted file mode 100644
index efbf885..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/client/config/hosts/KnownHostHashValueTest.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.client.config.hosts;
-
-import java.util.Arrays;
-import java.util.Collection;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runner.RunWith;
-import org.junit.runners.MethodSorters;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-import org.junit.runners.Parameterized.UseParametersRunnerFactory;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@RunWith(Parameterized.class)   // see https://github.com/junit-team/junit/wiki/Parameterized-tests
-@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
-@Category({ NoIoTestCase.class })
-public class KnownHostHashValueTest extends BaseTestSupport {
-    private final String hostName;
-    private final String hashValue;
-    private final KnownHostHashValue hash;
-
-    public KnownHostHashValueTest(String hostName, String hashValue) {
-        this.hostName = hostName;
-        this.hashValue = hashValue;
-        this.hash = KnownHostHashValue.parse(hashValue);
-    }
-
-    @Parameters(name = "host={0}, hash={1}")
-    public static Collection<Object[]> parameters() {
-        return Arrays.<Object[]>asList(
-                (Object[]) new String[]{"192.168.1.61", "|1|F1E1KeoE/eEWhi10WpGv4OdiO6Y=|3988QV0VE8wmZL7suNrYQLITLCg="});
-    }
-
-    @Test
-    public void testDecodeEncode() {
-        assertSame("Mismatched digester", KnownHostHashValue.DEFAULT_DIGEST, hash.getDigester());
-        assertEquals("Mismatched encoded form", hashValue, hash.toString());
-    }
-
-    @Test
-    public void testHostMatch() {
-        assertTrue("Specified host does not match", hash.isHostMatch(hostName));
-        assertFalse("Unexpected host match", hash.isHostMatch(getCurrentTestName()));
-    }
-
-    @Test
-    public void testCalculateHashValue() throws Exception {
-        byte[] expected = hash.getDigestValue();
-        byte[] actual = KnownHostHashValue.calculateHashValue(hostName, hash.getDigester(), hash.getSaltValue());
-        assertArrayEquals("Mismatched hash value", expected, actual);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcherTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcherTest.java b/sshd-core/src/test/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcherTest.java
deleted file mode 100644
index af720b8..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcherTest.java
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.client.config.keys;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.OpenOption;
-import java.nio.file.Path;
-import java.nio.file.StandardOpenOption;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Date;
-import java.util.EnumMap;
-import java.util.Map;
-import java.util.Objects;
-
-import org.apache.sshd.common.config.keys.BuiltinIdentities;
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.keyprovider.KeyIdentityProvider;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.apache.sshd.util.test.Utils;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class BuiltinClientIdentitiesWatcherTest extends BaseTestSupport {
-    public BuiltinClientIdentitiesWatcherTest() {
-        super();
-    }
-
-    @Test
-    public void testMultipleFilesWatch() throws Exception {
-        KeyPair identity = Utils.getFirstKeyPair(createTestHostKeyProvider());
-        String keyType = ValidateUtils.checkNotNullAndNotEmpty(KeyUtils.getKeyType(identity), "Cannot determine identity key type");
-
-        Path dir = assertHierarchyTargetFolderExists(getTempTargetRelativeFile(getClass().getSimpleName()));
-        Map<BuiltinIdentities, Path> locationsMap = new EnumMap<>(BuiltinIdentities.class);
-        Map<BuiltinIdentities, KeyPair> idsMap = new EnumMap<>(BuiltinIdentities.class);
-        for (BuiltinIdentities id : BuiltinIdentities.VALUES) {
-            Path idFile = dir.resolve(ClientIdentity.getIdentityFileName(id));
-            Files.deleteIfExists(idFile);
-            assertNull("Multiple file mappings for " + id, locationsMap.put(id, idFile));
-            assertNull("Multiple identity mappings for " + id, idsMap.put(id, KeyUtils.cloneKeyPair(keyType, identity)));
-        }
-
-        ClientIdentityLoader loader = new ClientIdentityLoader() {
-            @Override
-            public KeyPair loadClientIdentity(String location, FilePasswordProvider provider) throws IOException, GeneralSecurityException {
-                BuiltinIdentities id = findIdentity(location);
-                assertNotNull("Invalid location: " + location, id);
-                return idsMap.get(id);
-            }
-
-            @Override
-            public boolean isValidLocation(String location) throws IOException {
-                return findIdentity(location) != null;
-            }
-
-            private BuiltinIdentities findIdentity(String location) {
-                if (GenericUtils.isEmpty(location)) {
-                    return null;
-                }
-
-                for (Map.Entry<BuiltinIdentities, Path> le : locationsMap.entrySet()) {
-                    Path path = le.getValue();
-                    if (String.CASE_INSENSITIVE_ORDER.compare(location, path.toString()) == 0) {
-                        return le.getKey();
-                    }
-                }
-
-                return null;
-            }
-        };
-
-        Map<BuiltinIdentities, KeyPair> existing = new EnumMap<>(BuiltinIdentities.class);
-        KeyPairProvider watcher = new BuiltinClientIdentitiesWatcher(dir, false, loader, FilePasswordProvider.EMPTY, false);
-        testMultipleFilesWatch("No files", watcher, existing.values());
-
-        for (BuiltinIdentities id : BuiltinIdentities.VALUES) {
-            String phase = id + " + " + Objects.toString(existing.keySet());
-            touchIdentityFile(locationsMap.get(id));
-            existing.put(id, idsMap.get(id));
-
-            for (int index = 0; index < Byte.SIZE; index++) {
-                testMultipleFilesWatch(phase + "[" + index + "]", watcher, existing.values());
-            }
-        }
-
-        testMultipleFilesWatch("All files", watcher, existing.values());
-
-        for (BuiltinIdentities id : BuiltinIdentities.VALUES) {
-            existing.remove(id);
-            Files.deleteIfExists(locationsMap.get(id));
-            String phase = Objects.toString(existing.keySet()) + " - " + id;
-
-            for (int index = 0; index < Byte.SIZE; index++) {
-                testMultipleFilesWatch(phase + "[" + index + "]", watcher, existing.values());
-            }
-        }
-    }
-
-    private static void touchIdentityFile(Path idFile) throws IOException {
-        OpenOption[] options = IoUtils.EMPTY_OPEN_OPTIONS;
-        if (Files.exists(idFile, IoUtils.EMPTY_LINK_OPTIONS)) {
-            options = new OpenOption[]{StandardOpenOption.WRITE, StandardOpenOption.APPEND};
-        }
-
-        try (OutputStream out = Files.newOutputStream(idFile, options)) {
-            out.write(new Date(System.currentTimeMillis()).toString().getBytes(StandardCharsets.UTF_8));
-            out.write('\n');
-        }
-    }
-
-    private static void testMultipleFilesWatch(String phase, KeyIdentityProvider watcher, Collection<? extends KeyPair> expected) {
-        Iterable<KeyPair> keys = watcher.loadKeys();
-        Collection<KeyPair> actual = new ArrayList<>();
-        for (KeyPair kp : keys) {
-            actual.add(kp);
-        }
-        assertEquals(phase + ": mismatched sizes", GenericUtils.size(expected), GenericUtils.size(actual));
-
-        if (!GenericUtils.isEmpty(expected)) {
-            for (KeyPair kp : expected) {
-                assertTrue(phase + ": missing key", actual.contains(kp));
-            }
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcherTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcherTest.java b/sshd-core/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcherTest.java
deleted file mode 100644
index 60ded0a..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcherTest.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.client.config.keys;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.OpenOption;
-import java.nio.file.Path;
-import java.nio.file.StandardOpenOption;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.util.Date;
-import java.util.Objects;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.apache.sshd.util.test.Utils;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class ClientIdentityFileWatcherTest extends BaseTestSupport {
-    public ClientIdentityFileWatcherTest() {
-        super();
-    }
-
-    @Test
-    public void testIdentityReload() throws Exception {
-        Path dir = assertHierarchyTargetFolderExists(getTempTargetRelativeFile(getClass().getSimpleName()));
-        Path idFile = dir.resolve(getCurrentTestName() + ".pem");
-        KeyPair identity = Utils.getFirstKeyPair(createTestHostKeyProvider());
-        ClientIdentityLoader loader = new ClientIdentityLoader() {
-            @Override
-            public KeyPair loadClientIdentity(String location, FilePasswordProvider provider) throws IOException, GeneralSecurityException {
-                assertTrue("Invalid location: " + location, isValidLocation(location));
-                return identity;
-            }
-
-            @Override
-            public boolean isValidLocation(String location) throws IOException {
-                return Objects.equals(location, toString());
-            }
-
-            @Override
-            public String toString() {
-                return Objects.toString(idFile);
-            }
-        };
-
-        AtomicInteger reloadCount = new AtomicInteger(0);
-        ClientIdentityProvider idProvider = new ClientIdentityFileWatcher(idFile, loader, FilePasswordProvider.EMPTY, false) {
-            @Override
-            protected KeyPair reloadClientIdentity(Path path) throws IOException, GeneralSecurityException {
-                assertEquals("Mismatched client identity path", idFile, path);
-                reloadCount.incrementAndGet();
-                return super.reloadClientIdentity(path);
-            }
-        };
-        Files.deleteIfExists(idFile);
-
-        testIdentityReload("Non-existing", reloadCount, idProvider, null, 0);
-
-        touchIdentityFile(idFile);
-        for (int index = 1; index < Byte.SIZE; index++) {
-            testIdentityReload("Created iteration " + 1, reloadCount, idProvider, identity, 1);
-        }
-
-        touchIdentityFile(idFile);
-        for (int index = 1; index < Byte.SIZE; index++) {
-            testIdentityReload("Modified iteration " + 1, reloadCount, idProvider, identity, 2);
-        }
-    }
-
-    private static void touchIdentityFile(Path idFile) throws IOException {
-        OpenOption[] options = IoUtils.EMPTY_OPEN_OPTIONS;
-        if (Files.exists(idFile, IoUtils.EMPTY_LINK_OPTIONS)) {
-            options = new OpenOption[]{StandardOpenOption.WRITE, StandardOpenOption.APPEND};
-        }
-
-        try (OutputStream out = Files.newOutputStream(idFile, options)) {
-            out.write(new Date(System.currentTimeMillis()).toString().getBytes(StandardCharsets.UTF_8));
-            out.write('\n');
-        }
-    }
-
-    private static void testIdentityReload(
-            String phase, Number reloadCount, ClientIdentityProvider provider, KeyPair expectedIdentity, int expectedCount)
-                throws Exception {
-        KeyPair actualIdentity = provider.getClientIdentity();
-        assertSame(phase + ": mismatched identity", expectedIdentity, actualIdentity);
-        assertEquals(phase + ": mismatched re-load count", expectedCount, reloadCount.intValue());
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityTest.java b/sshd-core/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityTest.java
deleted file mode 100644
index 6861299..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityTest.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.client.config.keys;
-
-import java.nio.file.Files;
-import java.nio.file.LinkOption;
-import java.nio.file.Path;
-import java.security.KeyPair;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.EnumSet;
-import java.util.Map;
-
-import org.apache.sshd.common.config.keys.BuiltinIdentities;
-import org.apache.sshd.common.config.keys.IdentityUtils;
-import org.apache.sshd.common.keyprovider.KeyIdentityProvider;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class ClientIdentityTest extends BaseTestSupport {
-    public ClientIdentityTest() {
-        super();
-    }
-
-    @Test
-    public void testLoadClientIdentities() throws Exception {
-        Path resFolder = getTestResourcesFolder();
-        LinkOption[] options = IoUtils.getLinkOptions(true);
-        Collection<BuiltinIdentities> expected = EnumSet.noneOf(BuiltinIdentities.class);
-        for (BuiltinIdentities type : BuiltinIdentities.VALUES) {
-            String fileName = ClientIdentity.getIdentityFileName(type);
-            Path file = resFolder.resolve(fileName);
-            if (!Files.exists(file, options)) {
-                System.out.println("Skip non-existing identity file " + file);
-                continue;
-            }
-
-            if (!type.isSupported()) {
-                System.out.println("Skip unsupported identity file " + file);
-                continue;
-            }
-
-            expected.add(type);
-        }
-
-        Map<String, KeyPair> ids = ClientIdentity.loadDefaultIdentities(
-                resFolder,
-                false,   // don't be strict
-                null,    // none of the files is password protected
-                options);
-        assertEquals("Mismatched loaded ids count", GenericUtils.size(expected), GenericUtils.size(ids));
-
-        Collection<KeyPair> pairs = new ArrayList<>(ids.size());
-        for (BuiltinIdentities type : BuiltinIdentities.VALUES) {
-            if (expected.contains(type)) {
-                KeyPair kp = ids.get(type.getName());
-                assertNotNull("No key pair loaded for " + type, kp);
-                pairs.add(kp);
-            }
-        }
-
-        KeyIdentityProvider provider = IdentityUtils.createKeyPairProvider(ids, true /* supported only */);
-        assertNotNull("No provider generated", provider);
-
-        Iterable<KeyPair> keys = provider.loadKeys();
-        for (KeyPair kp : keys) {
-            assertTrue("Unexpected loaded key: " + kp, pairs.remove(kp));
-        }
-
-        assertEquals("Not all pairs listed", 0, pairs.size());
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/client/kex/KexTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/kex/KexTest.java b/sshd-core/src/test/java/org/apache/sshd/client/kex/KexTest.java
index 6ad17b2..678521f 100644
--- a/sshd-core/src/test/java/org/apache/sshd/client/kex/KexTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/client/kex/KexTest.java
@@ -41,9 +41,9 @@ import org.apache.sshd.common.kex.KeyExchange;
 import org.apache.sshd.common.util.security.SecurityUtils;
 import org.apache.sshd.server.SshServer;
 import org.apache.sshd.util.test.BaseTestSupport;
+import org.apache.sshd.util.test.CoreTestSupportUtils;
 import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
 import org.apache.sshd.util.test.TeeOutputStream;
-import org.apache.sshd.util.test.Utils;
 import org.junit.AfterClass;
 import org.junit.Assume;
 import org.junit.BeforeClass;
@@ -81,11 +81,11 @@ public class KexTest extends BaseTestSupport {
 
     @BeforeClass
     public static void setupClientAndServer() throws Exception {
-        sshd = Utils.setupTestServer(KexTest.class);
+        sshd = CoreTestSupportUtils.setupTestServer(KexTest.class);
         sshd.start();
         port = sshd.getPort();
 
-        client = Utils.setupTestClient(KexTest.class);
+        client = CoreTestSupportUtils.setupTestClient(KexTest.class);
         client.start();
     }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/client/keyverifier/KnownHostsServerKeyVerifierTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/keyverifier/KnownHostsServerKeyVerifierTest.java b/sshd-core/src/test/java/org/apache/sshd/client/keyverifier/KnownHostsServerKeyVerifierTest.java
index c556553..9151af2 100644
--- a/sshd-core/src/test/java/org/apache/sshd/client/keyverifier/KnownHostsServerKeyVerifierTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/client/keyverifier/KnownHostsServerKeyVerifierTest.java
@@ -51,8 +51,8 @@ import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.ValidateUtils;
 import org.apache.sshd.common.util.net.SshdSocketAddress;
 import org.apache.sshd.util.test.BaseTestSupport;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
 import org.apache.sshd.util.test.NoIoTestCase;
-import org.apache.sshd.util.test.Utils;
 import org.junit.BeforeClass;
 import org.junit.FixMethodOrder;
 import org.junit.Test;
@@ -230,7 +230,7 @@ public class KnownHostsServerKeyVerifierTest extends BaseTestSupport {
 
     @Test
     public void testRejectModifiedServerKey() throws Exception {
-        KeyPair kp = Utils.generateKeyPair(KeyUtils.RSA_ALGORITHM, 1024);
+        KeyPair kp = CommonTestSupportUtils.generateKeyPair(KeyUtils.RSA_ALGORITHM, 1024);
         final PublicKey modifiedKey = kp.getPublic();
         final AtomicInteger acceptCount = new AtomicInteger(0);
         ServerKeyVerifier verifier = new KnownHostsServerKeyVerifier(AcceptAllServerKeyVerifier.INSTANCE, createKnownHostsCopy()) {
@@ -258,7 +258,7 @@ public class KnownHostsServerKeyVerifierTest extends BaseTestSupport {
 
     @Test
     public void testAcceptModifiedServerKeyUpdatesFile() throws Exception {
-        KeyPair kp = Utils.generateKeyPair(KeyUtils.RSA_ALGORITHM, 1024);
+        KeyPair kp = CommonTestSupportUtils.generateKeyPair(KeyUtils.RSA_ALGORITHM, 1024);
         final PublicKey modifiedKey = kp.getPublic();
         Path path = createKnownHostsCopy();
         ServerKeyVerifier verifier = new KnownHostsServerKeyVerifier(AcceptAllServerKeyVerifier.INSTANCE, path) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/client/session/ClientSessionTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/session/ClientSessionTest.java b/sshd-core/src/test/java/org/apache/sshd/client/session/ClientSessionTest.java
index 87dc5ff..12c416d 100644
--- a/sshd-core/src/test/java/org/apache/sshd/client/session/ClientSessionTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/client/session/ClientSessionTest.java
@@ -29,7 +29,7 @@ import org.apache.sshd.client.SshClient;
 import org.apache.sshd.server.SshServer;
 import org.apache.sshd.util.test.BaseTestSupport;
 import org.apache.sshd.util.test.CommandExecutionHelper;
-import org.apache.sshd.util.test.Utils;
+import org.apache.sshd.util.test.CoreTestSupportUtils;
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
 import org.junit.FixMethodOrder;
@@ -51,11 +51,11 @@ public class ClientSessionTest extends BaseTestSupport {
 
     @BeforeClass
     public static void setupClientAndServer() throws Exception {
-        sshd = Utils.setupTestServer(ClientSessionTest.class);
+        sshd = CoreTestSupportUtils.setupTestServer(ClientSessionTest.class);
         sshd.start();
         port = sshd.getPort();
 
-        client = Utils.setupTestClient(ClientSessionTest.class);
+        client = CoreTestSupportUtils.setupTestClient(ClientSessionTest.class);
         client.start();
     }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/client/simple/SimpleSessionClientTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/simple/SimpleSessionClientTest.java b/sshd-core/src/test/java/org/apache/sshd/client/simple/SimpleSessionClientTest.java
index ba3ee68..a095f81 100644
--- a/sshd-core/src/test/java/org/apache/sshd/client/simple/SimpleSessionClientTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/client/simple/SimpleSessionClientTest.java
@@ -32,7 +32,7 @@ import org.apache.sshd.common.session.SessionListener;
 import org.apache.sshd.server.auth.password.PasswordAuthenticator;
 import org.apache.sshd.server.auth.password.RejectAllPasswordAuthenticator;
 import org.apache.sshd.server.auth.pubkey.RejectAllPublickeyAuthenticator;
-import org.apache.sshd.util.test.Utils;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
 import org.apache.sshd.util.test.client.simple.BaseSimpleClientTestSupport;
 import org.junit.FixMethodOrder;
 import org.junit.Test;
@@ -60,8 +60,8 @@ public class SimpleSessionClientTest extends BaseSimpleClientTestSupport {
 
     @Test
     public void testLoginSessionWithIdentity() throws Exception {
-        final KeyPair identity = Utils.getFirstKeyPair(createTestHostKeyProvider());
-        final AtomicBoolean identityQueried = new AtomicBoolean(false);
+        KeyPair identity = CommonTestSupportUtils.getFirstKeyPair(createTestHostKeyProvider());
+        AtomicBoolean identityQueried = new AtomicBoolean(false);
         sshd.setPublickeyAuthenticator((username, key, session) -> {
             if (username.equals(getCurrentTestName())) {
                 identityQueried.set(true);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/AttributeStoreTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/AttributeStoreTest.java b/sshd-core/src/test/java/org/apache/sshd/common/AttributeStoreTest.java
index c0f1a73..a8137f0 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/AttributeStoreTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/AttributeStoreTest.java
@@ -44,38 +44,38 @@ public class AttributeStoreTest extends BaseTestSupport {
 
     @Test
     public void testResolveFactoryManagerAttribute() {
-        assertNull("Unexpected null factory value", AttributeStore.resolveAttribute((FactoryManager) null, KEY));
+        assertNull("Unexpected null factory value", FactoryManager.resolveAttribute((FactoryManager) null, KEY));
 
         FactoryManager manager = Mockito.mock(FactoryManager.class);
         String expected = setAttributeValue(manager, getCurrentTestName());
-        assertSame("Mismatched resolved value", expected, AttributeStore.resolveAttribute(manager, KEY));
+        assertSame("Mismatched resolved value", expected, FactoryManager.resolveAttribute(manager, KEY));
     }
 
     @Test
     public void testResolveSessionAttribute() {
-        assertNull("Unexpected null session value", AttributeStore.resolveAttribute((Session) null, KEY));
+        assertNull("Unexpected null session value", Session.resolveAttribute((Session) null, KEY));
 
         Session session = Mockito.mock(Session.class);
-        final AtomicInteger managerCount = new AtomicInteger(0);
+        AtomicInteger managerCount = new AtomicInteger(0);
         Mockito.when(session.getFactoryManager()).then(invocation -> {
             managerCount.incrementAndGet();
             return null;
         });
         setAttributeValue(session, null);
-        assertNull("Unexpected success for empty attribute", AttributeStore.resolveAttribute(session, KEY));
+        assertNull("Unexpected success for empty attribute", Session.resolveAttribute(session, KEY));
         assertEquals("Factory manager not requested", 1, managerCount.getAndSet(0));
 
         String expected = setAttributeValue(session, getCurrentTestName());
-        assertSame("Mismatched attribute value", expected, AttributeStore.resolveAttribute(session, KEY));
+        assertSame("Mismatched attribute value", expected, Session.resolveAttribute(session, KEY));
         assertEquals("Unexpected manager request", 0, managerCount.get());
     }
 
     @Test
     public void testResolveChannelAttribute() {
-        assertNull("Unexpected null channek value", AttributeStore.resolveAttribute((Channel) null, KEY));
+        assertNull("Unexpected null channek value", Channel.resolveAttribute((Channel) null, KEY));
 
-        final Session session = Mockito.mock(Session.class);
-        final AtomicInteger managerCount = new AtomicInteger(0);
+        Session session = Mockito.mock(Session.class);
+        AtomicInteger managerCount = new AtomicInteger(0);
         Mockito.when(session.getFactoryManager()).thenAnswer(invocation -> {
             managerCount.incrementAndGet();
             return null;
@@ -83,19 +83,19 @@ public class AttributeStoreTest extends BaseTestSupport {
         setAttributeValue(session, null);
 
         Channel channel = Mockito.mock(Channel.class);
-        final AtomicInteger sessionCount = new AtomicInteger(0);
+        AtomicInteger sessionCount = new AtomicInteger(0);
         Mockito.when(channel.getSession()).thenAnswer(invocation -> {
             sessionCount.incrementAndGet();
             return session;
         });
         setAttributeValue(channel, null);
 
-        assertNull("Unexpected success for empty attribute", AttributeStore.resolveAttribute(channel, KEY));
+        assertNull("Unexpected success for empty attribute", Channel.resolveAttribute(channel, KEY));
         assertEquals("Session not requested", 1, sessionCount.getAndSet(0));
         assertEquals("Factory manager not requested", 1, managerCount.getAndSet(0));
 
         String expected = setAttributeValue(channel, getCurrentTestName());
-        assertSame("Mismatched attribute value", expected, AttributeStore.resolveAttribute(channel, KEY));
+        assertSame("Mismatched attribute value", expected, Channel.resolveAttribute(channel, KEY));
         assertEquals("Unexpected session request", 0, sessionCount.get());
         assertEquals("Unexpected manager request", 0, managerCount.get());
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/PropertyResolverUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/PropertyResolverUtilsTest.java b/sshd-core/src/test/java/org/apache/sshd/common/PropertyResolverUtilsTest.java
index 12319c0..69b6fe8 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/PropertyResolverUtilsTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/PropertyResolverUtilsTest.java
@@ -30,7 +30,7 @@ import java.util.concurrent.TimeUnit;
 
 import org.apache.sshd.common.session.Session;
 import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
+import org.apache.sshd.util.test.JUnitTestSupport;
 import org.apache.sshd.util.test.NoIoTestCase;
 import org.junit.FixMethodOrder;
 import org.junit.Test;
@@ -43,21 +43,21 @@ import org.mockito.Mockito;
  */
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 @Category({ NoIoTestCase.class })
-public class PropertyResolverUtilsTest extends BaseTestSupport {
+public class PropertyResolverUtilsTest extends JUnitTestSupport {
     public PropertyResolverUtilsTest() {
         super();
     }
 
     @Test
     public void testResolveAndUpdateClosestPropertyValue() {
-        final String propName = getCurrentTestName();
-        final String rootValue = getClass().getPackage().getName();
+        String propName = getCurrentTestName();
+        String rootValue = getClass().getPackage().getName();
         Session resolver = createMockSession();
         FactoryManager root = Objects.requireNonNull(resolver.getFactoryManager(), "No manager");
         assertNull("Unexpected root previous value", PropertyResolverUtils.updateProperty(root, propName, rootValue));
         assertSame("Mismatched root value", rootValue, PropertyResolverUtils.getString(resolver, propName));
 
-        final String nodeValue = getClass().getSimpleName();
+        String nodeValue = getClass().getSimpleName();
         assertNull("Unexpected node previous value", PropertyResolverUtils.updateProperty(resolver, propName, nodeValue));
         assertSame("Mismatched node value", nodeValue, PropertyResolverUtils.getString(resolver, propName));
     }
@@ -90,7 +90,7 @@ public class PropertyResolverUtilsTest extends BaseTestSupport {
             System.clearProperty(propKey);
         }
 
-        for (final boolean expected : new boolean[]{false, true}) {
+        for (boolean expected : new boolean[]{false, true}) {
             try {
                 System.setProperty(propKey, Boolean.toString(expected));
                 testBooleanProperty(resolver, propName, expected);
@@ -102,8 +102,8 @@ public class PropertyResolverUtilsTest extends BaseTestSupport {
 
     @Test
     public void testLongProperty() {
-        final long expected = System.currentTimeMillis();
-        final String name = getCurrentTestName();
+        long expected = System.currentTimeMillis();
+        String name = getCurrentTestName();
 
         Session session = createMockSession();
         assertEquals("Mismatched empty props value", expected, PropertyResolverUtils.getLongProperty(session, name, expected));
@@ -137,8 +137,8 @@ public class PropertyResolverUtilsTest extends BaseTestSupport {
 
     @Test
     public void testIntegerProperty() {
-        final int expected = 3777347;
-        final String name = getCurrentTestName();
+        int expected = 3777347;
+        String name = getCurrentTestName();
 
         Session session = createMockSession();
         assertEquals("Mismatched empty props value", expected, PropertyResolverUtils.getIntProperty(session, name, expected));
@@ -176,8 +176,8 @@ public class PropertyResolverUtilsTest extends BaseTestSupport {
 
     @Test
     public void testBooleanProperty() {
-        for (final boolean expected : new boolean[]{false, true}) {
-            final String name = getCurrentTestName();
+        for (boolean expected : new boolean[]{false, true}) {
+            String name = getCurrentTestName();
 
             Session session = createMockSession();
             assertEquals("Mismatched empty props value", expected, PropertyResolverUtils.getBooleanProperty(session, name, expected));

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/SshConstantsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/SshConstantsTest.java b/sshd-core/src/test/java/org/apache/sshd/common/SshConstantsTest.java
deleted file mode 100644
index a15ca6b..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/SshConstantsTest.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.common;
-
-import java.util.Collection;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class SshConstantsTest extends BaseTestSupport {
-    public SshConstantsTest() {
-        super();
-    }
-
-    @Test
-    public void testGetDisconnectReason() {
-        for (int reason = SshConstants.SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT; reason <= SshConstants.SSH2_DISCONNECT_ILLEGAL_USER_NAME; reason++) {
-            String name = SshConstants.getDisconnectReasonName(reason);
-            assertTrue("Mismatched name for reason=" + reason + ": " + name, name.startsWith("SSH2_DISCONNECT_"));
-        }
-    }
-
-    @Test
-    public void testGetOpenErrorName() {
-        for (int code = SshConstants.SSH_OPEN_ADMINISTRATIVELY_PROHIBITED; code <= SshConstants.SSH_OPEN_RESOURCE_SHORTAGE; code++) {
-            String name = SshConstants.getOpenErrorCodeName(code);
-            assertTrue("Mismatched name for code=" + code + ": " + name, name.startsWith("SSH_OPEN_"));
-        }
-    }
-
-    @Test
-    public void testAmbiguousOpcodes() throws Exception {
-        int[] knownAmbiguities = {30, 31, 60};
-        Collection<Integer> opcodes = SshConstants.getAmbiguousOpcodes();
-        assertTrue("Not enough ambiguities found", GenericUtils.size(opcodes) >= knownAmbiguities.length);
-
-        for (int cmd : knownAmbiguities) {
-            assertEquals("Mismatched mnemonic for known ambiguity=" + cmd, Integer.toString(cmd), SshConstants.getCommandMessageName(cmd));
-            assertTrue("Known ambiguity not reported as such: " + cmd, SshConstants.isAmbiguousOpcode(cmd));
-            assertTrue("Known ambiguity=" + cmd + " not listed: " + opcodes, opcodes.contains(cmd));
-        }
-
-        for (Integer cmd : opcodes) {
-            assertEquals("Mismatched mnemonic for " + cmd, cmd.toString(), SshConstants.getCommandMessageName(cmd));
-            assertTrue("Opcode not detected as ambiguous: " + cmd, SshConstants.isAmbiguousOpcode(cmd));
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/VersionPropertiesTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/VersionPropertiesTest.java b/sshd-core/src/test/java/org/apache/sshd/common/VersionPropertiesTest.java
deleted file mode 100644
index 06cfffc..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/VersionPropertiesTest.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.common;
-
-import java.util.Map;
-
-import org.apache.sshd.common.config.VersionProperties;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-public class VersionPropertiesTest extends BaseTestSupport {
-    public VersionPropertiesTest() {
-        super();
-    }
-
-    @Test
-    public void testNonEmptyProperties() {
-        Map<?, ?> props = VersionProperties.getVersionProperties();
-        assertTrue(GenericUtils.isNotEmpty(props));
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/auth/AuthenticationTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/auth/AuthenticationTest.java b/sshd-core/src/test/java/org/apache/sshd/common/auth/AuthenticationTest.java
index 322f55c..d7df493 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/auth/AuthenticationTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/auth/AuthenticationTest.java
@@ -74,7 +74,7 @@ import org.apache.sshd.server.session.ServerSession;
 import org.apache.sshd.server.session.ServerSessionImpl;
 import org.apache.sshd.server.session.SessionFactory;
 import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.Utils;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.FixMethodOrder;
@@ -587,14 +587,14 @@ public class AuthenticationTest extends BaseTestSupport {
     @Test   // see SSHD-618
     public void testPublicKeyAuthDifferentThanKex() throws Exception {
         KeyPairProvider serverKeys = KeyPairProvider.wrap(
-                    Utils.generateKeyPair(KeyUtils.RSA_ALGORITHM, 1024),
-                    Utils.generateKeyPair(KeyUtils.DSS_ALGORITHM, 512),
-                    Utils.generateKeyPair(KeyUtils.EC_ALGORITHM, 256));
+                    CommonTestSupportUtils.generateKeyPair(KeyUtils.RSA_ALGORITHM, 1024),
+                    CommonTestSupportUtils.generateKeyPair(KeyUtils.DSS_ALGORITHM, 512),
+                    CommonTestSupportUtils.generateKeyPair(KeyUtils.EC_ALGORITHM, 256));
         sshd.setKeyPairProvider(serverKeys);
         sshd.setKeyboardInteractiveAuthenticator(KeyboardInteractiveAuthenticator.NONE);
         sshd.setPasswordAuthenticator(RejectAllPasswordAuthenticator.INSTANCE);
 
-        final KeyPair clientIdentity = Utils.generateKeyPair(KeyUtils.EC_ALGORITHM, 256);
+        final KeyPair clientIdentity = CommonTestSupportUtils.generateKeyPair(KeyUtils.EC_ALGORITHM, 256);
         sshd.setPublickeyAuthenticator((username, key, session) -> {
             String keyType = KeyUtils.getKeyType(key);
             String expType = KeyUtils.getKeyType(clientIdentity);
@@ -650,7 +650,7 @@ public class AuthenticationTest extends BaseTestSupport {
                                     super.sendPublicKeyResponse(session, username, KeyPairProvider.SSH_DSS, key, keyBlob, offset, blobLen, buffer);
                                 } else if (count == 2) {
                                     // send another key
-                                    KeyPair otherPair = org.apache.sshd.util.test.Utils.generateKeyPair(KeyUtils.RSA_ALGORITHM, 1024);
+                                    KeyPair otherPair = org.apache.sshd.util.test.CommonTestSupportUtils.generateKeyPair(KeyUtils.RSA_ALGORITHM, 1024);
                                     PublicKey otherKey = otherPair.getPublic();
                                     Buffer buf = session.createBuffer(SshConstants.SSH_MSG_USERAUTH_PK_OK, blobLen + alg.length() + Long.SIZE);
                                     buf.putString(alg);
@@ -666,7 +666,7 @@ public class AuthenticationTest extends BaseTestSupport {
         }));
 
         try (SshClient client = setupTestClient()) {
-            KeyPair clientIdentity = Utils.generateKeyPair(KeyUtils.RSA_ALGORITHM, 1024);
+            KeyPair clientIdentity = CommonTestSupportUtils.generateKeyPair(KeyUtils.RSA_ALGORITHM, 1024);
             client.start();
 
             try {
@@ -693,7 +693,7 @@ public class AuthenticationTest extends BaseTestSupport {
     public void testHostBasedAuthentication() throws Exception {
         String hostClienUser = getClass().getSimpleName();
         String hostClientName = SshdSocketAddress.toAddressString(SshdSocketAddress.getFirstExternalNetwork4Address());
-        KeyPair hostClientKey = Utils.generateKeyPair(KeyUtils.RSA_ALGORITHM, 1024);
+        KeyPair hostClientKey = CommonTestSupportUtils.generateKeyPair(KeyUtils.RSA_ALGORITHM, 1024);
         AtomicInteger invocationCount = new AtomicInteger(0);
         sshd.setHostBasedAuthenticator((session, username, clientHostKey, clientHostName, clientUsername, certificates) -> {
             invocationCount.incrementAndGet();
@@ -751,7 +751,7 @@ public class AuthenticationTest extends BaseTestSupport {
         sshd.setKeyboardInteractiveAuthenticator(KeyboardInteractiveAuthenticator.NONE);
 
         try (SshClient client = setupTestClient()) {
-            KeyPair kp = Utils.generateKeyPair(KeyUtils.RSA_ALGORITHM, 1024);
+            KeyPair kp = CommonTestSupportUtils.generateKeyPair(KeyUtils.RSA_ALGORITHM, 1024);
             client.start();
             try {
                 for (int index = 1; index < 3; index++) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/cipher/AES192CTRTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/cipher/AES192CTRTest.java b/sshd-core/src/test/java/org/apache/sshd/common/cipher/AES192CTRTest.java
deleted file mode 100644
index 6611702..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/cipher/AES192CTRTest.java
+++ /dev/null
@@ -1,41 +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.cipher;
-
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-public class AES192CTRTest extends BaseCipherTest {
-    public AES192CTRTest() {
-        super();
-    }
-
-    @Test
-    public void testEncryptDecrypt() throws Exception {
-        // for AES 256 bits we need the JCE unlimited strength policy
-        ensureKeySizeSupported(16, 24, "AES", "AES/CTR/NoPadding");
-        testEncryptDecrypt(BuiltinCiphers.aes192ctr);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/cipher/AES256CBCTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/cipher/AES256CBCTest.java b/sshd-core/src/test/java/org/apache/sshd/common/cipher/AES256CBCTest.java
deleted file mode 100644
index dd39fd4..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/cipher/AES256CBCTest.java
+++ /dev/null
@@ -1,41 +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.cipher;
-
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-public class AES256CBCTest extends BaseCipherTest {
-    public AES256CBCTest() {
-        super();
-    }
-
-    @Test
-    public void testEncryptDecrypt() throws Exception {
-        // for AES 256 bits we need the JCE unlimited strength policy
-        ensureKeySizeSupported(16, 32, "AES", "AES/CBC/NoPadding");
-        testEncryptDecrypt(BuiltinCiphers.aes256cbc);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/cipher/ARCFOUR128Test.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/cipher/ARCFOUR128Test.java b/sshd-core/src/test/java/org/apache/sshd/common/cipher/ARCFOUR128Test.java
deleted file mode 100644
index 1c37449..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/cipher/ARCFOUR128Test.java
+++ /dev/null
@@ -1,39 +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.cipher;
-
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-public class ARCFOUR128Test extends BaseCipherTest {
-    public ARCFOUR128Test() {
-        super();
-    }
-
-    @Test
-    public void testEncryptDecrypt() throws Exception {
-        testEncryptDecrypt(BuiltinCiphers.arcfour128);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/cipher/ARCFOUR256Test.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/cipher/ARCFOUR256Test.java b/sshd-core/src/test/java/org/apache/sshd/common/cipher/ARCFOUR256Test.java
deleted file mode 100644
index 5511e0f..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/cipher/ARCFOUR256Test.java
+++ /dev/null
@@ -1,41 +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.cipher;
-
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-public class ARCFOUR256Test extends BaseCipherTest {
-    public ARCFOUR256Test() {
-        super();
-    }
-
-    @Test
-    public void testEncryptDecrypt() throws Exception {
-        // for RC4 256 bits we need the JCE unlimited strength policy
-        ensureKeySizeSupported(32, "ARCFOUR", "RC4");
-        testEncryptDecrypt(BuiltinCiphers.arcfour256);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/cipher/BaseCipherTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/cipher/BaseCipherTest.java b/sshd-core/src/test/java/org/apache/sshd/common/cipher/BaseCipherTest.java
deleted file mode 100644
index fd30e18..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/cipher/BaseCipherTest.java
+++ /dev/null
@@ -1,95 +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.cipher;
-
-import java.nio.charset.StandardCharsets;
-import java.security.GeneralSecurityException;
-import java.security.InvalidKeyException;
-
-import javax.crypto.spec.IvParameterSpec;
-import javax.crypto.spec.SecretKeySpec;
-
-import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.cipher.Cipher.Mode;
-import org.apache.sshd.common.util.security.SecurityUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.Assume;
-import org.junit.experimental.categories.Category;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@Category({ NoIoTestCase.class })
-public abstract class BaseCipherTest extends BaseTestSupport {
-    protected BaseCipherTest() {
-        super();
-    }
-
-    protected void ensureKeySizeSupported(int bsize, String algorithm, String transformation) throws GeneralSecurityException {
-        try {
-            javax.crypto.Cipher cipher = SecurityUtils.getCipher(transformation);
-            byte[] key = new byte[bsize];
-            cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, new SecretKeySpec(key, algorithm));
-        } catch (GeneralSecurityException e) {
-            if (e instanceof InvalidKeyException) {    // NOTE: assumption violations are NOT test failures...
-                Assume.assumeTrue(algorithm + "/" + transformation + "[" + bsize + "] N/A", false);
-            }
-
-            throw e;
-        }
-    }
-
-    protected void ensureKeySizeSupported(int ivsize, int bsize, String algorithm, String transformation) throws GeneralSecurityException {
-        try {
-            javax.crypto.Cipher cipher = SecurityUtils.getCipher(transformation);
-            byte[] key = new byte[bsize];
-            byte[] iv = new byte[ivsize];
-            cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, new SecretKeySpec(key, algorithm), new IvParameterSpec(iv));
-        } catch (GeneralSecurityException e) {
-            if (e instanceof InvalidKeyException) {
-                Assume.assumeTrue(algorithm + "/" + transformation + "[" + bsize + "/" + ivsize + "]", false /* force exception */);
-            }
-
-            throw e;
-        }
-    }
-
-    protected void testEncryptDecrypt(NamedFactory<Cipher> factory) throws Exception {
-        String facName = factory.getName();
-        Cipher enc = factory.create();
-        int keySize = enc.getBlockSize();
-        int ivSize = enc.getIVSize();
-        byte[] key = new byte[keySize];
-        byte[] iv = new byte[ivSize];
-        enc.init(Mode.Encrypt, key, iv);
-
-        byte[] expected = facName.getBytes(StandardCharsets.UTF_8);
-        byte[] workBuf = expected.clone();    // need to clone since the cipher works in-line
-        enc.update(workBuf, 0, workBuf.length);
-
-        Cipher dec = factory.create();
-        dec.init(Mode.Decrypt, key, iv);
-        byte[] actual = workBuf.clone();
-        dec.update(actual, 0, actual.length);
-
-        assertArrayEquals(facName, expected, actual);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/cipher/CipherTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/cipher/CipherTest.java b/sshd-core/src/test/java/org/apache/sshd/common/cipher/CipherTest.java
index 4a02074..0089782 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/cipher/CipherTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/cipher/CipherTest.java
@@ -35,10 +35,11 @@ import org.apache.sshd.common.random.Random;
 import org.apache.sshd.common.util.buffer.BufferUtils;
 import org.apache.sshd.server.SshServer;
 import org.apache.sshd.util.test.BaseTestSupport;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
+import org.apache.sshd.util.test.CoreTestSupportUtils;
 import org.apache.sshd.util.test.JSchLogger;
 import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
 import org.apache.sshd.util.test.SimpleUserInfo;
-import org.apache.sshd.util.test.Utils;
 import org.junit.AfterClass;
 import org.junit.Assume;
 import org.junit.BeforeClass;
@@ -67,15 +68,15 @@ public class CipherTest extends BaseTestSupport {
      * NOTE !!! order is important since we build from it the C2S/S2C ciphers proposal
      */
     private static final List<Object[]> PARAMETERS =
-            Collections.unmodifiableList(Arrays.asList(
-                    new Object[]{BuiltinCiphers.aes128cbc, com.jcraft.jsch.jce.AES128CBC.class, NUM_LOADTEST_ROUNDS},
-                    new Object[]{BuiltinCiphers.tripledescbc, com.jcraft.jsch.jce.TripleDESCBC.class, NUM_LOADTEST_ROUNDS},
-                    new Object[]{BuiltinCiphers.blowfishcbc, com.jcraft.jsch.jce.BlowfishCBC.class, NUM_LOADTEST_ROUNDS},
-                    new Object[]{BuiltinCiphers.aes192cbc, com.jcraft.jsch.jce.AES192CBC.class, NUM_LOADTEST_ROUNDS},
-                    new Object[]{BuiltinCiphers.aes256cbc, com.jcraft.jsch.jce.AES256CBC.class, NUM_LOADTEST_ROUNDS},
-                    new Object[]{BuiltinCiphers.arcfour128, com.jcraft.jsch.jce.ARCFOUR128.class, NUM_LOADTEST_ROUNDS},
-                    new Object[]{BuiltinCiphers.arcfour256, com.jcraft.jsch.jce.ARCFOUR256.class, NUM_LOADTEST_ROUNDS}
-            ));
+        Collections.unmodifiableList(Arrays.asList(
+            new Object[]{BuiltinCiphers.aes128cbc, com.jcraft.jsch.jce.AES128CBC.class, NUM_LOADTEST_ROUNDS},
+            new Object[]{BuiltinCiphers.tripledescbc, com.jcraft.jsch.jce.TripleDESCBC.class, NUM_LOADTEST_ROUNDS},
+            new Object[]{BuiltinCiphers.blowfishcbc, com.jcraft.jsch.jce.BlowfishCBC.class, NUM_LOADTEST_ROUNDS},
+            new Object[]{BuiltinCiphers.aes192cbc, com.jcraft.jsch.jce.AES192CBC.class, NUM_LOADTEST_ROUNDS},
+            new Object[]{BuiltinCiphers.aes256cbc, com.jcraft.jsch.jce.AES256CBC.class, NUM_LOADTEST_ROUNDS},
+            new Object[]{BuiltinCiphers.arcfour128, com.jcraft.jsch.jce.ARCFOUR128.class, NUM_LOADTEST_ROUNDS},
+            new Object[]{BuiltinCiphers.arcfour256, com.jcraft.jsch.jce.ARCFOUR256.class, NUM_LOADTEST_ROUNDS}
+        ));
 
     private static final List<NamedResource> TEST_CIPHERS =
         Collections.unmodifiableList(
@@ -86,7 +87,7 @@ public class CipherTest extends BaseTestSupport {
     private static SshServer sshd;
     private static int port;
 
-    private final Random random = Utils.getRandomizerInstance();
+    private final Random random = CommonTestSupportUtils.getRandomizerInstance();
     private final BuiltinCiphers builtInCipher;
     private final Class<? extends com.jcraft.jsch.Cipher> jschCipher;
     private final int loadTestRounds;
@@ -105,7 +106,7 @@ public class CipherTest extends BaseTestSupport {
     @BeforeClass
     public static void setupClientAndServer() throws Exception {
         JSchLogger.init();
-        sshd = Utils.setupTestServer(CipherTest.class);
+        sshd = CoreTestSupportUtils.setupTestServer(CipherTest.class);
         sshd.start();
         port = sshd.getPort();
     }


[09/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common utilities code from sshd-core into sshd-common (new artifact)

Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/cipher/ECCurvesTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/cipher/ECCurvesTest.java b/sshd-core/src/test/java/org/apache/sshd/common/cipher/ECCurvesTest.java
deleted file mode 100644
index 7df1c64..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/cipher/ECCurvesTest.java
+++ /dev/null
@@ -1,107 +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.cipher;
-
-import java.util.EnumSet;
-import java.util.Set;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class ECCurvesTest extends BaseTestSupport {
-    public ECCurvesTest() {
-        super();
-    }
-
-    @Test
-    public void testFromName() {
-        for (ECCurves expected : ECCurves.VALUES) {
-            String name = expected.getName();
-            for (int index = 0; index < name.length(); index++) {
-                ECCurves actual = ECCurves.fromCurveName(name);
-                assertSame(name, expected, actual);
-                name = shuffleCase(name);
-            }
-        }
-    }
-
-    @Test
-    public void testAllNamesListed() {
-        Set<ECCurves> listed = EnumSet.noneOf(ECCurves.class);
-        for (String name : ECCurves.NAMES) {
-            ECCurves c = ECCurves.fromCurveName(name);
-            assertNotNull("No curve for listed name=" + name, c);
-            assertTrue("Duplicated listed name: " + name, listed.add(c));
-        }
-
-        assertEquals("Mismatched listed vs. values", ECCurves.VALUES, listed);
-    }
-
-    @Test
-    public void testFromKeySize() {
-        for (ECCurves expected : ECCurves.VALUES) {
-            String name = expected.getName();
-            ECCurves actual = ECCurves.fromCurveSize(expected.getKeySize());
-            assertSame(name, expected, actual);
-        }
-    }
-
-    @Test
-    public void testFromCurveParameters() {
-        for (ECCurves expected : ECCurves.VALUES) {
-            String name = expected.getName();
-            ECCurves actual = ECCurves.fromCurveParameters(expected.getParameters());
-            assertSame(name, expected, actual);
-        }
-    }
-
-    @Test
-    public void testFromKeyType() {
-        for (ECCurves expected : ECCurves.VALUES) {
-            String keyType = expected.getKeyType();
-            for (int index = 0; index < keyType.length(); index++) {
-                ECCurves actual = ECCurves.fromKeyType(keyType);
-                assertSame(keyType, expected, actual);
-                keyType = shuffleCase(keyType);
-            }
-        }
-    }
-
-    @Test
-    public void testAllKeyTypesListed() {
-        Set<ECCurves> listed = EnumSet.noneOf(ECCurves.class);
-        for (String name : ECCurves.KEY_TYPES) {
-            ECCurves c = ECCurves.fromKeyType(name);
-            assertNotNull("No curve for listed key type=" + name, c);
-            assertTrue("Duplicated listed key type: " + name, listed.add(c));
-        }
-
-        assertEquals("Mismatched listed vs. values", ECCurves.VALUES, listed);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/compression/BuiltinCompressionsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/compression/BuiltinCompressionsTest.java b/sshd-core/src/test/java/org/apache/sshd/common/compression/BuiltinCompressionsTest.java
deleted file mode 100644
index 0daeb23..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/compression/BuiltinCompressionsTest.java
+++ /dev/null
@@ -1,171 +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.compression;
-
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.List;
-import java.util.Random;
-import java.util.Set;
-
-import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.NamedResource;
-import org.apache.sshd.common.compression.BuiltinCompressions.ParseResult;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-import org.mockito.Mockito;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class BuiltinCompressionsTest extends BaseTestSupport {
-    public BuiltinCompressionsTest() {
-        super();
-    }
-
-    @Test
-    public void testFromFactoryName() {
-        for (BuiltinCompressions expected : BuiltinCompressions.VALUES) {
-            String name = expected.getName();
-
-            for (int index = 0; index < name.length(); index++) {
-                BuiltinCompressions actual = BuiltinCompressions.fromFactoryName(name);
-                assertSame(name, expected, actual);
-                name = shuffleCase(name);
-            }
-        }
-    }
-
-    @Test
-    public void testAllConstantsCovered() throws Exception {
-        Set<BuiltinCompressions> avail = EnumSet.noneOf(BuiltinCompressions.class);
-        Field[] fields = BuiltinCompressions.Constants.class.getFields();
-        for (Field f : fields) {
-            String name = (String) f.get(null);
-            BuiltinCompressions value = BuiltinCompressions.fromFactoryName(name);
-            assertNotNull("No match found for " + name, value);
-            assertTrue(name + " re-specified", avail.add(value));
-        }
-
-        assertEquals("Incomplete coverage", BuiltinCompressions.VALUES, avail);
-    }
-
-    @Test
-    public void testParseCompressionsList() {
-        List<String> builtin = NamedResource.getNameList(BuiltinCompressions.VALUES);
-        List<String> unknown = Arrays.asList(getClass().getPackage().getName(), getClass().getSimpleName(), getCurrentTestName());
-        Random rnd = new Random();
-        for (int index = 0; index < (builtin.size() + unknown.size()); index++) {
-            Collections.shuffle(builtin, rnd);
-            Collections.shuffle(unknown, rnd);
-
-            List<String> weavedList = new ArrayList<>(builtin.size() + unknown.size());
-            for (int bIndex = 0, uIndex = 0; (bIndex < builtin.size()) || (uIndex < unknown.size());) {
-                boolean useBuiltin = false;
-                if (bIndex < builtin.size()) {
-                    useBuiltin = uIndex >= unknown.size() || rnd.nextBoolean();
-                }
-
-                if (useBuiltin) {
-                    weavedList.add(builtin.get(bIndex));
-                    bIndex++;
-                } else if (uIndex < unknown.size()) {
-                    weavedList.add(unknown.get(uIndex));
-                    uIndex++;
-                }
-            }
-
-            String fullList = GenericUtils.join(weavedList, ',');
-            ParseResult result = BuiltinCompressions.parseCompressionsList(fullList);
-            List<String> parsed = NamedResource.getNameList(result.getParsedFactories());
-            List<String> missing = result.getUnsupportedFactories();
-
-            // makes sure not only that the contents are the same but also the order
-            assertListEquals(fullList + "[parsed]", builtin, parsed);
-            assertListEquals(fullList + "[unsupported]", unknown, missing);
-        }
-    }
-
-    @Test
-    public void testResolveFactoryOnBuiltinValues() {
-        for (NamedFactory<Compression> expected : BuiltinCompressions.VALUES) {
-            String name = expected.getName();
-            NamedFactory<Compression> actual = BuiltinCompressions.resolveFactory(name);
-            assertSame(name, expected, actual);
-        }
-    }
-
-    @Test
-    public void testNotAllowedToRegisterBuiltinFactories() {
-        for (CompressionFactory expected : BuiltinCompressions.VALUES) {
-            try {
-                BuiltinCompressions.registerExtension(expected);
-                fail("Unexpected success for " + expected.getName());
-            } catch (IllegalArgumentException e) {
-                // expected - ignored
-            }
-        }
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void testNotAllowedToOverrideRegisteredFactories() {
-        CompressionFactory expected = Mockito.mock(CompressionFactory.class);
-        Mockito.when(expected.getName()).thenReturn(getCurrentTestName());
-
-        String name = expected.getName();
-        try {
-            for (int index = 1; index <= Byte.SIZE; index++) {
-                BuiltinCompressions.registerExtension(expected);
-                assertEquals("Unexpected success at attempt #" + index, 1, index);
-            }
-        } finally {
-            BuiltinCompressions.unregisterExtension(name);
-        }
-    }
-
-    @Test
-    public void testResolveFactoryOnRegisteredExtension() {
-        CompressionFactory expected = Mockito.mock(CompressionFactory.class);
-        Mockito.when(expected.getName()).thenReturn(getCurrentTestName());
-
-        String name = expected.getName();
-        try {
-            assertNull("Extension already registered", BuiltinCompressions.resolveFactory(name));
-            BuiltinCompressions.registerExtension(expected);
-
-            NamedFactory<Compression> actual = BuiltinCompressions.resolveFactory(name);
-            assertSame("Mismatched resolved instance", expected, actual);
-        } finally {
-            NamedFactory<Compression> actual = BuiltinCompressions.unregisterExtension(name);
-            assertSame("Mismatched unregistered instance", expected, actual);
-            assertNull("Extension not un-registered", BuiltinCompressions.resolveFactory(name));
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/compression/CompressionTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/compression/CompressionTest.java b/sshd-core/src/test/java/org/apache/sshd/common/compression/CompressionTest.java
index a8132fb..b18c273 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/compression/CompressionTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/compression/CompressionTest.java
@@ -33,10 +33,11 @@ import org.apache.sshd.common.session.Session;
 import org.apache.sshd.common.session.SessionListener;
 import org.apache.sshd.server.SshServer;
 import org.apache.sshd.util.test.BaseTestSupport;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
+import org.apache.sshd.util.test.CoreTestSupportUtils;
 import org.apache.sshd.util.test.JSchLogger;
 import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
 import org.apache.sshd.util.test.SimpleUserInfo;
-import org.apache.sshd.util.test.Utils;
 import org.junit.After;
 import org.junit.AfterClass;
 import org.junit.Assume;
@@ -96,8 +97,8 @@ public class CompressionTest extends BaseTestSupport {
     public static void setupClientAndServer() throws Exception {
         JSchLogger.init();
 
-        sshd = Utils.setupTestServer(MacTest.class);
-        sshd.setKeyPairProvider(Utils.createTestHostKeyProvider(MacTest.class));
+        sshd = CoreTestSupportUtils.setupTestServer(MacTest.class);
+        sshd.setKeyPairProvider(CommonTestSupportUtils.createTestHostKeyProvider(MacTest.class));
         sshd.start();
         port = sshd.getPort();
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/config/SshConfigFileReaderTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/config/SshConfigFileReaderTest.java b/sshd-core/src/test/java/org/apache/sshd/common/config/SshConfigFileReaderTest.java
index f9de9b1..94ce291 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/config/SshConfigFileReaderTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/config/SshConfigFileReaderTest.java
@@ -71,7 +71,7 @@ public class SshConfigFileReaderTest extends BaseTestSupport {
         URL url = getClass().getResource("sshd_config");
         assertNotNull("Cannot locate test file", url);
 
-        Properties props = SshConfigFileReader.readConfigFile(url);
+        Properties props = ConfigFileReaderSupport.readConfigFile(url);
         assertFalse("No properties read", props.isEmpty());
         assertTrue("Unexpected commented property data", GenericUtils.isEmpty(props.getProperty("ListenAddress")));
         assertTrue("Unexpected non-existing property data", GenericUtils.isEmpty(props.getProperty(getCurrentTestName())));
@@ -86,55 +86,55 @@ public class SshConfigFileReaderTest extends BaseTestSupport {
     @Test
     public void testParseCiphersList() {
         List<? extends NamedResource> expected = BaseBuilder.DEFAULT_CIPHERS_PREFERENCE;
-        Properties props = initNamedResourceProperties(SshConfigFileReader.CIPHERS_CONFIG_PROP, expected);
+        Properties props = initNamedResourceProperties(ConfigFileReaderSupport.CIPHERS_CONFIG_PROP, expected);
         BuiltinCiphers.ParseResult result = SshConfigFileReader.getCiphers(PropertyResolverUtils.toPropertyResolver(props));
         testParsedFactoriesList(expected, result.getParsedFactories(), result.getUnsupportedFactories());
     }
 
     @Test
     public void testKnownDefaultCipherFactoriesList() {
-        testKnownDefaultFactoriesList(SshConfigFileReader.DEFAULT_CIPHERS, BuiltinCiphers::fromFactoryName,
+        testKnownDefaultFactoriesList(ConfigFileReaderSupport.DEFAULT_CIPHERS, BuiltinCiphers::fromFactoryName,
             GenericUtils.asSortedSet(String.CASE_INSENSITIVE_ORDER, "cast128-cbc", "arcfour"));
     }
 
     @Test
     public void testParseMacsList() {
         List<? extends NamedResource> expected = BaseBuilder.DEFAULT_MAC_PREFERENCE;
-        Properties props = initNamedResourceProperties(SshConfigFileReader.MACS_CONFIG_PROP, expected);
+        Properties props = initNamedResourceProperties(ConfigFileReaderSupport.MACS_CONFIG_PROP, expected);
         BuiltinMacs.ParseResult result = SshConfigFileReader.getMacs(PropertyResolverUtils.toPropertyResolver(props));
         testParsedFactoriesList(expected, result.getParsedFactories(), result.getUnsupportedFactories());
     }
 
     @Test
     public void testKnownDefaultMacFactoriesList() {
-        testKnownDefaultFactoriesList(SshConfigFileReader.DEFAULT_MACS, BuiltinMacs::fromFactoryName,
+        testKnownDefaultFactoriesList(ConfigFileReaderSupport.DEFAULT_MACS, BuiltinMacs::fromFactoryName,
             GenericUtils.asSortedSet(String.CASE_INSENSITIVE_ORDER, "umac-64@openssh.com", "hmac-ripemd160", "hmac-sha2-256-96", "hmac-sha2-512-96"));
     }
 
     @Test
     public void testParseSignaturesList() {
         List<? extends NamedResource> expected = BaseBuilder.DEFAULT_SIGNATURE_PREFERENCE;
-        Properties props = initNamedResourceProperties(SshConfigFileReader.HOST_KEY_ALGORITHMS_CONFIG_PROP, expected);
+        Properties props = initNamedResourceProperties(ConfigFileReaderSupport.HOST_KEY_ALGORITHMS_CONFIG_PROP, expected);
         BuiltinSignatures.ParseResult result = SshConfigFileReader.getSignatures(PropertyResolverUtils.toPropertyResolver(props));
         testParsedFactoriesList(expected, result.getParsedFactories(), result.getUnsupportedFactories());
     }
 
     @Test
     public void testKnownDefaultSignatureFactoriesList() {
-        testKnownDefaultFactoriesList(SshConfigFileReader.DEFAULT_HOST_KEY_ALGORITHMS, BuiltinSignatures::fromFactoryName);
+        testKnownDefaultFactoriesList(ConfigFileReaderSupport.DEFAULT_HOST_KEY_ALGORITHMS, BuiltinSignatures::fromFactoryName);
     }
 
     @Test
     public void testParseKexFactoriesList() {
         List<? extends NamedResource> expected = BaseBuilder.DEFAULT_KEX_PREFERENCE;
-        Properties props = initNamedResourceProperties(SshConfigFileReader.KEX_ALGORITHMS_CONFIG_PROP, expected);
+        Properties props = initNamedResourceProperties(ConfigFileReaderSupport.KEX_ALGORITHMS_CONFIG_PROP, expected);
         BuiltinDHFactories.ParseResult result = SshConfigFileReader.getKexFactories(PropertyResolverUtils.toPropertyResolver(props));
         testParsedFactoriesList(expected, result.getParsedFactories(), result.getUnsupportedFactories());
     }
 
     @Test
     public void testKnownDefaultKexFactoriesList() {
-        testKnownDefaultFactoriesList(SshConfigFileReader.DEFAULT_KEX_ALGORITHMS, BuiltinDHFactories::fromFactoryName);
+        testKnownDefaultFactoriesList(ConfigFileReaderSupport.DEFAULT_KEX_ALGORITHMS, BuiltinDHFactories::fromFactoryName);
     }
 
     private static void testKnownDefaultFactoriesList(String factories, Function<? super String, ? extends NamedResource> resolver) {
@@ -159,7 +159,7 @@ public class SshConfigFileReaderTest extends BaseTestSupport {
     public void testGetCompression() {
         Properties props = new Properties();
         for (CompressionConfigValue expected : CompressionConfigValue.VALUES) {
-            props.setProperty(SshConfigFileReader.COMPRESSION_PROP, expected.name().toLowerCase());
+            props.setProperty(ConfigFileReaderSupport.COMPRESSION_PROP, expected.name().toLowerCase());
 
             NamedResource actual = SshConfigFileReader.getCompression(PropertyResolverUtils.toPropertyResolver(props));
             assertNotNull("No match for " + expected.name(), actual);
@@ -177,7 +177,8 @@ public class SshConfigFileReaderTest extends BaseTestSupport {
             }
         };
         // must be lenient since we do not cover the full default spectrum
-        AbstractFactoryManager actual = SshConfigFileReader.configure(expected, PropertyResolverUtils.toPropertyResolver(props), true, true);
+        AbstractFactoryManager actual = SshConfigFileReader.configure(
+            expected, PropertyResolverUtils.toPropertyResolver(props), true, true);
         assertSame("Mismatched configured result", expected, actual);
         validateAbstractFactoryManagerConfiguration(expected, props, true);
     }
@@ -237,7 +238,7 @@ public class SshConfigFileReaderTest extends BaseTestSupport {
     public void testInvalidDelimiter() throws IOException {
         String line = getClass().getSimpleName() + "+" + getCurrentTestName();
         try (Reader rdr = new StringReader(line)) {
-            Properties props = SshConfigFileReader.readConfigFile(rdr, true);
+            Properties props = ConfigFileReaderSupport.readConfigFile(rdr, true);
             fail("Unexpected success: " + props);
         }
     }
@@ -248,7 +249,7 @@ public class SshConfigFileReaderTest extends BaseTestSupport {
         String expected = getCurrentTestName();
         Properties props;
         try (Reader rdr = new StringReader(name + "\t" + expected)) {
-            props = SshConfigFileReader.readConfigFile(rdr, true);
+            props = ConfigFileReaderSupport.readConfigFile(rdr, true);
         }
 
         String actual = props.getProperty(name);
@@ -285,7 +286,8 @@ public class SshConfigFileReaderTest extends BaseTestSupport {
     }
 
     private static <M extends FactoryManager> M validateFactoryManagerCiphers(M manager, Properties props) {
-        return validateFactoryManagerCiphers(manager, props.getProperty(SshConfigFileReader.CIPHERS_CONFIG_PROP, SshConfigFileReader.DEFAULT_CIPHERS));
+        return validateFactoryManagerCiphers(manager,
+            props.getProperty(ConfigFileReaderSupport.CIPHERS_CONFIG_PROP, ConfigFileReaderSupport.DEFAULT_CIPHERS));
     }
 
     private static <M extends FactoryManager> M validateFactoryManagerCiphers(M manager, String value) {
@@ -295,7 +297,8 @@ public class SshConfigFileReaderTest extends BaseTestSupport {
     }
 
     private static <M extends FactoryManager> M validateFactoryManagerSignatures(M manager, Properties props) {
-        return validateFactoryManagerSignatures(manager, props.getProperty(SshConfigFileReader.HOST_KEY_ALGORITHMS_CONFIG_PROP, SshConfigFileReader.DEFAULT_HOST_KEY_ALGORITHMS));
+        return validateFactoryManagerSignatures(manager,
+            props.getProperty(ConfigFileReaderSupport.HOST_KEY_ALGORITHMS_CONFIG_PROP, ConfigFileReaderSupport.DEFAULT_HOST_KEY_ALGORITHMS));
     }
 
     private static <M extends FactoryManager> M validateFactoryManagerSignatures(M manager, String value) {
@@ -305,7 +308,8 @@ public class SshConfigFileReaderTest extends BaseTestSupport {
     }
 
     private static <M extends FactoryManager> M validateFactoryManagerMacs(M manager, Properties props) {
-        return validateFactoryManagerMacs(manager, props.getProperty(SshConfigFileReader.MACS_CONFIG_PROP, SshConfigFileReader.DEFAULT_MACS));
+        return validateFactoryManagerMacs(manager,
+            props.getProperty(ConfigFileReaderSupport.MACS_CONFIG_PROP, ConfigFileReaderSupport.DEFAULT_MACS));
     }
 
     private static <M extends FactoryManager> M validateFactoryManagerMacs(M manager, String value) {
@@ -315,7 +319,8 @@ public class SshConfigFileReaderTest extends BaseTestSupport {
     }
 
     private static <M extends FactoryManager> M validateFactoryManagerCompressions(M manager, Properties props, boolean lenient) {
-        return validateFactoryManagerCompressions(manager, props.getProperty(SshConfigFileReader.COMPRESSION_PROP, SshConfigFileReader.DEFAULT_COMPRESSION), lenient);
+        return validateFactoryManagerCompressions(manager,
+            props.getProperty(ConfigFileReaderSupport.COMPRESSION_PROP, ConfigFileReaderSupport.DEFAULT_COMPRESSION), lenient);
     }
 
     private static <M extends FactoryManager> M validateFactoryManagerCompressions(M manager, String value, boolean lenient) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/config/TimeValueConfigTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/config/TimeValueConfigTest.java b/sshd-core/src/test/java/org/apache/sshd/common/config/TimeValueConfigTest.java
deleted file mode 100644
index e9acf4d..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/config/TimeValueConfigTest.java
+++ /dev/null
@@ -1,57 +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.config;
-
-import java.util.concurrent.TimeUnit;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class TimeValueConfigTest extends BaseTestSupport {
-    public TimeValueConfigTest() {
-        super();
-    }
-
-    @Test
-    public void testDurationOf() {
-        Object[] values = {
-            "600", TimeUnit.SECONDS.toMillis(600L),
-            "10m", TimeUnit.MINUTES.toMillis(10L),
-            "1h30m", TimeUnit.MINUTES.toMillis(90L),
-            "2d", TimeUnit.DAYS.toMillis(2L),
-            "3w", TimeUnit.DAYS.toMillis(3L * 7L)
-        };
-        for (int index = 0; index < values.length; index += 2) {
-            String s = (String) values[index];
-            Number expected = (Number) values[index + 1];
-            long actual = TimeValueConfig.durationOf(s);
-            assertEquals(s, expected.longValue(), actual);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntryLoginOptionsParseTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntryLoginOptionsParseTest.java b/sshd-core/src/test/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntryLoginOptionsParseTest.java
deleted file mode 100644
index 5588622..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntryLoginOptionsParseTest.java
+++ /dev/null
@@ -1,124 +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.config.keys;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.MethodSorters;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-import org.junit.runners.Parameterized.UseParametersRunnerFactory;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@RunWith(Parameterized.class)   // see https://github.com/junit-team/junit/wiki/Parameterized-tests
-@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
-public class AuthorizedKeyEntryLoginOptionsParseTest extends BaseTestSupport {
-    private final String value;
-    private final String loginPart;
-    private final String keyPart;
-    private final Map<String, String> options;
-
-    public AuthorizedKeyEntryLoginOptionsParseTest(String value, String loginPart, String keyPart, Map<String, String> options) {
-        this.value = value;
-        this.loginPart = loginPart;
-        this.keyPart = keyPart;
-        this.options = options;
-    }
-
-    @Parameters(name = "{0}")
-    public static List<Object[]> parameters() {
-        List<Object[]> params = new ArrayList<>();
-        addData(params, "ssh-rsa AAAAB2...19Q==", "john@example.net", "from=\"*.sales.example.net,!pc.sales.example.net\"");
-        addData(params, "ssh-dss AAAAC3...51R==", "example.net", "command=\"dump /home\"", "no-pty", "no-port-forwarding");
-        addData(params, "ssh-dss AAAAB5...21S==", "", "permitopen=\"192.0.2.1:80\"", "permitopen=\"192.0.2.2:25\"");
-        addData(params, "ssh-rsa AAAA...==", "jane@example.net", "tunnel=\"0\"", "command=\"sh /etc/netstart tun0\"");
-        addData(params, "ssh-rsa AAAA1C8...32Tv==", "user@example.net", "!restrict", "command=\"uptime\"");
-        addData(params, "ssh-rsa AAAA1f8...IrrC5==", "user@example.net", "restrict", "!pty", "command=\"nethack\"");
-        return params;
-    }
-
-    private static void addData(List<Object[]> params, String keyData, String comment, String... comps) {
-        StringBuilder sb = new StringBuilder();
-
-        Map<String, String> optionsMap = new HashMap<>();
-        for (String c : comps) {
-            if (sb.length() > 0) {
-                sb.append(',');
-            }
-            sb.append(c);
-
-            int pos = c.indexOf('=');
-            if (pos > 0) {
-                String name = c.substring(0, pos);
-                String value = GenericUtils.stripQuotes(c.substring(pos + 1)).toString();
-                String prev = optionsMap.put(name, value);
-                if (prev != null) {
-                    optionsMap.put(name, prev + "," + value);
-                }
-            } else {
-                optionsMap.put(c, Boolean.toString(c.charAt(0) != AuthorizedKeyEntry.BOOLEAN_OPTION_NEGATION_INDICATOR));
-            }
-        }
-
-        int pos = sb.length();
-
-        sb.append(' ').append(keyData);
-        if (GenericUtils.isNotEmpty(comment)) {
-            sb.append(' ').append(comment);
-        }
-
-        String value = sb.toString();
-        params.add(new Object[] {value, value.substring(0, pos), value.substring(pos + 1), optionsMap});
-    }
-
-    @Test
-    public void testResolveEntryComponents() {
-        Map.Entry<String, String> actual = AuthorizedKeyEntry.resolveEntryComponents(value);
-        assertNotNull(value, actual);
-        assertEquals("login(" + value + ")", loginPart, actual.getKey());
-        assertEquals("remainder(" + value + ")", keyPart, actual.getValue());
-    }
-
-    @Test
-    public void testParseLoginOptions() {
-        Map<String, String> parsed = AuthorizedKeyEntry.parseLoginOptions(loginPart);
-        options.forEach((key, expected) -> {
-            String actual = parsed.get(key);
-            assertEquals(key, expected, actual);
-        });
-        assertEquals("Mismatched size", options.size(), parsed.size());
-    }
-
-    @Override
-    public String toString() {
-        return getClass().getSimpleName() + "[" + value + "]";
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntryTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntryTest.java b/sshd-core/src/test/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntryTest.java
index d28e3df..2b0dec4 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntryTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntryTest.java
@@ -123,8 +123,10 @@ public class AuthorizedKeyEntryTest extends AuthorizedKeysTestSupport {
     }
 
     private PublickeyAuthenticator testAuthorizedKeysAuth(Collection<AuthorizedKeyEntry> entries) throws Exception {
-        Collection<PublicKey> keySet = AuthorizedKeyEntry.resolveAuthorizedKeys(PublicKeyEntryResolver.FAILING, entries);
-        PublickeyAuthenticator auth = AuthorizedKeyEntry.fromAuthorizedEntries(PublicKeyEntryResolver.FAILING, entries);
+        Collection<PublicKey> keySet =
+            AuthorizedKeyEntry.resolveAuthorizedKeys(PublicKeyEntryResolver.FAILING, entries);
+        PublickeyAuthenticator auth =
+            PublickeyAuthenticator.fromAuthorizedEntries(PublicKeyEntryResolver.FAILING, entries);
         for (PublicKey key : keySet) {
             assertTrue("Failed to authenticate with key=" + key.getAlgorithm(), auth.authenticate(getCurrentTestName(), key, null));
         }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/config/keys/BuiltinIdentitiesTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/BuiltinIdentitiesTest.java b/sshd-core/src/test/java/org/apache/sshd/common/config/keys/BuiltinIdentitiesTest.java
deleted file mode 100644
index acc349a..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/BuiltinIdentitiesTest.java
+++ /dev/null
@@ -1,116 +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.config.keys;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.util.List;
-
-import org.apache.sshd.common.util.security.SecurityUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.Assume;
-import org.junit.BeforeClass;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runner.RunWith;
-import org.junit.runners.MethodSorters;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-import org.junit.runners.Parameterized.UseParametersRunnerFactory;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@RunWith(Parameterized.class)   // see https://github.com/junit-team/junit/wiki/Parameterized-tests
-@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
-@Category({ NoIoTestCase.class })
-public class BuiltinIdentitiesTest extends BaseTestSupport {
-    private final BuiltinIdentities expected;
-
-    public BuiltinIdentitiesTest(BuiltinIdentities expected) {
-        this.expected = expected;
-    }
-
-    @Parameters(name = "{0}")
-    public static List<Object[]> parameters() {
-        return parameterize(BuiltinIdentities.VALUES);
-    }
-
-    @BeforeClass    // Dirty hack around the parameterized run
-    public static void testAllConstantsCovered() throws Exception {
-        Field[] fields = BuiltinIdentities.Constants.class.getFields();
-        for (Field f : fields) {
-            int mods = f.getModifiers();
-            if (!Modifier.isStatic(mods)) {
-                continue;
-            }
-
-            if (!Modifier.isFinal(mods)) {
-                continue;
-            }
-
-            Class<?> type = f.getType();
-            if (!String.class.isAssignableFrom(type)) {
-                continue;
-            }
-
-            String name = f.getName();
-            String value = (String) f.get(null);
-            BuiltinIdentities id = BuiltinIdentities.fromName(value);
-            assertNotNull("No match found for field " + name + "=" + value, id);
-        }
-    }
-
-    @Test
-    public void testFromName() {
-        String name = expected.getName();
-        for (int index = 0, count = name.length(); index < count; index++) {
-            assertSame(name, expected, BuiltinIdentities.fromName(name));
-            name = shuffleCase(name);
-        }
-    }
-
-    @Test
-    public void testFromAlgorithm() {
-        String algorithm = expected.getAlgorithm();
-        for (int index = 0, count = algorithm.length(); index < count; index++) {
-            assertSame(algorithm, expected, BuiltinIdentities.fromAlgorithm(algorithm));
-            algorithm = shuffleCase(algorithm);
-        }
-    }
-
-    @Test
-    public void testFromKey() throws GeneralSecurityException {
-        Assume.assumeTrue("Unsupported built-in identity", expected.isSupported());
-        KeyPairGenerator gen = SecurityUtils.getKeyPairGenerator(expected.getAlgorithm());
-        KeyPair kp = gen.generateKeyPair();
-        outputDebugMessage("Checking built-in identity: %s", expected);
-        assertSame(expected + "[pair]", expected, BuiltinIdentities.fromKeyPair(kp));
-        assertSame(expected + "[public]", expected, BuiltinIdentities.fromKey(kp.getPublic()));
-        assertSame(expected + "[private]", expected, BuiltinIdentities.fromKey(kp.getPrivate()));
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/config/keys/KeyRandomArtTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/KeyRandomArtTest.java b/sshd-core/src/test/java/org/apache/sshd/common/config/keys/KeyRandomArtTest.java
deleted file mode 100644
index 4c74ef5..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/KeyRandomArtTest.java
+++ /dev/null
@@ -1,117 +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.config.keys;
-
-import java.security.KeyPair;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.LinkedList;
-import java.util.List;
-
-import org.apache.sshd.common.cipher.ECCurves;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.apache.sshd.util.test.Utils;
-import org.junit.AfterClass;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runner.RunWith;
-import org.junit.runners.MethodSorters;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-import org.junit.runners.Parameterized.UseParametersRunnerFactory;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@RunWith(Parameterized.class)   // see https://github.com/junit-team/junit/wiki/Parameterized-tests
-@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
-@Category({ NoIoTestCase.class })
-public class KeyRandomArtTest extends BaseTestSupport {
-    private static final Collection<KeyPair> KEYS = new LinkedList<>();
-
-    private final String algorithm;
-    private final int keySize;
-    private final KeyPair keyPair;
-
-    public KeyRandomArtTest(String algorithm, int keySize) throws Exception {
-        this.algorithm = algorithm;
-        this.keySize = keySize;
-        this.keyPair = Utils.generateKeyPair(algorithm, keySize);
-        KEYS.add(this.keyPair);
-    }
-
-    @Parameters(name = "algorithm={0}, key-size={1}")
-    public static List<Object[]> parameters() {
-        List<Object[]> params = new ArrayList<>();
-        for (int keySize : RSA_SIZES) {
-            params.add(new Object[]{KeyUtils.RSA_ALGORITHM, keySize});
-        }
-
-        for (int keySize : DSS_SIZES) {
-            params.add(new Object[]{KeyUtils.DSS_ALGORITHM, keySize});
-        }
-
-        if (SecurityUtils.isECCSupported()) {
-            for (ECCurves curve : ECCurves.VALUES) {
-                params.add(new Object[]{KeyUtils.EC_ALGORITHM, curve.getKeySize()});
-            }
-        }
-
-        if (SecurityUtils.isEDDSACurveSupported()) {
-            for (int keySize : ED25519_SIZES) {
-                params.add(new Object[]{SecurityUtils.EDDSA, keySize});
-            }
-        }
-        return params;
-    }
-
-    @AfterClass
-    public static void dumpAllArts() throws Exception {
-        KeyRandomArt.combine(System.out, ' ', () -> KEYS);
-    }
-
-    @Test
-    public void testRandomArtString() throws Exception {
-        KeyRandomArt art = new KeyRandomArt(keyPair.getPublic());
-        assertEquals("Mismatched algorithm", algorithm, art.getAlgorithm());
-        assertEquals("Mismatched key size", keySize, art.getKeySize());
-
-        String s = art.toString();
-        String[] lines = GenericUtils.split(s, '\n');
-        assertEquals("Mismatched lines count", KeyRandomArt.FLDSIZE_Y + 2, lines.length);
-
-        for (int index = 0; index < lines.length; index++) {
-            String l = lines[index];
-            if ((l.length() > 0) && (l.charAt(l.length() - 1) == '\r')) {
-                l = l.substring(0, l.length() - 1);
-                lines[index] = l;
-            }
-            System.out.append('\t').println(l);
-
-            assertTrue("Mismatched line length #" + (index + 1) + ": " + l.length(), l.length() >= (KeyRandomArt.FLDSIZE_X + 2));
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsCloneTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsCloneTest.java b/sshd-core/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsCloneTest.java
deleted file mode 100644
index cb23591..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsCloneTest.java
+++ /dev/null
@@ -1,120 +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.config.keys;
-
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-import org.apache.sshd.common.cipher.ECCurves;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runner.RunWith;
-import org.junit.runners.MethodSorters;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-import org.junit.runners.Parameterized.UseParametersRunnerFactory;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@RunWith(Parameterized.class)   // see https://github.com/junit-team/junit/wiki/Parameterized-tests
-@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
-@Category({ NoIoTestCase.class })
-public class KeyUtilsCloneTest extends BaseTestSupport {
-    private final String keyType;
-    private final int keySize;
-
-    public KeyUtilsCloneTest(String keyType, int keySize) {
-        this.keyType = ValidateUtils.checkNotNullAndNotEmpty(keyType, "No key type specified");
-        this.keySize = keySize;
-    }
-
-    @Parameters(name = "type={0}, size={1}")
-    public static List<Object[]> parameters() {
-        List<Object[]> list = new ArrayList<>();
-        addTests(list, KeyPairProvider.SSH_DSS, DSS_SIZES);
-        addTests(list, KeyPairProvider.SSH_RSA, RSA_SIZES);
-        if (SecurityUtils.isECCSupported()) {
-            for (ECCurves curve : ECCurves.VALUES) {
-                if (!curve.isSupported()) {
-                    continue;
-                }
-                addTests(list, curve.getKeyType(), Collections.singletonList(curve.getKeySize()));
-            }
-        }
-        if (SecurityUtils.isEDDSACurveSupported()) {
-            addTests(list, KeyPairProvider.SSH_ED25519, ED25519_SIZES);
-        }
-        return Collections.unmodifiableList(list);
-    }
-
-    private static void addTests(List<Object[]> list, String keyType, Collection<Integer> sizes) {
-        for (Integer keySize : sizes) {
-            list.add(new Object[]{keyType, keySize});
-        }
-    }
-
-    @Test
-    @SuppressWarnings("checkstyle:avoidnestedblocks")
-    public void testKeyPairCloning() throws GeneralSecurityException {
-        outputDebugMessage("generateKeyPair(%s)[%d]", keyType, keySize);
-        KeyPair original = KeyUtils.generateKeyPair(keyType, keySize);
-
-        outputDebugMessage("cloneKeyPair(%s)[%d]", keyType, keySize);
-        KeyPair cloned = KeyUtils.cloneKeyPair(keyType, original);
-
-        String prefix = keyType + "[" + keySize + "]";
-        assertNotSame(prefix + ": Key pair not cloned", original, cloned);
-        assertTrue(prefix + ": Cloned pair not equals", KeyUtils.compareKeyPairs(original, cloned));
-
-        {
-            PublicKey k1 = original.getPublic();
-            PublicKey k2 = cloned.getPublic();
-            assertNotSame(prefix + ": Public key not cloned", k1, k2);
-            assertTrue(prefix + ": Cloned public key not equals", KeyUtils.compareKeys(k1, k2));
-
-            String f1 = KeyUtils.getFingerPrint(k1);
-            String f2 = KeyUtils.getFingerPrint(k2);
-            assertEquals(prefix + ": Mismatched fingerprints", f1, f2);
-        }
-
-        {
-            PrivateKey k1 = original.getPrivate();
-            PrivateKey k2 = cloned.getPrivate();
-            assertNotSame(prefix + ": Private key not cloned", k1, k2);
-            assertTrue(prefix + ": Cloned private key not equals", KeyUtils.compareKeys(k1, k2));
-        }
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsFingerprintCaseSensitivityTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsFingerprintCaseSensitivityTest.java b/sshd-core/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsFingerprintCaseSensitivityTest.java
deleted file mode 100644
index c9457fa..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsFingerprintCaseSensitivityTest.java
+++ /dev/null
@@ -1,96 +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.config.keys;
-
-import java.io.IOException;
-import java.security.GeneralSecurityException;
-import java.security.PublicKey;
-import java.util.AbstractMap.SimpleImmutableEntry;
-import java.util.Arrays;
-import java.util.Collection;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.BeforeClass;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runner.RunWith;
-import org.junit.runners.MethodSorters;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-import org.junit.runners.Parameterized.UseParametersRunnerFactory;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@RunWith(Parameterized.class)   // see https://github.com/junit-team/junit/wiki/Parameterized-tests
-@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
-@Category({ NoIoTestCase.class })
-public class KeyUtilsFingerprintCaseSensitivityTest extends BaseTestSupport {
-
-    // CHECKSTYLE:OFF
-    private static final String KEY_STRING = "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAxr3N5fkt966xJINl0hH7Q6lLDRR1D0yMjcXCE5roE9VFut2ctGFuo90TCOxkPOMnwzwConeyScVF4ConZeWsxbG9VtRh61IeZ6R5P5ZTvE9xPdZBgIEWvU1bRfrrOfSMihqF98pODspE6NoTtND2eglwSGwxcYFmpdTAmu+8qgxgGxlEaaCjqwdiNPZhygrH81Mv2ruolNeZkn4Bj+wFFmZTD/waN1pQaMf+SO1+kEYIYFNl5+8JRGuUcr8MhHHJB+gwqMTF2BSBVITJzZUiQR0TMtkK6Vbs7yt1F9hhzDzAFDwhV+rsfNQaOHpl3zP07qH+/99A0XG1CVcEdHqVMw== lgoldstein@LGOLDSTEIN-WIN7";
-    // CHECKSTYLE:ON
-    private static final String MD5_PREFIX = "MD5:";
-    private static final String MD5 = "24:32:3c:80:01:b3:e1:fa:7c:53:ca:e3:e8:4e:c6:8e";
-    private static final String MD5_FULL = MD5_PREFIX + MD5;
-    private static final String SHA1_PREFIX = "SHA1:";
-    private static final String SHA1 = "ZNLzC6u+F37oq8BpEAwP69EQtoA";
-    private static final String SHA1_FULL = SHA1_PREFIX + SHA1;
-
-    private static PublicKey key;
-
-    private String expected;
-    private String test;
-
-    public KeyUtilsFingerprintCaseSensitivityTest(String expected, String test) {
-        this.expected = expected;
-        this.test = test;
-    }
-
-    @BeforeClass
-    public static void beforeClass() throws GeneralSecurityException, IOException {
-        key = PublicKeyEntry.parsePublicKeyEntry(KEY_STRING).resolvePublicKey(PublicKeyEntryResolver.FAILING);
-    }
-
-    @Parameters(name = "expected={0}, test={1}")
-    public static Collection<Object[]> parameters() {
-        return Arrays.asList(
-            new Object[] {MD5_FULL, MD5_FULL},
-            new Object[] {MD5_FULL, MD5_FULL.toUpperCase()},
-            new Object[] {MD5_FULL, MD5_FULL.toLowerCase()},
-            new Object[] {MD5_FULL, MD5_PREFIX.toUpperCase() + MD5},
-            new Object[] {MD5_FULL, MD5_PREFIX.toLowerCase() + MD5},
-            new Object[] {MD5_FULL, MD5.toLowerCase()},
-            new Object[] {MD5_FULL, MD5.toUpperCase()},
-            new Object[] {SHA1_FULL, SHA1_FULL},
-            new Object[] {SHA1_FULL, SHA1_PREFIX.toUpperCase() + SHA1},
-            new Object[] {SHA1_FULL, SHA1_PREFIX.toLowerCase() + SHA1}
-        );
-    }
-
-    @Test
-    public void testCase() throws Exception {
-        assertEquals("Check failed", new SimpleImmutableEntry<>(true, expected), KeyUtils.checkFingerPrint(test, key));
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsFingerprintGenerationTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsFingerprintGenerationTest.java b/sshd-core/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsFingerprintGenerationTest.java
deleted file mode 100644
index e78eaeb..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsFingerprintGenerationTest.java
+++ /dev/null
@@ -1,159 +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.config.keys;
-
-import java.io.IOException;
-import java.security.GeneralSecurityException;
-import java.security.PublicKey;
-import java.security.spec.InvalidKeySpecException;
-import java.util.AbstractMap.SimpleImmutableEntry;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.sshd.common.digest.BuiltinDigests;
-import org.apache.sshd.common.digest.DigestFactory;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runner.RunWith;
-import org.junit.runners.MethodSorters;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-import org.junit.runners.Parameterized.UseParametersRunnerFactory;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@RunWith(Parameterized.class)   // see https://github.com/junit-team/junit/wiki/Parameterized-tests
-@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
-@Category({ NoIoTestCase.class })
-public class KeyUtilsFingerprintGenerationTest extends BaseTestSupport {
-    private final PublicKey key;
-    private final DigestFactory digestFactory;
-    private final String expected;
-
-    public KeyUtilsFingerprintGenerationTest(PublicKey key, DigestFactory digestFactory, String expected) {
-        this.key = key;
-        this.digestFactory = digestFactory;
-        this.expected = expected;
-    }
-
-    @Parameters(name = "key={0}, digestFactory={1}, expected={2}")
-    public static Collection<Object[]> parameters() throws IOException, GeneralSecurityException {
-        List<? extends Map.Entry<String, List<? extends Map.Entry<DigestFactory, String>>>> keyEntries =
-            Collections.unmodifiableList(Arrays.asList(
-                new SimpleImmutableEntry<>(
-                    // CHECKSTYLE:OFF
-                    "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAxr3N5fkt966xJINl0hH7Q6lLDRR1D0yMjcXCE5roE9VFut2ctGFuo90TCOxkPOMnwzwConeyScVF4ConZeWsxbG9VtRh61IeZ6R5P5ZTvE9xPdZBgIEWvU1bRfrrOfSMihqF98pODspE6NoTtND2eglwSGwxcYFmpdTAmu+8qgxgGxlEaaCjqwdiNPZhygrH81Mv2ruolNeZkn4Bj+wFFmZTD/waN1pQaMf+SO1+kEYIYFNl5+8JRGuUcr8MhHHJB+gwqMTF2BSBVITJzZUiQR0TMtkK6Vbs7yt1F9hhzDzAFDwhV+rsfNQaOHpl3zP07qH+/99A0XG1CVcEdHqVMw== lgoldstein@LGOLDSTEIN-WIN7",
-                    // CHECKSTYLE:ON
-                    Arrays.asList(
-                        new SimpleImmutableEntry<>(
-                            BuiltinDigests.md5,
-                            "MD5:24:32:3c:80:01:b3:e1:fa:7c:53:ca:e3:e8:4e:c6:8e"
-                        ),
-                        new SimpleImmutableEntry<>(
-                            BuiltinDigests.sha256,
-                            "SHA256:1wNOZO+/XgNGJMx8UUJst33V+bBMTz5EcL0B6y2iRv0"
-                        )
-                    )
-                ),
-                new SimpleImmutableEntry<>(
-                    // CHECKSTYLE:OFF
-                    "ssh-dss AAAAB3NzaC1kc3MAAACBAMg/IxsG5BxnF5gM7IKqqR0rftxZC+n5GlbO+J4H+iIb/KR8NBehkxG3CrBZMF96M2K1sEGYLob+3k4r71oWaPul8n5rt9kpd+JSq4iD2ygOyg6Kd1/YDBHoxneizy6I/bGsLwhAAKWcRNrXmYVKGzhrhvZWN12AJDq2mGdj3szLAAAAFQD7a2MltdUSF7FU3//SpW4WGjZbeQAAAIBf0nNsfKQL/TEMo7IpTrEMg5V0RnSigCX0+yUERS42GW/ZeCZBJw7oL2XZbuBtu63vMjDgVpnb92BdrcPgjJ7EFW6DlcyeuywStmg1ygXmDR2AQCxv0eX2CQgrdUczmRa155SDVUTvTQlO1IyKx0vwKAh1H7E3yJUfkTAJstbGYQAAAIEAtv+cdRfNevYFkp55jVqazc8zRLvfb64jzgc5oSJVc64kFs4yx+abYpGX9WxNxDlG6g2WiY8voDBB0YnUJsn0kVRjBKX9OceROxrfT4K4dVbQZsdt+SLaXWL4lGJFrFZL3LZqvySvq6xfhJfakQDDivW4hUOhFPXPHrE5/Ia3T7A= dsa-key-20130709",
-                    // CHECKSTYLE:ON
-                    Arrays.asList(
-                        new SimpleImmutableEntry<>(
-                            BuiltinDigests.md5,
-                            "MD5:fb:29:14:8d:94:f9:1d:cf:6b:0e:a4:35:1d:83:44:2f"
-                        ),
-                        new SimpleImmutableEntry<>(
-                            BuiltinDigests.sha256,
-                            "SHA256:grxw4KhY1cK6eOczBWs7tDVvo9V0PQw4E1wN1gJvHlw"
-                        )
-                    )
-                ),
-                new SimpleImmutableEntry<>(
-                    // CHECKSTYLE:OFF
-                    "ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBFImZtcTj842stlcVHLFBFxTEx7lu3jW9aZCvd0r9fUNKZ6LbRPh6l1oJ4ozArnw7XreQBUc5oNd9HB5RNJ8jl1nWXY5cXBA7McZrKZrYmk+zxNhH6UL+kMLaJkyngJHQw== root@osv-linux",
-                    // CHECKSTYLE:ON
-                    Arrays.asList(
-                        new SimpleImmutableEntry<>(
-                            BuiltinDigests.md5,
-                            "MD5:e6:dc:a2:4f:5b:11:b2:3c:0f:e8:f6:d8:d1:01:e9:d3"
-                        ),
-                        new SimpleImmutableEntry<>(
-                            BuiltinDigests.sha512,
-                            "SHA512:4w6ZB78tmFWhpN2J50Ok6WeMJhZp1X0xN0EKWxZmRLcYDbCWhyJDe8lgrQKWqdTCMZ5aNEBl9xQUklcC5Gt2jg"
-                        )
-                    )
-                )
-            ));
-
-        List<Object[]> ret = new ArrayList<>();
-        for (Map.Entry<String, ? extends Collection<? extends Map.Entry<DigestFactory, String>>> kentry : keyEntries) {
-            String keyValue = kentry.getKey();
-            try {
-                PublicKey key = PublicKeyEntry.parsePublicKeyEntry(keyValue).resolvePublicKey(PublicKeyEntryResolver.FAILING);
-                for (Map.Entry<DigestFactory, String> dentry : kentry.getValue()) {
-                    DigestFactory factory = dentry.getKey();
-                    String fingerprint = dentry.getValue();
-                    if (!factory.isSupported()) {
-                        System.out.println("Skip unsupported digest: " + fingerprint);
-                        continue;
-                    }
-
-                    ret.add(new Object[]{key, factory, fingerprint});
-                }
-            } catch (InvalidKeySpecException e) {
-                System.out.println("Skip unsupported key: " + keyValue);
-            }
-        }
-
-        return ret;
-    }
-
-    @Test
-    public void testFingerprint() throws Exception {
-        String name = digestFactory.getName();
-        assertEquals(
-            String.format("Fingerprint does not match for digest %s", name),
-            expected,
-            KeyUtils.getFingerPrint(digestFactory, key)
-        );
-        assertEquals(
-            String.format("Fingerprint check failed for digest %s", name),
-            new SimpleImmutableEntry<>(true, expected),
-            KeyUtils.checkFingerPrint(expected, digestFactory, key)
-        );
-        assertEquals(
-            String.format("Fingerprint check succeeded for invalid digest %s", name),
-            new SimpleImmutableEntry<>(false, expected),
-            KeyUtils.checkFingerPrint(expected + "A", digestFactory, key)
-        );
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsTest.java b/sshd-core/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsTest.java
deleted file mode 100644
index 0f7e07c..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsTest.java
+++ /dev/null
@@ -1,157 +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.config.keys;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.attribute.PosixFilePermission;
-import java.security.DigestException;
-import java.util.Collection;
-import java.util.Date;
-import java.util.Map;
-
-import org.apache.sshd.common.digest.BaseDigest;
-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.OsUtils;
-import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class KeyUtilsTest extends BaseTestSupport {
-    public KeyUtilsTest() {
-        super();
-    }
-
-    @Test
-    public void testGenerateFingerPrintOnException() {
-        for (DigestFactory info : BuiltinDigests.VALUES) {
-            if (!info.isSupported()) {
-                System.out.println("Skip unsupported digest: " + info.getAlgorithm());
-                continue;
-            }
-
-            Exception thrown = new DigestException(info.getAlgorithm() + ":" + info.getBlockSize());
-            Digest digest = new BaseDigest(info.getAlgorithm(), info.getBlockSize()) {
-                @Override
-                public byte[] digest() throws Exception {
-                    throw thrown;
-                }
-            };
-            String actual = KeyUtils.getFingerPrint(new DigestFactory() {
-                @Override
-                public String getName() {
-                    return getCurrentTestName();
-                }
-
-                @Override
-                public String getAlgorithm() {
-                    return digest.getAlgorithm();
-                }
-
-                @Override
-                public boolean isSupported() {
-                    return info.isSupported();
-                }
-
-                @Override
-                public int getBlockSize() {
-                    return info.getBlockSize();
-                }
-
-                @Override
-                public Digest create() {
-                    return digest;
-                }
-            }, getCurrentTestName());
-            String expected = thrown.getClass().getSimpleName();
-            assertEquals("Mismatched fingerprint for " + thrown.getMessage(), expected, actual);
-        }
-    }
-
-    @Test
-    public void testGenerateDefaultFingerprintDigest() {
-        DigestFactory defaultValue = KeyUtils.getDefaultFingerPrintFactory();
-        assertNotNull("No current default fingerprint digest factory", defaultValue);
-        try {
-            for (DigestFactory f : BuiltinDigests.VALUES) {
-                if (!f.isSupported()) {
-                    System.out.println("Skip unsupported digest=" + f.getAlgorithm());
-                    continue;
-                }
-
-                KeyUtils.setDefaultFingerPrintFactory(f);
-
-                String data = getClass().getName() + "#" + getCurrentTestName() + "(" + f.getName() + ")";
-                String expected = KeyUtils.getFingerPrint(f, data);
-                String actual = KeyUtils.getFingerPrint(data);
-                assertEquals("Mismatched fingerprint for digest=" + f.getName(), expected, actual);
-            }
-        } finally {
-            KeyUtils.setDefaultFingerPrintFactory(defaultValue); // restore the original
-        }
-    }
-
-    @Test   // see SSHD-606
-    public void testValidateStrictKeyFilePermissions() throws IOException {
-        Path file = getTempTargetRelativeFile(getClass().getSimpleName(), getCurrentTestName());
-        outputDebugMessage("%s deletion result=%s", file, Files.deleteIfExists(file));
-        assertNull("Unexpected violation for non-existent file: " + file, KeyUtils.validateStrictKeyFilePermissions(file));
-
-        assertHierarchyTargetFolderExists(file.getParent());
-        try (OutputStream output = Files.newOutputStream(file)) {
-            output.write((getClass().getName() + "#" + getCurrentTestName() + "@" + new Date(System.currentTimeMillis())).getBytes(StandardCharsets.UTF_8));
-        }
-
-        Collection<PosixFilePermission> perms = IoUtils.getPermissions(file);
-        if (GenericUtils.isEmpty(perms)) {
-            assertNull("Unexpected violation for no permissions file: " + file, KeyUtils.validateStrictKeyFilePermissions(file));
-        } else if (OsUtils.isUNIX()) {
-            Map.Entry<String, Object> violation = null;
-            for (PosixFilePermission p : KeyUtils.STRICTLY_PROHIBITED_FILE_PERMISSION) {
-                if (perms.contains(p)) {
-                    violation = KeyUtils.validateStrictKeyFilePermissions(file);
-                    assertNotNull("Unexpected success for permission=" + p + " of file " + file + " permissions=" + perms, violation);
-                    break;
-                }
-            }
-
-            if (violation == null) {    // we expect a failure since the parent does not have the necessary permissions
-                assertNotNull("Unexpected UNIX success for file " + file + " permissions=" + perms, KeyUtils.validateStrictKeyFilePermissions(file));
-            }
-        } else {
-            assertNull("Unexpected Windows violation for file " + file + " permissions=" + perms, KeyUtils.validateStrictKeyFilePermissions(file));
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/config/keys/PublicKeyEntryTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/PublicKeyEntryTest.java b/sshd-core/src/test/java/org/apache/sshd/common/config/keys/PublicKeyEntryTest.java
deleted file mode 100644
index eee2d72..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/PublicKeyEntryTest.java
+++ /dev/null
@@ -1,62 +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.config.keys;
-
-import java.security.GeneralSecurityException;
-import java.security.PublicKey;
-import java.security.spec.InvalidKeySpecException;
-import java.util.Arrays;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class PublicKeyEntryTest extends BaseTestSupport {
-    public PublicKeyEntryTest() {
-        super();
-    }
-
-    @Test
-    public void testFallbackResolver() throws Exception {
-        PublicKeyEntry entry =  // TODO use some
-                PublicKeyEntry.parsePublicKeyEntry(
-                        GenericUtils.join(
-                                Arrays.asList(getCurrentTestName(), "AAAA", getClass().getSimpleName()), ' '));
-        for (PublicKeyEntryResolver resolver : new PublicKeyEntryResolver[]{
-            null, PublicKeyEntryResolver.FAILING, PublicKeyEntryResolver.IGNORING}) {
-            try {
-                PublicKey key = entry.resolvePublicKey(resolver);
-                assertSame("Mismatched successful resolver", PublicKeyEntryResolver.IGNORING, resolver);
-                assertNull("Unexpected success for resolver=" + resolver + ": " + KeyUtils.getFingerPrint(key), key);
-            } catch (GeneralSecurityException e) {
-                assertObjectInstanceOf("Mismatched thrown exception for resolver=" + resolver, InvalidKeySpecException.class, e);
-            }
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/config/keys/loader/AESPrivateKeyObfuscatorTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/loader/AESPrivateKeyObfuscatorTest.java b/sshd-core/src/test/java/org/apache/sshd/common/config/keys/loader/AESPrivateKeyObfuscatorTest.java
deleted file mode 100644
index cc3c379..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/loader/AESPrivateKeyObfuscatorTest.java
+++ /dev/null
@@ -1,79 +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.config.keys.loader;
-
-import java.security.GeneralSecurityException;
-import java.security.Key;
-import java.util.List;
-import java.util.Random;
-
-import javax.crypto.Cipher;
-import javax.crypto.spec.SecretKeySpec;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runner.RunWith;
-import org.junit.runners.MethodSorters;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-import org.junit.runners.Parameterized.UseParametersRunnerFactory;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@RunWith(Parameterized.class)   // see https://github.com/junit-team/junit/wiki/Parameterized-tests
-@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
-@Category({ NoIoTestCase.class })
-public class AESPrivateKeyObfuscatorTest extends BaseTestSupport {
-    private static final Random RANDOMIZER = new Random(System.currentTimeMillis());
-
-    private final int keyLength;
-
-    public AESPrivateKeyObfuscatorTest(int keyLength) {
-        this.keyLength = keyLength;
-    }
-
-    @Parameters(name = "keyLength={0}")
-    public static List<Object[]> parameters() {
-        List<Integer> lengths = AESPrivateKeyObfuscator.getAvailableKeyLengths();
-        assertFalse("No lengths available", GenericUtils.isEmpty(lengths));
-        return parameterize(lengths);
-    }
-
-    @Test
-    public void testAvailableKeyLengthExists() throws GeneralSecurityException {
-        assertEquals("Not a BYTE size multiple", 0, keyLength % Byte.SIZE);
-
-        byte[] iv = new byte[keyLength / Byte.SIZE];
-        synchronized (RANDOMIZER) {
-            RANDOMIZER.nextBytes(iv);
-        }
-
-        Key key = new SecretKeySpec(iv, AESPrivateKeyObfuscator.CIPHER_NAME);
-        Cipher c = SecurityUtils.getCipher(AESPrivateKeyObfuscator.CIPHER_NAME);
-        c.init(Cipher.DECRYPT_MODE, key);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest.java b/sshd-core/src/test/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest.java
deleted file mode 100644
index 482037d..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest.java
+++ /dev/null
@@ -1,108 +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.config.keys.loader.openssh;
-
-import java.net.URL;
-import java.security.KeyPair;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.util.Collection;
-import java.util.List;
-
-import org.apache.sshd.common.config.keys.AuthorizedKeyEntry;
-import org.apache.sshd.common.config.keys.BuiltinIdentities;
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.config.keys.PrivateKeyEntryDecoder;
-import org.apache.sshd.common.config.keys.PublicKeyEntryResolver;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.Assume;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runner.RunWith;
-import org.junit.runners.MethodSorters;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-import org.junit.runners.Parameterized.UseParametersRunnerFactory;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@RunWith(Parameterized.class)   // see https://github.com/junit-team/junit/wiki/Parameterized-tests
-@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
-@Category({ NoIoTestCase.class })
-public class OpenSSHKeyPairResourceParserTest extends BaseTestSupport {
-    private static final OpenSSHKeyPairResourceParser PARSER = OpenSSHKeyPairResourceParser.INSTANCE;
-    private final BuiltinIdentities identity;
-
-    public OpenSSHKeyPairResourceParserTest(BuiltinIdentities identity) {
-        this.identity = identity;
-    }
-
-    @Parameters(name = "type={0}")
-    public static List<Object[]> parameters() {
-        return parameterize(BuiltinIdentities.VALUES);
-    }
-
-    @Test
-    public void testLoadKeyPairs() throws Exception {
-        Assume.assumeTrue(identity + " not supported", identity.isSupported());
-
-        String resourceKey = getClass().getSimpleName() + "-" + identity.getName().toUpperCase() + "-" + KeyPair.class.getSimpleName();
-        URL urlKeyPair = getClass().getResource(resourceKey);
-        assertNotNull("Missing key-pair resource: " + resourceKey, urlKeyPair);
-
-        Collection<KeyPair> pairs = PARSER.loadKeyPairs(urlKeyPair, FilePasswordProvider.EMPTY);
-        assertEquals("Mismatched pairs count", 1, GenericUtils.size(pairs));
-
-        URL urlPubKey = getClass().getResource(resourceKey + ".pub");
-        assertNotNull("Missing public key resource: " + resourceKey, urlPubKey);
-
-        List<AuthorizedKeyEntry> entries = AuthorizedKeyEntry.readAuthorizedKeys(urlPubKey);
-        assertEquals("Mismatched public keys count", 1, GenericUtils.size(entries));
-
-        AuthorizedKeyEntry entry = entries.get(0);
-        PublicKey pubEntry = entry.resolvePublicKey(PublicKeyEntryResolver.FAILING);
-        assertNotNull("Cannot retrieve public key", pubEntry);
-
-        Class<? extends PublicKey> pubType = identity.getPublicKeyType();
-        Class<? extends PrivateKey> prvType = identity.getPrivateKeyType();
-        for (KeyPair kp : pairs) {
-            PublicKey pubKey = ValidateUtils.checkInstanceOf(kp.getPublic(), pubType, "Mismatched public key type");
-            assertKeyEquals("Mismatched identity public key", pubEntry, pubKey);
-
-            PrivateKey prvKey = ValidateUtils.checkInstanceOf(kp.getPrivate(), prvType, "Mismatched private key type");
-            @SuppressWarnings("rawtypes")
-            PrivateKeyEntryDecoder decoder =
-                OpenSSHKeyPairResourceParser.getPrivateKeyEntryDecoder(prvKey);
-            assertNotNull("No private key decoder", decoder);
-
-            if (decoder.isPublicKeyRecoverySupported()) {
-                @SuppressWarnings("unchecked")
-                PublicKey recKey = decoder.recoverPublicKey(prvKey);
-                assertKeyEquals("Mismatched recovered public key", pubKey, recKey);
-            }
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParserTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParserTest.java b/sshd-core/src/test/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParserTest.java
deleted file mode 100644
index 69e6b66..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParserTest.java
+++ /dev/null
@@ -1,107 +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.config.keys.loader.pem;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.security.PrivateKey;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-import org.apache.commons.ssl.PEMItem;
-import org.apache.commons.ssl.PEMUtil;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runner.RunWith;
-import org.junit.runners.MethodSorters;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-import org.junit.runners.Parameterized.UseParametersRunnerFactory;
-
-/**
- * TODO Add javadoc
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@RunWith(Parameterized.class)   // see https://github.com/junit-team/junit/wiki/Parameterized-tests
-@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class PKCS8PEMResourceKeyPairParserTest extends BaseTestSupport {
-    private final String algorithm;
-    private final int keySize;
-
-    public PKCS8PEMResourceKeyPairParserTest(String algorithm, int keySize) {
-        this.algorithm = algorithm;
-        this.keySize = keySize;
-    }
-
-    @Parameters(name = "{0} / {1}")
-    public static List<Object[]> parameters() {
-        List<Object[]> params = new ArrayList<>();
-        for (Integer ks : RSA_SIZES) {
-            params.add(new Object[]{KeyUtils.RSA_ALGORITHM, ks});
-        }
-        for (Integer ks : DSS_SIZES) {
-            params.add(new Object[]{KeyUtils.DSS_ALGORITHM, ks});
-        }
-        return params;
-    }
-
-    @Test   // see SSHD-760
-    public void testPkcs8() throws IOException, GeneralSecurityException {
-        KeyPairGenerator generator = SecurityUtils.getKeyPairGenerator(algorithm);
-        if (keySize > 0) {
-            generator.initialize(keySize);
-        }
-        KeyPair kp = generator.generateKeyPair();
-
-        try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
-            Collection<Object> items = new ArrayList<>();
-            PrivateKey prv1 = kp.getPrivate();
-            items.add(new PEMItem(prv1.getEncoded(), "PRIVATE KEY"));
-            byte[] bytes = PEMUtil.encode(items);
-            os.write(bytes);
-            os.close();
-
-            try (ByteArrayInputStream bais = new ByteArrayInputStream(os.toByteArray())) {
-                KeyPair kp2 = SecurityUtils.loadKeyPairIdentity(getCurrentTestName(), bais, null);
-
-                assertEquals("Mismatched public key", kp.getPublic(), kp2.getPublic());
-                assertEquals("Mismatched private key", prv1, kp2.getPrivate());
-            }
-        }
-    }
-
-    @Override
-    public String toString() {
-        return getClass().getSimpleName() + "[" + algorithm + "/" + keySize + "]";
-    }
-}


[05/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common utilities code from sshd-core into sshd-common (new artifact)

Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/util/test/BaseTestSupport.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/util/test/BaseTestSupport.java b/sshd-core/src/test/java/org/apache/sshd/util/test/BaseTestSupport.java
index ba58c5e..589ab2f 100644
--- a/sshd-core/src/test/java/org/apache/sshd/util/test/BaseTestSupport.java
+++ b/sshd-core/src/test/java/org/apache/sshd/util/test/BaseTestSupport.java
@@ -18,76 +18,23 @@
  */
 package org.apache.sshd.util.test;
 
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.nio.file.Files;
-import java.nio.file.LinkOption;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.security.Key;
-import java.security.KeyPair;
-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.RSAPrivateKey;
-import java.security.interfaces.RSAPublicKey;
-import java.security.spec.ECField;
-import java.security.spec.ECParameterSpec;
-import java.security.spec.ECPoint;
-import java.security.spec.EllipticCurve;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.concurrent.TimeUnit;
-
 import org.apache.sshd.client.SshClient;
 import org.apache.sshd.common.io.DefaultIoServiceFactoryFactory;
 import org.apache.sshd.common.io.IoServiceFactoryFactory;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.io.IoUtils;
 import org.apache.sshd.common.util.net.SshdSocketAddress;
 import org.apache.sshd.server.SshServer;
-import org.junit.Assert;
 import org.junit.Rule;
-import org.junit.rules.TestName;
 import org.junit.rules.TestWatcher;
 import org.junit.runner.Description;
-import org.junit.runner.RunWith;
 
 /**
  * Helper used as base class for all test classes
  *
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
-@RunWith(JUnit4SingleInstanceClassRunner.class)
-public abstract class BaseTestSupport extends Assert {
-    public static final String TEMP_SUBFOLDER_NAME = "temp";
+public abstract class BaseTestSupport extends JUnitTestSupport {
     // can be used to override the 'localhost' with an address other than 127.0.0.1 in case it is required
     public static final String TEST_LOCALHOST = System.getProperty("org.apache.sshd.test.localhost", SshdSocketAddress.LOCALHOST_IPV4);
-    public static final boolean OUTPUT_DEBUG_MESSAGES = Boolean.parseBoolean(System.getProperty("org.apache.sshd.test.outputDebugMessages", "false"));
-    public static final String MAIN_SUBFOLDER = "main";
-    public static final String TEST_SUBFOLDER = "test";
-    public static final String RESOURCES_SUBFOLDER = "resources";
-
-    // useful test sizes for keys
-    public static final List<Integer> DSS_SIZES =
-            Collections.unmodifiableList(Arrays.asList(512, 768, 1024));
-    public static final List<Integer> RSA_SIZES =
-            Collections.unmodifiableList(Arrays.asList(1024, 2048, 3072, 4096));
-    public static final List<Integer> ED25519_SIZES =
-            Collections.unmodifiableList(Arrays.asList(256));
 
     @Rule
     public final TestWatcher rule = new TestWatcher() {
@@ -115,503 +62,21 @@ public abstract class BaseTestSupport extends Assert {
         }
     };
 
-    @Rule
-    public final TestName testNameHolder = new TestName();
-    private Path targetFolder;
-    private Path tempFolder;
-
     protected BaseTestSupport() {
         super();
     }
 
-    public final String getCurrentTestName() {
-        return testNameHolder.getMethodName();
-    }
-
     protected SshServer setupTestServer() {
-        return Utils.setupTestServer(getClass());
+        return CoreTestSupportUtils.setupTestServer(getClass());
     }
 
     protected SshClient setupTestClient() {
-        return Utils.setupTestClient(getClass());
-    }
-
-    protected KeyPairProvider createTestHostKeyProvider() {
-        return Utils.createTestHostKeyProvider(getClass());
-    }
-
-    /**
-     * Attempts to build a <U>relative</U> path whose root is the location
-     * of the TEMP sub-folder of the Maven &quot;target&quot; folder associated
-     * with the project
-     *
-     * @param comps The path components - ignored if {@code null}/empty
-     * @return The {@link Path} representing the result - same as target folder if no components
-     * @see #TEMP_SUBFOLDER_NAME
-     * @see #getTargetRelativeFile(Collection)
-     */
-    protected Path getTempTargetRelativeFile(String... comps) {
-        return getTempTargetRelativeFile(GenericUtils.isEmpty(comps) ? Collections.emptyList() : Arrays.asList(comps));
-    }
-
-    /**
-     * Attempts to build a <U>relative</U> path whose root is the location
-     * of the TEMP sub-folder of the Maven &quot;target&quot; folder associated
-     * with the project
-     *
-     * @param comps The path components - ignored if {@code null}/empty
-     * @return The {@link Path} representing the result - same as target folder if no components
-     * @see #TEMP_SUBFOLDER_NAME
-     * @see #getTempTargetFolder()
-     */
-    protected Path getTempTargetRelativeFile(Collection<String> comps) {
-        return Utils.resolve(getTempTargetFolder(), comps);
-    }
-
-    /**
-     * @return The TEMP sub-folder {@link Path} of the Maven &quot;target&quot; folder
-     * associated with the project - never {@code null}
-     */
-    protected Path getTempTargetFolder() {
-        synchronized (TEMP_SUBFOLDER_NAME) {
-            if (tempFolder == null) {
-                tempFolder = Objects.requireNonNull(detectTargetFolder(), "No target folder detected").resolve(TEMP_SUBFOLDER_NAME);
-            }
-        }
-
-        return tempFolder;
-    }
-
-    /**
-     * Attempts to build a <U>relative</U> path whose root is the location
-     * of the Maven &quot;target&quot; folder associated with the project
-     *
-     * @param comps The path components - ignored if {@code null}/empty
-     * @return The {@link Path} representing the result - same as target folder if no components
-     */
-    protected Path getTargetRelativeFile(String... comps) {
-        return getTargetRelativeFile(GenericUtils.isEmpty(comps) ? Collections.emptyList() : Arrays.asList(comps));
-    }
-
-    /**
-     * Attempts to build a <U>relative</U> path whose root is the location
-     * of the Maven &quot;target&quot; folder associated with the project
-     *
-     * @param comps The path components - ignored if {@code null}/empty
-     * @return The {@link Path} representing the result - same as target folder if no components
-     * @see #detectTargetFolder()
-     */
-    protected Path getTargetRelativeFile(Collection<String> comps) {
-        return Utils.resolve(detectTargetFolder(), comps);
-    }
-
-    /**
-     * Attempts to detect the location of the Maven &quot;target&quot; folder
-     * associated with the project that contains the actual class extending this
-     * base class
-     *
-     * @return The {@link File} representing the location of the &quot;target&quot; folder
-     * @throws IllegalArgumentException If failed to detect the folder
-     */
-    protected Path detectTargetFolder() throws IllegalArgumentException {
-        synchronized (TEMP_SUBFOLDER_NAME) {
-            if (targetFolder == null) {
-                targetFolder = Objects.requireNonNull(Utils.detectTargetFolder(getClass()), "Failed to detect target folder").toPath();
-            }
-        }
-
-        return targetFolder;
-    }
-
-    /**
-     * Creates a folder bearing the class's simple name under the project's target temporary folder
-     *
-     * @return The created folder {@link Path}
-     * @throws IOException If failed to detect or create the folder's location
-     * @see #detectTargetFolder() detectTargetFolder
-     * @see #assertHierarchyTargetFolderExists(Path, LinkOption...) assertHierarchyTargetFolderExists
-     */
-    protected Path createTempClassFolder() throws IOException {
-        Path tmpDir = detectTargetFolder();
-        return assertHierarchyTargetFolderExists(tmpDir.resolve(getClass().getSimpleName()));
-    }
-
-    protected Path detectSourcesFolder() throws IllegalStateException {
-        Path target = detectTargetFolder();
-        Path parent = target.getParent();
-        return parent.resolve("src");
-    }
-
-    protected Path getTestResourcesFolder() {
-        try {
-            URL url = getClass().getResource(getClass().getSimpleName() + ".class");
-            return Paths.get(url.toURI()).getParent();
-        } catch (URISyntaxException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    protected Path getClassResourcesFolder(String resType /* test or main */) {
-        return getClassResourcesFolder(resType, getClass());
-    }
-
-    protected Path getClassResourcesFolder(String resType /* test or main */, Class<?> clazz) {
-        return getPackageResourcesFolder(resType, clazz.getPackage());
-    }
-
-    protected Path getPackageResourcesFolder(String resType /* test or main */, Package pkg) {
-        return getPackageResourcesFolder(resType, pkg.getName());
-    }
-
-    protected Path getPackageResourcesFolder(String resType /* test or main */, String pkgName) {
-        Path src = detectSourcesFolder();
-        Path root = src.resolve(resType);
-        Path resources = root.resolve(RESOURCES_SUBFOLDER);
-        return resources.resolve(pkgName.replace('.', File.separatorChar));
-    }
-
-    /* ------------------- Useful extra test helpers ---------------------- */
-
-    public static String shuffleCase(CharSequence cs) {
-        if (GenericUtils.isEmpty(cs)) {
-            return "";
-        }
-
-        StringBuilder sb = new StringBuilder(cs.length());
-        for (int index = 0; index < cs.length(); index++) {
-            char ch = cs.charAt(index);
-            double v = Math.random();
-            if (Double.compare(v, 0.3d) < 0) {
-                ch = Character.toUpperCase(ch);
-            } else if ((Double.compare(v, 0.3d) >= 0) && (Double.compare(v, 0.6d) < 0)) {
-                ch = Character.toLowerCase(ch);
-            }
-            sb.append(ch);
-        }
-
-        return sb.toString();
-    }
-
-    public static String repeat(CharSequence csq, int nTimes) {
-        if (GenericUtils.isEmpty(csq) || (nTimes <= 0)) {
-            return "";
-        }
-
-        StringBuilder sb = new StringBuilder(nTimes * csq.length());
-        for (int index = 0; index < nTimes; index++) {
-            sb.append(csq);
-        }
-
-        return sb.toString();
-    }
-
-    public static List<Object[]> parameterize(Collection<?> params) {
-        if (GenericUtils.isEmpty(params)) {
-            return Collections.emptyList();
-        }
-
-        List<Object[]> result = new ArrayList<>(params.size());
-        for (Object p : params) {
-            result.add(new Object[]{p});
-        }
-
-        return result;
-    }
-
-    /* ----------------------- Useful extra assertions --------------------- */
-
-    public static void assertEquals(String message, boolean expected, boolean actual) {
-        assertEquals(message, Boolean.valueOf(expected), Boolean.valueOf(actual));
-    }
-
-    public static <T> void assertEquals(String message, Iterable<? extends T> expected, Iterable<? extends T> actual) {
-        if (expected != actual) {
-            assertEquals(message, expected.iterator(), actual.iterator());
-        }
-    }
-
-    public static <T> void assertEquals(String message, Iterator<? extends T> expected, Iterator<? extends T> actual) {
-        if (expected == actual) {
-            return;
-        }
-
-        for (int index = 0; expected.hasNext(); index++) {
-            assertTrue(message + "[next actual index=" + index + "]", actual.hasNext());
-
-            T expValue = expected.next();
-            T actValue = actual.next();
-            assertEquals(message + "[iterator index=" + index + "]", expValue, actValue);
-        }
-
-        // once expected is exhausted make sure no more actual items left
-        assertFalse(message + "[non-empty-actual]", actual.hasNext());
-    }
-
-    public static Path assertHierarchyTargetFolderExists(Path folder, LinkOption... options) throws IOException {
-        if (Files.exists(folder, options)) {
-            assertTrue("Target is an existing file instead of a folder: " + folder, Files.isDirectory(folder, options));
-        } else {
-            Files.createDirectories(folder);
-        }
-
-        return folder;
-    }
-
-    public static void assertFileContentsEquals(String prefix, Path expected, Path actual) throws IOException {
-        long cmpSize = Files.size(expected);
-        assertEquals(prefix + ": Mismatched file size", cmpSize, Files.size(expected));
-
-        try (InputStream expStream = Files.newInputStream(expected);
-             InputStream actStream = Files.newInputStream(actual)) {
-            byte[] expData = new byte[IoUtils.DEFAULT_COPY_SIZE];
-            byte[] actData = new byte[expData.length];
-
-            for (long offset = 0L; offset < cmpSize;) {
-                Arrays.fill(expData, (byte) 0);
-                int expLen = expStream.read(expData);
-                Arrays.fill(actData, (byte) 0);
-                int actLen = actStream.read(actData);
-                assertEquals(prefix + ": Mismatched read size at offset=" + offset, expLen, actLen);
-                assertArrayEquals(prefix + ": Mismatched data at offset=" + offset, expData, actData);
-
-                offset += expLen;
-            }
-        }
-    }
-
-    public static File assertHierarchyTargetFolderExists(File folder) {
-        if (folder.exists()) {
-            assertTrue("Target is an existing file instead of a folder: " + folder.getAbsolutePath(), folder.isDirectory());
-        } else {
-            assertTrue("Failed to create hierarchy of " + folder.getAbsolutePath(), folder.mkdirs());
-        }
-
-        return folder;
-    }
-
-    public static void assertObjectInstanceOf(String message, Class<?> expected, Object obj) {
-        assertNotNull(message + " - no actual object", obj);
-
-        Class<?> actual = obj.getClass();
-        if (!expected.isAssignableFrom(actual)) {
-            fail(message + " - actual object type (" + actual.getName() + ") incompatible with expected (" + expected.getName() + ")");
-        }
-    }
-
-    public static <E> void assertListEquals(String message, List<? extends E> expected, List<? extends E> actual) {
-        int expSize = GenericUtils.size(expected);
-        int actSize = GenericUtils.size(actual);
-        assertEquals(message + "[size]", expSize, actSize);
-
-        for (int index = 0; index < expSize; index++) {
-            E expValue = expected.get(index);
-            E actValue = actual.get(index);
-            assertEquals(message + "[" + index + "]", expValue, actValue);
-        }
-    }
-
-    public static <K, V> void assertMapEquals(String message, Map<? extends K, ? extends V> expected, Map<? super K, ? extends V> actual) {
-        int numItems = GenericUtils.size(expected);
-        assertEquals(message + "[size]", numItems, GenericUtils.size(actual));
-
-        if (numItems > 0) {
-            expected.forEach((key, expValue) -> {
-                V actValue = actual.get(key);
-                assertEquals(message + "[" + key + "]", expValue, actValue);
-            });
-        }
-    }
-
-    public static void assertKeyPairEquals(String message, KeyPair expected, KeyPair actual) {
-        assertKeyEquals(message + "[public]", expected.getPublic(), actual.getPublic());
-        assertKeyEquals(message + "[private]", expected.getPrivate(), actual.getPrivate());
-    }
-
-    public static <T extends Key> void assertKeyEquals(String message, T expected, T actual) {
-        if (expected == actual) {
-            return;
-        }
-
-        assertEquals(message + "[algorithm]", expected.getAlgorithm(), actual.getAlgorithm());
-
-        if (expected instanceof RSAPublicKey) {
-            assertRSAPublicKeyEquals(message, RSAPublicKey.class.cast(expected), RSAPublicKey.class.cast(actual));
-        } else if (expected instanceof DSAPublicKey) {
-            assertDSAPublicKeyEquals(message, DSAPublicKey.class.cast(expected), DSAPublicKey.class.cast(actual));
-        } else if (expected instanceof ECPublicKey) {
-            assertECPublicKeyEquals(message, ECPublicKey.class.cast(expected), ECPublicKey.class.cast(actual));
-        } else if (expected instanceof RSAPrivateKey) {
-            assertRSAPrivateKeyEquals(message, RSAPrivateKey.class.cast(expected), RSAPrivateKey.class.cast(actual));
-        } else if (expected instanceof ECPrivateKey) {
-            assertECPrivateKeyEquals(message, ECPrivateKey.class.cast(expected), ECPrivateKey.class.cast(actual));
-        }
-        assertArrayEquals(message + "[encdoded-data]", expected.getEncoded(), actual.getEncoded());
-    }
-
-    public static void assertRSAPublicKeyEquals(String message, RSAPublicKey expected, RSAPublicKey actual) {
-        if (expected == actual) {
-            return;
-        }
-
-        assertEquals(message + "[e]", expected.getPublicExponent(), actual.getPublicExponent());
-        assertEquals(message + "[n]", expected.getModulus(), actual.getModulus());
-    }
-
-    public static void assertDSAPublicKeyEquals(String message, DSAPublicKey expected, DSAPublicKey actual) {
-        if (expected == actual) {
-            return;
-        }
-
-        assertEquals(message + "[y]", expected.getY(), actual.getY());
-        assertDSAParamsEquals(message + "[params]", expected.getParams(), actual.getParams());
-    }
-
-    public static void assertECPublicKeyEquals(String message, ECPublicKey expected, ECPublicKey actual) {
-        if (expected == actual) {
-            return;
-        }
-
-        assertECPointEquals(message + "[W]", expected.getW(), actual.getW());
-        assertECParameterSpecEquals(message, expected, actual);
-    }
-
-    public static void assertRSAPrivateKeyEquals(String message, RSAPrivateKey expected, RSAPrivateKey actual) {
-        if (expected == actual) {
-            return;
-        }
-
-        assertEquals(message + "[d]", expected.getPrivateExponent(), actual.getPrivateExponent());
-        assertEquals(message + "[n]", expected.getModulus(), actual.getModulus());
-    }
-
-    public static void assertDSAPrivateKeyEquals(String message, DSAPrivateKey expected, DSAPrivateKey actual) {
-        if (expected == actual) {
-            return;
-        }
-
-        assertEquals(message + "[x]", expected.getX(), actual.getX());
-        assertDSAParamsEquals(message + "[params]", expected.getParams(), actual.getParams());
-    }
-
-    public static void assertDSAParamsEquals(String message, DSAParams expected, DSAParams actual) {
-        if (expected == actual) {
-            return;
-        }
-
-        assertEquals(message + "[g]", expected.getG(), actual.getG());
-        assertEquals(message + "[p]", expected.getP(), actual.getP());
-        assertEquals(message + "[q]", expected.getQ(), actual.getQ());
-    }
-
-    public static void assertECPrivateKeyEquals(String message, ECPrivateKey expected, ECPrivateKey actual) {
-        if (expected == actual) {
-            return;
-        }
-
-        assertEquals(message + "[S]", expected.getS(), actual.getS());
-        assertECParameterSpecEquals(message, expected, actual);
-    }
-
-    public static void assertECParameterSpecEquals(String message, ECKey expected, ECKey actual) {
-        if (expected == actual) {
-            return;
-        }
-        assertECParameterSpecEquals(message, expected.getParams(), actual.getParams());
-    }
-
-    public static void assertECParameterSpecEquals(String message, ECParameterSpec expected, ECParameterSpec actual) {
-        if (expected == actual) {
-            return;
-        }
-
-        assertEquals(message + "[order]", expected.getOrder(), actual.getOrder());
-        assertEquals(message + "[cofactor]", expected.getCofactor(), actual.getCofactor());
-        assertECPointEquals(message + "[generator]", expected.getGenerator(), actual.getGenerator());
-        assertCurveEquals(message + "[curve]", expected.getCurve(), actual.getCurve());
-    }
-
-    public static void assertCurveEquals(String message, EllipticCurve expected, EllipticCurve actual) {
-        if (expected == actual) {
-            return;
-        }
-
-        assertEquals(message + "[A]", expected.getA(), actual.getA());
-        assertEquals(message + "[B]", expected.getB(), actual.getB());
-        assertArrayEquals(message + "[seed]", expected.getSeed(), actual.getSeed());
-        assertECFieldEquals(message + "[field]", expected.getField(), actual.getField());
-    }
-
-    public static void assertECFieldEquals(String message, ECField expected, ECField actual) {
-        if (expected == actual) {
-            return;
-        }
-
-        assertEquals(message + "[size]", expected.getFieldSize(), actual.getFieldSize());
-    }
-
-    public static void assertECPointEquals(String message, ECPoint expected, ECPoint actual) {
-        if (expected == actual) {
-            return;
-        }
-
-        assertEquals(message + "[x]", expected.getAffineX(), actual.getAffineX());
-        assertEquals(message + "[y]", expected.getAffineY(), actual.getAffineY());
-    }
-
-    public static void assertFileLength(File file, long length, long timeout) throws Exception {
-        assertFileLength(file.toPath(), length, timeout);
-    }
-
-    /**
-     * Waits the specified timeout for the file to exist and have the required length
-     *
-     * @param file    The file {@link Path} to check
-     * @param length  Expected length
-     * @param timeout Timeout (msec.) to wait for satisfying the requirements
-     * @throws Exception If failed to access the file
-     */
-    public static void assertFileLength(Path file, long length, long timeout) throws Exception {
-        if (waitForFile(file, length, timeout)) {
-            return;
-        }
-        assertTrue("File not found: " + file, Files.exists(file));
-        assertEquals("Mismatched file size for " + file, length, Files.size(file));
-    }
-
-    public static boolean waitForFile(Path file, long length, long timeout) throws Exception {
-        while (timeout > 0L) {
-            long sleepTime = Math.min(timeout, 100L);
-            if (Files.exists(file) && (Files.size(file) == length)) {
-                return true;
-            }
-
-            long sleepStart = System.nanoTime();
-            Thread.sleep(sleepTime);
-            long sleepEnd = System.nanoTime();
-            long nanoSleep = sleepEnd - sleepStart;
-
-            sleepTime = TimeUnit.NANOSECONDS.toMillis(nanoSleep);
-            timeout -= sleepTime;
-        }
-
-        return false;
-    }
-
-    public static void outputDebugMessage(String format, Object... args) {
-        if (OUTPUT_DEBUG_MESSAGES) {
-            outputDebugMessage(String.format(format, args));
-        }
-    }
-
-    public static void outputDebugMessage(Object message) {
-        if (OUTPUT_DEBUG_MESSAGES) {
-            System.out.append("===[DEBUG]=== ").println(message);
-        }
+        return CoreTestSupportUtils.setupTestClient(getClass());
     }
 
     public static IoServiceFactoryFactory getIoServiceProvider() {
         DefaultIoServiceFactoryFactory factory =
-                DefaultIoServiceFactoryFactory.getDefaultIoServiceFactoryFactoryInstance();
+            DefaultIoServiceFactoryFactory.getDefaultIoServiceFactoryFactoryInstance();
         return factory.getIoServiceProvider();
     }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/util/test/CoreTestSupportUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/util/test/CoreTestSupportUtils.java b/sshd-core/src/test/java/org/apache/sshd/util/test/CoreTestSupportUtils.java
new file mode 100644
index 0000000..e6bb6cb
--- /dev/null
+++ b/sshd-core/src/test/java/org/apache/sshd/util/test/CoreTestSupportUtils.java
@@ -0,0 +1,63 @@
+/*
+ * 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.util.test;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+
+import org.apache.sshd.client.SshClient;
+import org.apache.sshd.client.config.hosts.HostConfigEntryResolver;
+import org.apache.sshd.client.keyverifier.AcceptAllServerKeyVerifier;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.server.SshServer;
+import org.apache.sshd.server.auth.pubkey.AcceptAllPublickeyAuthenticator;
+import org.apache.sshd.server.shell.UnknownCommandFactory;
+
+public final class CoreTestSupportUtils {
+    private CoreTestSupportUtils() {
+        throw new UnsupportedOperationException("No instance");
+    }
+
+    public static int getFreePort() throws Exception {
+        try (ServerSocket s = new ServerSocket()) {
+            s.setReuseAddress(true);
+            s.bind(new InetSocketAddress((InetAddress) null, 0));
+            return s.getLocalPort();
+        }
+    }
+
+    public static SshClient setupTestClient(Class<?> anchor) {
+        SshClient client = SshClient.setUpDefaultClient();
+        client.setServerKeyVerifier(AcceptAllServerKeyVerifier.INSTANCE);
+        client.setHostConfigEntryResolver(HostConfigEntryResolver.EMPTY);
+        client.setKeyPairProvider(KeyPairProvider.EMPTY_KEYPAIR_PROVIDER);
+        return client;
+    }
+
+    public static SshServer setupTestServer(Class<?> anchor) {
+        SshServer sshd = SshServer.setUpDefaultServer();
+        sshd.setKeyPairProvider(CommonTestSupportUtils.createTestHostKeyProvider(anchor));
+        sshd.setPasswordAuthenticator(BogusPasswordAuthenticator.INSTANCE);
+        sshd.setPublickeyAuthenticator(AcceptAllPublickeyAuthenticator.INSTANCE);
+        sshd.setShellFactory(EchoShellFactory.INSTANCE);
+        sshd.setCommandFactory(UnknownCommandFactory.INSTANCE);
+        return sshd;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/util/test/JUnit4ClassRunnerWithParameters.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/util/test/JUnit4ClassRunnerWithParameters.java b/sshd-core/src/test/java/org/apache/sshd/util/test/JUnit4ClassRunnerWithParameters.java
deleted file mode 100644
index acf44a5..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/util/test/JUnit4ClassRunnerWithParameters.java
+++ /dev/null
@@ -1,48 +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.util.test;
-
-import org.junit.runners.model.InitializationError;
-import org.junit.runners.parameterized.BlockJUnit4ClassRunnerWithParameters;
-import org.junit.runners.parameterized.TestWithParameters;
-
-/**
- * Uses a cached created instance instead of a new one on every call of {@code #createTest()}
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class JUnit4ClassRunnerWithParameters extends BlockJUnit4ClassRunnerWithParameters {
-    private volatile Object testInstance;
-
-    public JUnit4ClassRunnerWithParameters(TestWithParameters test) throws InitializationError {
-        super(test);
-    }
-
-    @Override
-    public Object createTest() throws Exception {
-        synchronized (this) {
-            if (testInstance == null) {
-                testInstance = super.createTest();
-            }
-        }
-
-        return testInstance;
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/util/test/JUnit4ClassRunnerWithParametersFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/util/test/JUnit4ClassRunnerWithParametersFactory.java b/sshd-core/src/test/java/org/apache/sshd/util/test/JUnit4ClassRunnerWithParametersFactory.java
deleted file mode 100644
index 12ab252..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/util/test/JUnit4ClassRunnerWithParametersFactory.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.util.test;
-
-import org.junit.runner.Runner;
-import org.junit.runners.model.InitializationError;
-import org.junit.runners.parameterized.ParametersRunnerFactory;
-import org.junit.runners.parameterized.TestWithParameters;
-
-/**
- * Avoids re-creating a test class instance for each parameterized test method. Usage:
- *
- * <PRE><code>
- * @FixMethodOrder(MethodSorters.NAME_ASCENDING)
- * @RunWith(Parameterized.class)
- * @UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
- * public class MyParameterizedTest {
- *      public MyParameterizedTest(...params...) {
- *          ....
- *      }
- *
- *      @Parameters(...)
- *      public static List<Object[]> parameters() {
- *          ...
- *      }
- * }
- * </code></PRE>
- *
- * @see JUnit4ClassRunnerWithParameters
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class JUnit4ClassRunnerWithParametersFactory implements ParametersRunnerFactory {
-    public JUnit4ClassRunnerWithParametersFactory() {
-        super();
-    }
-
-    @Override
-    public Runner createRunnerForTestWithParameters(TestWithParameters test) throws InitializationError {
-        return new JUnit4ClassRunnerWithParameters(test);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/util/test/JUnit4SingleInstanceClassRunner.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/util/test/JUnit4SingleInstanceClassRunner.java b/sshd-core/src/test/java/org/apache/sshd/util/test/JUnit4SingleInstanceClassRunner.java
deleted file mode 100644
index a8f11ce..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/util/test/JUnit4SingleInstanceClassRunner.java
+++ /dev/null
@@ -1,54 +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.util.test;
-
-import java.util.AbstractMap.SimpleImmutableEntry;
-import java.util.Map;
-import java.util.concurrent.atomic.AtomicReference;
-
-import org.junit.runners.BlockJUnit4ClassRunner;
-import org.junit.runners.model.InitializationError;
-import org.junit.runners.model.TestClass;
-
-/**
- * @see <A HREF="https://issues.apache.org/jira/browse/SSHD-764">SSHD-764</A>
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class JUnit4SingleInstanceClassRunner extends BlockJUnit4ClassRunner {
-    private final AtomicReference<Map.Entry<Class<?>, ?>> testHolder = new AtomicReference<>();
-
-    public JUnit4SingleInstanceClassRunner(Class<?> klass) throws InitializationError {
-        super(klass);
-    }
-
-    @Override
-    protected Object createTest() throws Exception {
-        Map.Entry<Class<?>, ?> lastTest = testHolder.get();
-        Class<?> lastTestClass = (lastTest == null) ? null : lastTest.getKey();
-        TestClass curTest = getTestClass();
-        Class<?> curTestClass = curTest.getJavaClass();
-        if (curTestClass == lastTestClass) {
-            return lastTest.getValue();
-        }
-
-        Object instance = super.createTest();
-        testHolder.set(new SimpleImmutableEntry<>(curTestClass, instance));
-        return instance;
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/util/test/NoIoTestCase.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/util/test/NoIoTestCase.java b/sshd-core/src/test/java/org/apache/sshd/util/test/NoIoTestCase.java
deleted file mode 100644
index 7d52892..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/util/test/NoIoTestCase.java
+++ /dev/null
@@ -1,30 +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.util.test;
-
-/**
- * Marker interface used as <A HREF="https://github.com/junit-team/junit4/wiki/categories">jUnit category</A>
- * to indicate a test that does not require real client/server interaction.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface NoIoTestCase {
-    // Marker interface
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/util/test/OutputCountTrackingOutputStream.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/util/test/OutputCountTrackingOutputStream.java b/sshd-core/src/test/java/org/apache/sshd/util/test/OutputCountTrackingOutputStream.java
deleted file mode 100644
index b4eb84c..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/util/test/OutputCountTrackingOutputStream.java
+++ /dev/null
@@ -1,56 +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.util.test;
-
-import java.io.FilterOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class OutputCountTrackingOutputStream extends FilterOutputStream {
-    protected long writeCount;
-
-    public OutputCountTrackingOutputStream(OutputStream out) {
-        super(out);
-    }
-
-    @Override
-    public void write(int b) throws IOException {
-        out.write(b);
-        updateWriteCount(1L);
-    }
-
-    @Override
-    public void write(byte[] b, int off, int len) throws IOException {
-        out.write(b, off, len); // don't call super since it calls the single 'write'
-        updateWriteCount(len);
-    }
-
-    public long getWriteCount() {
-        return writeCount;
-    }
-
-    protected long updateWriteCount(long delta) {
-        writeCount += delta;
-        return writeCount;
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/util/test/TeeOutputStream.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/util/test/TeeOutputStream.java b/sshd-core/src/test/java/org/apache/sshd/util/test/TeeOutputStream.java
deleted file mode 100644
index 4cd6014..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/util/test/TeeOutputStream.java
+++ /dev/null
@@ -1,64 +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.util.test;
-
-import java.io.IOException;
-import java.io.OutputStream;
-
-/**
- * TODO Add javadoc
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class TeeOutputStream extends OutputStream {
-
-    private OutputStream[] tees;
-
-    public TeeOutputStream(OutputStream... tees) {
-        this.tees = tees;
-    }
-
-    @Override
-    public void write(int b) throws IOException {
-        for (OutputStream s : tees) {
-            s.write(b);
-        }
-    }
-
-    @Override
-    public void write(byte[] b, int off, int len) throws IOException {
-        for (OutputStream s : tees) {
-            s.write(b, off, len);
-        }
-    }
-
-    @Override
-    public void flush() throws IOException {
-        for (OutputStream s : tees) {
-            s.flush();
-        }
-    }
-
-    @Override
-    public void close() throws IOException {
-        for (OutputStream s : tees) {
-            s.close();
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/util/test/Utils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/util/test/Utils.java b/sshd-core/src/test/java/org/apache/sshd/util/test/Utils.java
deleted file mode 100644
index a007239..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/util/test/Utils.java
+++ /dev/null
@@ -1,652 +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.util.test;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.MalformedURLException;
-import java.net.ServerSocket;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.DirectoryStream;
-import java.nio.file.Files;
-import java.nio.file.LinkOption;
-import java.nio.file.Path;
-import java.security.CodeSource;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.security.ProtectionDomain;
-import java.security.spec.InvalidKeySpecException;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.atomic.AtomicReference;
-
-import org.apache.sshd.client.SshClient;
-import org.apache.sshd.client.config.hosts.HostConfigEntryResolver;
-import org.apache.sshd.client.keyverifier.AcceptAllServerKeyVerifier;
-import org.apache.sshd.common.Factory;
-import org.apache.sshd.common.cipher.ECCurves;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.keyprovider.FileKeyPairProvider;
-import org.apache.sshd.common.keyprovider.KeyIdentityProvider;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.keyprovider.KeyPairProviderHolder;
-import org.apache.sshd.common.random.Random;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-import org.apache.sshd.server.SshServer;
-import org.apache.sshd.server.auth.pubkey.AcceptAllPublickeyAuthenticator;
-import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
-import org.apache.sshd.server.shell.UnknownCommandFactory;
-
-public final class Utils {
-    /**
-     * URL/URI scheme that refers to a file
-     */
-    public static final String FILE_URL_SCHEME = "file";
-    /**
-     * Prefix used in URL(s) that reference a file resource
-     */
-    public static final String FILE_URL_PREFIX = FILE_URL_SCHEME + ":";
-
-    /**
-     * Separator used in URL(s) that reference a resource inside a JAR
-     * to denote the sub-path inside the JAR
-     */
-    public static final char RESOURCE_SUBPATH_SEPARATOR = '!';
-
-    /**
-     * Suffix of JAR files
-     */
-    public static final String JAR_FILE_SUFFIX = ".jar";
-
-    /**
-     * URL/URI scheme that refers to a JAR
-     */
-    public static final String JAR_URL_SCHEME = "jar";
-
-    /**
-     * Prefix used in URL(s) that reference a resource inside a JAR
-     */
-    public static final String JAR_URL_PREFIX = JAR_URL_SCHEME + ":";
-
-    /**
-     * Suffix of compile Java class files
-     */
-    public static final String CLASS_FILE_SUFFIX = ".class";
-
-    public static final List<String> TARGET_FOLDER_NAMES =    // NOTE: order is important
-            Collections.unmodifiableList(
-                    Arrays.asList("target" /* Maven */, "build" /* Gradle */));
-
-    public static final String DEFAULT_TEST_HOST_KEY_PROVIDER_ALGORITHM = KeyUtils.RSA_ALGORITHM;
-    // uses a cached instance to avoid re-creating the keys as it is a time-consuming effort
-    private static final AtomicReference<KeyPairProvider> KEYPAIR_PROVIDER_HOLDER = new AtomicReference<>();
-    // uses a cached instance to avoid re-creating the keys as it is a time-consuming effort
-    private static final Map<String, FileKeyPairProvider> PROVIDERS_MAP = new ConcurrentHashMap<>();
-
-    private Utils() {
-        throw new UnsupportedOperationException("No instance");
-    }
-
-    public static KeyPairProvider createTestHostKeyProvider(Class<?> anchor) {
-        KeyPairProvider provider = KEYPAIR_PROVIDER_HOLDER.get();
-        if (provider != null) {
-            return provider;
-        }
-
-        File targetFolder = Objects.requireNonNull(detectTargetFolder(anchor), "Failed to detect target folder");
-        File file = new File(targetFolder, "hostkey." + DEFAULT_TEST_HOST_KEY_PROVIDER_ALGORITHM.toLowerCase());
-        provider = createTestHostKeyProvider(file);
-
-        KeyPairProvider prev = KEYPAIR_PROVIDER_HOLDER.getAndSet(provider);
-        if (prev != null) { // check if somebody else beat us to it
-            return prev;
-        } else {
-            return provider;
-        }
-    }
-
-    public static KeyPairProvider createTestHostKeyProvider(File file) {
-        return createTestHostKeyProvider(Objects.requireNonNull(file, "No file").toPath());
-    }
-
-    public static KeyPairProvider createTestHostKeyProvider(Path path) {
-        SimpleGeneratorHostKeyProvider keyProvider = new SimpleGeneratorHostKeyProvider();
-        keyProvider.setPath(Objects.requireNonNull(path, "No path"));
-        keyProvider.setAlgorithm(DEFAULT_TEST_HOST_KEY_PROVIDER_ALGORITHM);
-        return validateKeyPairProvider(keyProvider);
-    }
-
-    public static KeyPair getFirstKeyPair(KeyPairProviderHolder holder) {
-        return getFirstKeyPair(Objects.requireNonNull(holder, "No holder").getKeyPairProvider());
-    }
-
-    public static KeyPair getFirstKeyPair(KeyIdentityProvider provider) {
-        Objects.requireNonNull(provider, "No key pair provider");
-        Iterable<? extends KeyPair> pairs = Objects.requireNonNull(provider.loadKeys(), "No loaded keys");
-        Iterator<? extends KeyPair> iter = Objects.requireNonNull(pairs.iterator(), "No keys iterator");
-        ValidateUtils.checkTrue(iter.hasNext(), "Empty loaded kyes iterator");
-        return Objects.requireNonNull(iter.next(), "No key pair in iterator");
-    }
-
-    public static KeyPair generateKeyPair(String algorithm, int keySize) throws GeneralSecurityException {
-        KeyPairGenerator gen = SecurityUtils.getKeyPairGenerator(algorithm);
-        if (KeyUtils.EC_ALGORITHM.equalsIgnoreCase(algorithm)) {
-            ECCurves curve = ECCurves.fromCurveSize(keySize);
-            if (curve == null) {
-                throw new InvalidKeySpecException("Unknown curve for key size=" + keySize);
-            }
-            gen.initialize(curve.getParameters());
-        } else {
-            gen.initialize(keySize);
-        }
-
-        return gen.generateKeyPair();
-    }
-
-    public static FileKeyPairProvider createTestKeyPairProvider(String resource) {
-        File file = getFile(resource);
-        String filePath = file.getAbsolutePath();
-        FileKeyPairProvider provider = PROVIDERS_MAP.get(filePath);
-        if (provider != null) {
-            return provider;
-        }
-
-        provider = new FileKeyPairProvider();
-        provider.setFiles(Collections.singletonList(file));
-        provider = validateKeyPairProvider(provider);
-
-        FileKeyPairProvider prev = PROVIDERS_MAP.put(filePath, provider);
-        if (prev != null) { // check if somebody else beat us to it
-            return prev;
-        } else {
-            return provider;
-        }
-    }
-
-    private static <P extends KeyIdentityProvider> P validateKeyPairProvider(P provider) {
-        Objects.requireNonNull(provider, "No provider");
-
-        // get the I/O out of the way
-        Iterable<KeyPair> keys = Objects.requireNonNull(provider.loadKeys(), "No keys loaded");
-        if (keys instanceof Collection<?>) {
-            ValidateUtils.checkNotNullAndNotEmpty((Collection<?>) keys, "Empty keys loaded");
-        }
-
-        return provider;
-    }
-
-    public static Random getRandomizerInstance() {
-        Factory<Random> factory = SecurityUtils.getRandomFactory();
-        return factory.create();
-    }
-
-    public static int getFreePort() throws Exception {
-        try (ServerSocket s = new ServerSocket()) {
-            s.setReuseAddress(true);
-            s.bind(new InetSocketAddress((InetAddress) null, 0));
-            return s.getLocalPort();
-        }
-    }
-
-    private static File getFile(String resource) {
-        URL url = Utils.class.getClassLoader().getResource(resource);
-        try {
-            return new File(url.toURI());
-        } catch (URISyntaxException e) {
-            return new File(url.getPath());
-        }
-    }
-
-    public static Path resolve(Path root, String... children) {
-        if (GenericUtils.isEmpty(children)) {
-            return root;
-        } else {
-            return resolve(root, Arrays.asList(children));
-        }
-    }
-
-    public static Path resolve(Path root, Collection<String> children) {
-        Path path = root;
-        if (!GenericUtils.isEmpty(children)) {
-            for (String child : children) {
-                path = path.resolve(child);
-            }
-        }
-
-        return path;
-    }
-
-    public static File resolve(File root, String... children) {
-        if (GenericUtils.isEmpty(children)) {
-            return root;
-        } else {
-            return resolve(root, Arrays.asList(children));
-        }
-    }
-
-    public static File resolve(File root, Collection<String> children) {
-        File path = root;
-        if (!GenericUtils.isEmpty(children)) {
-            for (String child : children) {
-                path = new File(path, child);
-            }
-        }
-
-        return path;
-    }
-
-    /**
-     * Removes the specified file - if it is a directory, then its children
-     * are deleted recursively and then the directory itself. <B>Note:</B>
-     * no attempt is made to make sure that {@link File#delete()} was successful
-     *
-     * @param file The {@link File} to be deleted - ignored if {@code null}
-     *             or does not exist anymore
-     * @return The <tt>file</tt> argument
-     */
-    public static File deleteRecursive(File file) {
-        if ((file == null) || (!file.exists())) {
-            return file;
-        }
-
-        if (file.isDirectory()) {
-            File[] children = file.listFiles();
-            if (!GenericUtils.isEmpty(children)) {
-                for (File child : children) {
-                    deleteRecursive(child);
-                }
-            }
-        }
-
-        // seems that if a file is not writable it cannot be deleted
-        if (!file.canWrite()) {
-            file.setWritable(true, false);
-        }
-
-        if (!file.delete()) {
-            System.err.append("Failed to delete ").println(file.getAbsolutePath());
-        }
-
-        return file;
-    }
-
-    /**
-     * Removes the specified file - if it is a directory, then its children
-     * are deleted recursively and then the directory itself.
-     *
-     * @param path    The file {@link Path} to be deleted - ignored if {@code null}
-     *                or does not exist anymore
-     * @param options The {@link LinkOption}s to use
-     * @return The <tt>path</tt> argument
-     * @throws IOException If failed to access/remove some file(s)
-     */
-    public static Path deleteRecursive(Path path, LinkOption... options) throws IOException {
-        if ((path == null) || (!Files.exists(path))) {
-            return path;
-        }
-
-        if (Files.isDirectory(path)) {
-            try (DirectoryStream<Path> ds = Files.newDirectoryStream(path)) {
-                for (Path child : ds) {
-                    deleteRecursive(child, options);
-                }
-            }
-        }
-
-        try {
-            // seems that if a file is not writable it cannot be deleted
-            if (!Files.isWritable(path)) {
-                path.toFile().setWritable(true, false);
-            }
-            Files.delete(path);
-        } catch (IOException e) {
-            // same logic as deleteRecursive(File) which does not check if deletion succeeded
-            System.err.append("Failed (").append(e.getClass().getSimpleName()).append(")")
-                    .append(" to delete ").append(path.toString())
-                    .append(": ").println(e.getMessage());
-        }
-
-        return path;
-    }
-
-    /**
-     * @param anchor An anchor {@link Class} whose container we want to use
-     *               as the starting point for the &quot;target&quot; folder lookup up the
-     *               hierarchy
-     * @return The &quot;target&quot; <U>folder</U> - {@code null} if not found
-     * @see #detectTargetFolder(File)
-     */
-    public static File detectTargetFolder(Class<?> anchor) {
-        return detectTargetFolder(getClassContainerLocationFile(anchor));
-    }
-
-    /**
-     * @param clazz A {@link Class} object
-     * @return A {@link File} of the location of the class bytes container
-     * - e.g., the root folder, the containing JAR, etc.. Returns
-     * {@code null} if location could not be resolved
-     * @throws IllegalArgumentException If location is not a valid
-     *                                  {@link File} location
-     * @see #getClassContainerLocationURI(Class)
-     * @see #toFileSource(URI)
-     */
-    public static File getClassContainerLocationFile(Class<?> clazz)
-            throws IllegalArgumentException {
-        try {
-            URI uri = getClassContainerLocationURI(clazz);
-            return toFileSource(uri);
-        } catch (URISyntaxException | MalformedURLException e) {
-            throw new IllegalArgumentException(e.getClass().getSimpleName() + ": " + e.getMessage(), e);
-        }
-    }
-
-    /**
-     * @param clazz A {@link Class} object
-     * @return A {@link URI} to the location of the class bytes container
-     * - e.g., the root folder, the containing JAR, etc.. Returns
-     * {@code null} if location could not be resolved
-     * @throws URISyntaxException if location is not a valid URI
-     * @see #getClassContainerLocationURL(Class)
-     */
-    public static URI getClassContainerLocationURI(Class<?> clazz) throws URISyntaxException {
-        URL url = getClassContainerLocationURL(clazz);
-        return (url == null) ? null : url.toURI();
-    }
-
-    /**
-     * @param clazz A {@link Class} object
-     * @return A {@link URL} to the location of the class bytes container
-     * - e.g., the root folder, the containing JAR, etc.. Returns
-     * {@code null} if location could not be resolved
-     */
-    public static URL getClassContainerLocationURL(Class<?> clazz) {
-        ProtectionDomain pd = clazz.getProtectionDomain();
-        CodeSource cs = (pd == null) ? null : pd.getCodeSource();
-        URL url = (cs == null) ? null : cs.getLocation();
-        if (url == null) {
-            url = getClassBytesURL(clazz);
-            if (url == null) {
-                return null;
-            }
-
-            String srcForm = getURLSource(url);
-            if (GenericUtils.isEmpty(srcForm)) {
-                return null;
-            }
-
-            try {
-                url = new URL(srcForm);
-            } catch (MalformedURLException e) {
-                throw new IllegalArgumentException("getClassContainerLocationURL(" + clazz.getName() + ")"
-                        + " Failed to create URL=" + srcForm + " from " + url.toExternalForm()
-                        + ": " + e.getMessage());
-            }
-        }
-
-        return url;
-    }
-
-    /**
-     * Converts a {@link URL} that may refer to an internal resource to
-     * a {@link File} representing is &quot;source&quot; container (e.g.,
-     * if it is a resource in a JAR, then the result is the JAR's path)
-     *
-     * @param url The {@link URL} - ignored if {@code null}
-     * @return The matching {@link File}
-     * @throws MalformedURLException If source URL does not refer to a
-     *                               file location
-     * @see #toFileSource(URI)
-     */
-    public static File toFileSource(URL url) throws MalformedURLException {
-        if (url == null) {
-            return null;
-        }
-
-        try {
-            return toFileSource(url.toURI());
-        } catch (URISyntaxException e) {
-            throw new MalformedURLException("toFileSource(" + url.toExternalForm() + ")"
-                    + " cannot (" + e.getClass().getSimpleName() + ")"
-                    + " convert to URI: " + e.getMessage());
-        }
-    }
-
-    /**
-     * Converts a {@link URI} that may refer to an internal resource to
-     * a {@link File} representing is &quot;source&quot; container (e.g.,
-     * if it is a resource in a JAR, then the result is the JAR's path)
-     *
-     * @param uri The {@link URI} - ignored if {@code null}
-     * @return The matching {@link File}
-     * @throws MalformedURLException If source URI does not refer to a
-     *                               file location
-     * @see #getURLSource(URI)
-     */
-    public static File toFileSource(URI uri) throws MalformedURLException {
-        String src = getURLSource(uri);
-        if (GenericUtils.isEmpty(src)) {
-            return null;
-        }
-
-        if (!src.startsWith(FILE_URL_PREFIX)) {
-            throw new MalformedURLException("toFileSource(" + src + ") not a '" + FILE_URL_SCHEME + "' scheme");
-        }
-
-        try {
-            return new File(new URI(src));
-        } catch (URISyntaxException e) {
-            throw new MalformedURLException("toFileSource(" + src + ")"
-                    + " cannot (" + e.getClass().getSimpleName() + ")"
-                    + " convert to URI: " + e.getMessage());
-        }
-    }
-
-    /**
-     * @param uri The {@link URI} value - ignored if {@code null}
-     * @return The URI(s) source path where {@link #JAR_URL_PREFIX} and
-     * any sub-resource are stripped
-     * @see #getURLSource(String)
-     */
-    public static String getURLSource(URI uri) {
-        return getURLSource((uri == null) ? null : uri.toString());
-    }
-
-    /**
-     * @param url The {@link URL} value - ignored if {@code null}
-     * @return The URL(s) source path where {@link #JAR_URL_PREFIX} and
-     * any sub-resource are stripped
-     * @see #getURLSource(String)
-     */
-    public static String getURLSource(URL url) {
-        return getURLSource((url == null) ? null : url.toExternalForm());
-    }
-
-    /**
-     * @param externalForm The {@link URL#toExternalForm()} string - ignored if
-     *                     {@code null}/empty
-     * @return The URL(s) source path where {@link #JAR_URL_PREFIX} and
-     * any sub-resource are stripped
-     */
-    public static String getURLSource(String externalForm) {
-        String url = externalForm;
-        if (GenericUtils.isEmpty(url)) {
-            return url;
-        }
-
-        url = stripJarURLPrefix(externalForm);
-        if (GenericUtils.isEmpty(url)) {
-            return url;
-        }
-
-        int sepPos = url.indexOf(RESOURCE_SUBPATH_SEPARATOR);
-        if (sepPos < 0) {
-            return adjustURLPathValue(url);
-        } else {
-            return adjustURLPathValue(url.substring(0, sepPos));
-        }
-    }
-
-    /**
-     * @param url A {@link URL} - ignored if {@code null}
-     * @return The path after stripping any trailing '/' provided the path
-     * is not '/' itself
-     * @see #adjustURLPathValue(String)
-     */
-    public static String adjustURLPathValue(URL url) {
-        return adjustURLPathValue((url == null) ? null : url.getPath());
-    }
-
-    /**
-     * @param path A URL path value - ignored if {@code null}/empty
-     * @return The path after stripping any trailing '/' provided the path
-     * is not '/' itself
-     */
-    public static String adjustURLPathValue(final String path) {
-        final int pathLen = (path == null) ? 0 : path.length();
-        if ((pathLen <= 1) || (path.charAt(pathLen - 1) != '/')) {
-            return path;
-        }
-
-        return path.substring(0, pathLen - 1);
-    }
-
-    public static String stripJarURLPrefix(String externalForm) {
-        String url = externalForm;
-        if (GenericUtils.isEmpty(url)) {
-            return url;
-        }
-
-        if (url.startsWith(JAR_URL_PREFIX)) {
-            return url.substring(JAR_URL_PREFIX.length());
-        }
-
-        return url;
-    }
-
-    /**
-     * @param clazz The request {@link Class}
-     * @return A {@link URL} to the location of the <code>.class</code> file
-     * - {@code null} if location could not be resolved
-     */
-    public static URL getClassBytesURL(Class<?> clazz) {
-        String className = clazz.getName();
-        int sepPos = className.indexOf('$');
-        // if this is an internal class, then need to use its parent as well
-        if (sepPos > 0) {
-            sepPos = className.lastIndexOf('.');
-            if (sepPos > 0) {
-                className = className.substring(sepPos + 1);
-            }
-        } else {
-            className = clazz.getSimpleName();
-        }
-
-        return clazz.getResource(className + CLASS_FILE_SUFFIX);
-    }
-
-    public static String getClassBytesResourceName(Class<?> clazz) {
-        return getClassBytesResourceName((clazz == null) ? null : clazz.getName());
-    }
-
-    /**
-     * @param name The fully qualified class name - ignored if {@code null}/empty
-     * @return The relative path of the class file byte-code resource
-     */
-    public static String getClassBytesResourceName(String name) {
-        if (GenericUtils.isEmpty(name)) {
-            return name;
-        } else {
-            return name.replace('.', '/') + CLASS_FILE_SUFFIX;
-        }
-    }
-
-    /**
-     * @param anchorFile An anchor {@link File} we want to use
-     *                   as the starting point for the &quot;target&quot; or &quot;build&quot; folder
-     *                   lookup up the hierarchy
-     * @return The &quot;target&quot; <U>folder</U> - {@code null} if not found
-     */
-    public static File detectTargetFolder(File anchorFile) {
-        for (File file = anchorFile; file != null; file = file.getParentFile()) {
-            if (!file.isDirectory()) {
-                continue;
-            }
-
-            String name = file.getName();
-            if (TARGET_FOLDER_NAMES.contains(name)) {
-                return file;
-            }
-        }
-
-        return null;
-    }
-
-    public static String resolveRelativeRemotePath(Path root, Path file) {
-        Path relPath = root.relativize(file);
-        return relPath.toString().replace(File.separatorChar, '/');
-    }
-
-    public static SshClient setupTestClient(Class<?> anchor) {
-        SshClient client = SshClient.setUpDefaultClient();
-        client.setServerKeyVerifier(AcceptAllServerKeyVerifier.INSTANCE);
-        client.setHostConfigEntryResolver(HostConfigEntryResolver.EMPTY);
-        client.setKeyPairProvider(KeyPairProvider.EMPTY_KEYPAIR_PROVIDER);
-        return client;
-    }
-
-    public static SshServer setupTestServer(Class<?> anchor) {
-        SshServer sshd = SshServer.setUpDefaultServer();
-        sshd.setKeyPairProvider(createTestHostKeyProvider(anchor));
-        sshd.setPasswordAuthenticator(BogusPasswordAuthenticator.INSTANCE);
-        sshd.setPublickeyAuthenticator(AcceptAllPublickeyAuthenticator.INSTANCE);
-        sshd.setShellFactory(EchoShellFactory.INSTANCE);
-        sshd.setCommandFactory(UnknownCommandFactory.INSTANCE);
-        return sshd;
-    }
-
-    /**
-     * @param path The {@link Path} to write the data to
-     * @param data The data to write (as UTF-8)
-     * @return The UTF-8 data bytes
-     * @throws IOException If failed to write
-     */
-    public static byte[] writeFile(Path path, String data) throws IOException {
-        try (OutputStream fos = Files.newOutputStream(path, IoUtils.EMPTY_OPEN_OPTIONS)) {
-            byte[] bytes = data.getBytes(StandardCharsets.UTF_8);
-            fos.write(bytes);
-            return bytes;
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/client/config/hosts/testReadGlobalHostsConfigEntries.config.txt
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/client/config/hosts/testReadGlobalHostsConfigEntries.config.txt b/sshd-core/src/test/resources/org/apache/sshd/client/config/hosts/testReadGlobalHostsConfigEntries.config.txt
deleted file mode 100644
index 5f772cd..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/client/config/hosts/testReadGlobalHostsConfigEntries.config.txt
+++ /dev/null
@@ -1,22 +0,0 @@
-# Global section first
-
-    User global
-    Port 7365
-    IdentityFile ~/.ssh/github.key
-    HostName 37.77.34.7
-
-# User override
-Host *.apache.org
-    User mina-sshd
-
-# Port override
-Host *.github.com
-    Port 443
-
-# Host name override
-Host 10.*.*.*
-    HostName 7.3.6.5
-
-# Identity override
-Host 192.168.*.*
-    IdentityFile ~/.ssh/internal.key

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/client/config/hosts/testReadMultipleHostPatterns.config.txt
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/client/config/hosts/testReadMultipleHostPatterns.config.txt b/sshd-core/src/test/resources/org/apache/sshd/client/config/hosts/testReadMultipleHostPatterns.config.txt
deleted file mode 100644
index 0f5edcb..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/client/config/hosts/testReadMultipleHostPatterns.config.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-# Same configuration duplicated in multiple configuration entries
-Host *.github.com *.apache.org 10.*.*.*
-    User mina-sshd
-    Port 443
-    HostName 7.3.6.5

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/client/config/hosts/testReadSimpleHostsConfigEntries.config.txt
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/client/config/hosts/testReadSimpleHostsConfigEntries.config.txt b/sshd-core/src/test/resources/org/apache/sshd/client/config/hosts/testReadSimpleHostsConfigEntries.config.txt
deleted file mode 100644
index 1058777..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/client/config/hosts/testReadSimpleHostsConfigEntries.config.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-# Simple configuration file
-Host *.github.com
-    User mina-sshd
-    Port 443
-    IdentityFile ~/.ssh/github.key
-
-Host *.apache.org
-    User mina-sshd

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/client/config/keys/id_dsa
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/client/config/keys/id_dsa b/sshd-core/src/test/resources/org/apache/sshd/client/config/keys/id_dsa
deleted file mode 100644
index 1d3ef24..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/client/config/keys/id_dsa
+++ /dev/null
@@ -1,12 +0,0 @@
------BEGIN DSA PRIVATE KEY-----
-MIIBvAIBAAKBgQDIPyMbBuQcZxeYDOyCqqkdK37cWQvp+RpWzvieB/oiG/ykfDQX
-oZMRtwqwWTBfejNitbBBmC6G/t5OK+9aFmj7pfJ+a7fZKXfiUquIg9soDsoOindf
-2AwR6MZ3os8uiP2xrC8IQAClnETa15mFShs4a4b2VjddgCQ6tphnY97MywIVAPtr
-YyW11RIXsVTf/9KlbhYaNlt5AoGAX9JzbHykC/0xDKOyKU6xDIOVdEZ0ooAl9Psl
-BEUuNhlv2XgmQScO6C9l2W7gbbut7zIw4FaZ2/dgXa3D4IyexBVug5XMnrssErZo
-NcoF5g0dgEAsb9Hl9gkIK3VHM5kWteeUg1VE700JTtSMisdL8CgIdR+xN8iVH5Ew
-CbLWxmECgYEAtv+cdRfNevYFkp55jVqazc8zRLvfb64jzgc5oSJVc64kFs4yx+ab
-YpGX9WxNxDlG6g2WiY8voDBB0YnUJsn0kVRjBKX9OceROxrfT4K4dVbQZsdt+SLa
-XWL4lGJFrFZL3LZqvySvq6xfhJfakQDDivW4hUOhFPXPHrE5/Ia3T7ACFQCE6flG
-nmVCAbzo9YsbdJWBnxMnBA==
------END DSA PRIVATE KEY-----
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/client/config/keys/id_ecdsa
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/client/config/keys/id_ecdsa b/sshd-core/src/test/resources/org/apache/sshd/client/config/keys/id_ecdsa
deleted file mode 100644
index 31b1268..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/client/config/keys/id_ecdsa
+++ /dev/null
@@ -1,5 +0,0 @@
------BEGIN EC PRIVATE KEY-----
-MHcCAQEEIPKmiQzAASg656IP4PuuElLdLdO/MIXrGxQG6tGkKZ1HoAoGCCqGSM49
-AwEHoUQDQgAEobHtw9wkL332ep9fi8Gw5g8sEGwslNonPUCDR6YUZ9mjOehliLpF
-DLHLxlIFafrVM+LIpagjpRKZcnpGPWQDnA==
------END EC PRIVATE KEY-----

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/client/config/keys/id_rsa_key
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/client/config/keys/id_rsa_key b/sshd-core/src/test/resources/org/apache/sshd/client/config/keys/id_rsa_key
deleted file mode 100644
index afc6aa8..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/client/config/keys/id_rsa_key
+++ /dev/null
@@ -1,27 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIIEoQIBAAKCAQEAxr3N5fkt966xJINl0hH7Q6lLDRR1D0yMjcXCE5roE9VFut2c
-tGFuo90TCOxkPOMnwzwConeyScVF4ConZeWsxbG9VtRh61IeZ6R5P5ZTvE9xPdZB
-gIEWvU1bRfrrOfSMihqF98pODspE6NoTtND2eglwSGwxcYFmpdTAmu+8qgxgGxlE
-aaCjqwdiNPZhygrH81Mv2ruolNeZkn4Bj+wFFmZTD/waN1pQaMf+SO1+kEYIYFNl
-5+8JRGuUcr8MhHHJB+gwqMTF2BSBVITJzZUiQR0TMtkK6Vbs7yt1F9hhzDzAFDwh
-V+rsfNQaOHpl3zP07qH+/99A0XG1CVcEdHqVMwIBIwKCAQALW02YHN4OJz1Siypj
-xoNi87slUaBKBF/NlkWauGUIcpZFMTwnkIn6vCz5MhRbQC4oadRDzFNUrC/g7HdH
-prlqYe2P7uEGIfMb3YNFdk3tgOHmRsHqFgFMpVWsOjlTxNTUsQ74N3Isuxnha4wY
-9f90sBULc+WRdRvO9jbkSDaqoYVKAqCFWtocL+ZWwBXWrIrsQW4PElgZ/duc5DX7
-eeJ5DXCSj9dO+1KxsWEOKaoeABEegrRVys1/shcDNPhf/p0QShKIdPcpnDUc8cny
-1bq8GSt6jEQ+tuRoSnYrY+RD+mlkHrx373Xc1a9woV+QKTThmd9TQ8gzHMHNqq0a
-7kR7AoGBAOuPOTRiKaDtQyMTEX7eeHsPNE24EgvONjNpxyQ6gKGthG5SiB0IO7mP
-r7EggbR2EY8eMCY5HjvxzxgH86n2Pqbzsr6UlQq7YTPupCm/7fPgRknu917GA20f
-1cuY8B04Jp4FIGryBmCcScX6usXXhjfAvYCWWfkSytA8gX9+b1TNAoGBANf8shbp
-wRnQfgAzw2S+xs29pdwa6Jb/xuLvHSyklmgidrK4nsVI8G+zeCqwkqkNM02sM+vR
-c8EX7+myrGf+S2V3JS3AMNXEhavrWVH0CuqFHlBjSwHZ0uKuPpWHlCnud+23AdQz
-Bf1H7tYKt5es3J/B37o4YxhAL6U9qq+ewZH/AoGBAOTURjLjA94oT9jt870SoOyS
-bVLQEYfPol3UeE8UQnEsN4Ec+UDGK2PNaNfzsTL2WjNB5aF5UJIA184znD60seQC
-raMxQFORdF5ViYekgMEFwJ+XrnlSpD4e7PGqgtqOUWZOH33VKsRADSa5DTU3xDYo
-8porp9wDoHKD64MqXYWTAoGADFeVJeF4v6WDivukw+2k9dBSntz3WRXuG5iilNY0
-evqnsnDzITdeMkTFCcDycA9iBHA9ezCKRYxW2id3kOn1rgbO7KvliI7jESNkMJGa
-OUlvck7RFgxyc1pp+ep9fr0rbKtfMLJ1Xu4q56jXSn7oCSEFeFr+WSg9PKRwJ0rm
-fV8CgYAkYOiNY8jH5lwwoPWOIPJg62zdzmRQktrU3z7Nzk5udN6JnG3g57QjkozX
-AgHARKQ2MuXW9OfOnYNhbGeavcBQmg5XUx3eL4PRFw8mFZdjpBD/kM/dfCEwEta3
-FpRlVGn0RNqVV5xxClObD/CikkDqKZG4MSj3CrO3JK33gl1Lgg==
------END RSA PRIVATE KEY-----

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-DSA-KeyPair
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-DSA-KeyPair b/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-DSA-KeyPair
deleted file mode 100644
index 217d508..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-DSA-KeyPair
+++ /dev/null
@@ -1,21 +0,0 @@
------BEGIN OPENSSH PRIVATE KEY-----
-b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABsQAAAAdzc2gtZH
-NzAAAAgQD3axy1MBdh4TJC+T22DKKDe7sXnDrgJRs1jBcDl/qPdQDoLlvvTd8oHggXkIzI
-6Tx2ldiJ3KADMkDg1sBSU5zenIr9wisfEQOLNqfIOOirR9Z8jULGVlDO+pPwps0P9bcOka
-JCAfABNth6Oaz3NJjfFD8ZmmWDVaN2rSi1J3mjnwAAABUA5be00MaSti06Hor265V8Ggtj
-i/0AAACAa9MU8kBJ4Z1+UsrWruVr72eckEwjOPcRFeA6MoSM70GGdYVkLyQ78MMjKAFZcb
-seYT3lsja+c4LQ7yErROHSGFo+ImQnYGY3Cos2mS8tRBHtZ4YmKsBmS3Zrb4lEfauA1pgD
-pcGrUAavniKIAB/C9cpvj+IpZ36+rHpap4JKJ7kAAACAQP4NRzjtauzrDJvs73c0DPczAk
-LAan+90ouvIuI0CmMxFrSqLIIEaX3cB/P4dkKtBsMh0CxwJpoXfn+TO+1j7i08GuMONeqB
-3lOHn1/MB2qUAZh/kdU8RUsxWkrG80JMeP1kew1sHaoRfOUZ8+Xw/RL7nNmhhmHJR3rkgR
-8EoWEAAAHoMgIkejICJHoAAAAHc3NoLWRzcwAAAIEA92sctTAXYeEyQvk9tgyig3u7F5w6
-4CUbNYwXA5f6j3UA6C5b703fKB4IF5CMyOk8dpXYidygAzJA4NbAUlOc3pyK/cIrHxEDiz
-anyDjoq0fWfI1CxlZQzvqT8KbND/W3DpGiQgHwATbYejms9zSY3xQ/GZplg1Wjdq0otSd5
-o58AAAAVAOW3tNDGkrYtOh6K9uuVfBoLY4v9AAAAgGvTFPJASeGdflLK1q7la+9nnJBMIz
-j3ERXgOjKEjO9BhnWFZC8kO/DDIygBWXG7HmE95bI2vnOC0O8hK0Th0hhaPiJkJ2BmNwqL
-NpkvLUQR7WeGJirAZkt2a2+JRH2rgNaYA6XBq1AGr54iiAAfwvXKb4/iKWd+vqx6WqeCSi
-e5AAAAgED+DUc47Wrs6wyb7O93NAz3MwJCwGp/vdKLryLiNApjMRa0qiyCBGl93Afz+HZC
-rQbDIdAscCaaF35/kzvtY+4tPBrjDjXqgd5Th59fzAdqlAGYf5HVPEVLMVpKxvNCTHj9ZH
-sNbB2qEXzlGfPl8P0S+5zZoYZhyUd65IEfBKFhAAAAFGL9BNpShiQnNZ1noW8Woxyq1GuH
-AAAADnJvb3RAdWJ1bnR1LTE1AQIDBAU=
------END OPENSSH PRIVATE KEY-----
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-DSA-KeyPair.pub
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-DSA-KeyPair.pub b/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-DSA-KeyPair.pub
deleted file mode 100644
index c3feece..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-DSA-KeyPair.pub
+++ /dev/null
@@ -1 +0,0 @@
-ssh-dss AAAAB3NzaC1kc3MAAACBAPdrHLUwF2HhMkL5PbYMooN7uxecOuAlGzWMFwOX+o91AOguW+9N3ygeCBeQjMjpPHaV2IncoAMyQODWwFJTnN6civ3CKx8RA4s2p8g46KtH1nyNQsZWUM76k/CmzQ/1tw6RokIB8AE22Ho5rPc0mN8UPxmaZYNVo3atKLUneaOfAAAAFQDlt7TQxpK2LToeivbrlXwaC2OL/QAAAIBr0xTyQEnhnX5Sytau5WvvZ5yQTCM49xEV4DoyhIzvQYZ1hWQvJDvwwyMoAVlxux5hPeWyNr5zgtDvIStE4dIYWj4iZCdgZjcKizaZLy1EEe1nhiYqwGZLdmtviUR9q4DWmAOlwatQBq+eIogAH8L1ym+P4ilnfr6selqngkonuQAAAIBA/g1HOO1q7OsMm+zvdzQM9zMCQsBqf73Si68i4jQKYzEWtKosggRpfdwH8/h2Qq0GwyHQLHAmmhd+f5M77WPuLTwa4w416oHeU4efX8wHapQBmH+R1TxFSzFaSsbzQkx4/WR7DWwdqhF85Rnz5fD9Evuc2aGGYclHeuSBHwShYQ== root@ubuntu-15
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ECDSA-KeyPair
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ECDSA-KeyPair b/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ECDSA-KeyPair
deleted file mode 100644
index 403ff40..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ECDSA-KeyPair
+++ /dev/null
@@ -1,12 +0,0 @@
------BEGIN OPENSSH PRIVATE KEY-----
-b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAArAAAABNlY2RzYS
-1zaGEyLW5pc3RwNTIxAAAACG5pc3RwNTIxAAAAhQQAGRPv5eCmd3jFTCWrioWVHgQhHn/d
-ir8nriiEonZDPP+hEjX1AiYyahfvFWoqKI4lKRzoEmF5Wk6ct+9LM0JFGcEAck7Z3J/NXt
-CnHeEvnusHMoANjhKLExBURROOOTGziyHMuGBMBIgRFnf4rBhiTzduexJnaMglyqxIrDpG
-hOxwhQAAAAEQxPcsaMT3LGgAAAATZWNkc2Etc2hhMi1uaXN0cDUyMQAAAAhuaXN0cDUyMQ
-AAAIUEABkT7+Xgpnd4xUwlq4qFlR4EIR5/3Yq/J64ohKJ2Qzz/oRI19QImMmoX7xVqKiiO
-JSkc6BJheVpOnLfvSzNCRRnBAHJO2dyfzV7Qpx3hL57rBzKADY4SixMQVEUTjjkxs4shzL
-hgTASIERZ3+KwYYk83bnsSZ2jIJcqsSKw6RoTscIUAAAAAQQ+HCwVtvFlnRydGXZ+xpyKM
-KxDp5h7YMg5/dpRFrp3qNonm5/RHoT2Hw9i5GZtrXT2xPiR69wLOzTb4pnIWlENfAAAADn
-Jvb3RAdWJ1bnR1LTE1AQIDBAU=
------END OPENSSH PRIVATE KEY-----
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ECDSA-KeyPair.pub
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ECDSA-KeyPair.pub b/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ECDSA-KeyPair.pub
deleted file mode 100644
index bd43737..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ECDSA-KeyPair.pub
+++ /dev/null
@@ -1 +0,0 @@
-ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAAZE+/l4KZ3eMVMJauKhZUeBCEef92KvyeuKISidkM8/6ESNfUCJjJqF+8VaioojiUpHOgSYXlaTpy370szQkUZwQByTtncn81e0Kcd4S+e6wcygA2OEosTEFRFE445MbOLIcy4YEwEiBEWd/isGGJPN257EmdoyCXKrEisOkaE7HCFAA== root@ubuntu-15

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ED25519-KeyPair
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ED25519-KeyPair b/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ED25519-KeyPair
deleted file mode 100644
index 3176f3b..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ED25519-KeyPair
+++ /dev/null
@@ -1,7 +0,0 @@
------BEGIN OPENSSH PRIVATE KEY-----
-b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
-QyNTUxOQAAACB0Tsaj0scs8fYKMlnz2Mncky545NoICP9eGMGIpo5G3QAAAJjCVtyJwlbc
-iQAAAAtzc2gtZWQyNTUxOQAAACB0Tsaj0scs8fYKMlnz2Mncky545NoICP9eGMGIpo5G3Q
-AAAEDjQpuV2OWHZVy7R09w6bw2DnBa1UdZrsAmQ7dPyxasx3ROxqPSxyzx9goyWfPYydyT
-Lnjk2ggI/14YwYimjkbdAAAADnJvb3RAdWJ1bnR1LTE1AQIDBAUGBw==
------END OPENSSH PRIVATE KEY-----
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ED25519-KeyPair.pub
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ED25519-KeyPair.pub b/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ED25519-KeyPair.pub
deleted file mode 100644
index 128e883..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ED25519-KeyPair.pub
+++ /dev/null
@@ -1 +0,0 @@
-ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHROxqPSxyzx9goyWfPYydyTLnjk2ggI/14YwYimjkbd root@ubuntu-15
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-RSA-KeyPair
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-RSA-KeyPair b/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-RSA-KeyPair
deleted file mode 100644
index 84ee354..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-RSA-KeyPair
+++ /dev/null
@@ -1,49 +0,0 @@
------BEGIN OPENSSH PRIVATE KEY-----
-b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAACFwAAAAdzc2gtcn
-NhAAAAAwEAAQAAAgEArsY72FUUnK8yLqaY3WkotvMFm2aryoVVPbaHiXMqmKTTQPBFshwe
-Rk+wEJBE7th4fIq2MuTu+hMxEQ8+oYJ47Ka1+F96QE1F0uu1Tuec/ZpeuEiXsTBApWZ5Sa
-AMqQ3gMflfqgp96ipV3SOpLyEG9GIqVMJYNE/ggwIV7Uc4ybC21Zy3I7QOfHho8BeaMjV/
-703rlu2UWs8Twjo7VMWLvnGMUjNMzNGql9WouLEzHshmkUZBRdKvnTjsT9vx1q5OycaH/g
-Hos23EbNzn50rbiIm93PcZ/otGBjjfbHcsRmjxZhqCzLss5Nr93PLIePN8Cez91S7aLjCw
-Ri4ugKxK0EZwNHnHPi0yR0eI1eMfJLttdYnrOZiSxEVbCsZiDfTybuBYUDaH2EtgmzH9N2
-5IZeJOjrjE68NZhiwXnaIoHvfpkPTkWMSGLsLY3Kg0dYwUbAy9ErqkS/MEMmN70OxIxqTI
-hFSZ0bv3FBbZTZnl/GAEWmV+KSjURKuJlS9EBYE4ZvzYhGi6K7g3IQJZwrcqy6s8u24r5u
-qAQCihfuWFabq4nSYB1zczQ7Qq+V8wemak6lgoFGq4f58k3FUhkJbwQRFJoQYvaMI63dls
-9q67bkocjycsGPvWDU7IE9dVs36z7FevlQjsoHwAr4+0T1PFsqHn++KJ0p3AlopIV7ObS0
-cAAAdIyEoveMhKL3gAAAAHc3NoLXJzYQAAAgEArsY72FUUnK8yLqaY3WkotvMFm2aryoVV
-PbaHiXMqmKTTQPBFshweRk+wEJBE7th4fIq2MuTu+hMxEQ8+oYJ47Ka1+F96QE1F0uu1Tu
-ec/ZpeuEiXsTBApWZ5SaAMqQ3gMflfqgp96ipV3SOpLyEG9GIqVMJYNE/ggwIV7Uc4ybC2
-1Zy3I7QOfHho8BeaMjV/703rlu2UWs8Twjo7VMWLvnGMUjNMzNGql9WouLEzHshmkUZBRd
-KvnTjsT9vx1q5OycaH/gHos23EbNzn50rbiIm93PcZ/otGBjjfbHcsRmjxZhqCzLss5Nr9
-3PLIePN8Cez91S7aLjCwRi4ugKxK0EZwNHnHPi0yR0eI1eMfJLttdYnrOZiSxEVbCsZiDf
-TybuBYUDaH2EtgmzH9N25IZeJOjrjE68NZhiwXnaIoHvfpkPTkWMSGLsLY3Kg0dYwUbAy9
-ErqkS/MEMmN70OxIxqTIhFSZ0bv3FBbZTZnl/GAEWmV+KSjURKuJlS9EBYE4ZvzYhGi6K7
-g3IQJZwrcqy6s8u24r5uqAQCihfuWFabq4nSYB1zczQ7Qq+V8wemak6lgoFGq4f58k3FUh
-kJbwQRFJoQYvaMI63dls9q67bkocjycsGPvWDU7IE9dVs36z7FevlQjsoHwAr4+0T1PFsq
-Hn++KJ0p3AlopIV7ObS0cAAAADAQABAAACAEC2ca42Qn2JeSFA2lUQ/NxQu4DA0VcZa2iA
-T0sbc2g1j2r6DDRYqPULyHs5j+yIBoTGr5PEpJ+/v2k7pcsTjkbBq5sdbxyj//iLAgUHSV
-+1auD9L/2Rij4z5TDXrBhkZODcHwnM/LZZmpVZAOnMZtDSEC52D0a/VLfta8UBnAtB/VC2
-yDDWGI8J3oEXnCZ/HLG0vkiSC15cUkqWWSWYR5fqXzSIfiVQdMPnfqN0KSN6vE+KHd0Gl7
-O1QfqHy9HblJytf+kTN+BIAnT0joFbfFHf/ob0j5FqbDEdmcyFi8U+UjOmGUxI+eR7GYhX
-9NakAQeD0rC+ulZj/MDflKRcJw6+H/wHn2pdhahZY1A644SFin4SliFKl57njRkY/psjSg
-jpP0Nneg4+YFyFk9iUnmzxStJwuGE48I5eRESTFKgaMce5IVGOpz/YchQ9AL+ll2Qy6JfN
-nhpjuZD6Zh5KwsuLtCbaGfu8K8bbMEZg8F7jU6UdbpLmRLroKt6yFhffEp/tRrsX2hFoMt
-qCMpTEX2G+EzItPfFzq3/bFtnMxHmDiu4P8J03ALza/0OSIa4bdqMYG+97VBMbMeT6U76Q
-iYoDJH5/cdCBO/Cx+elM7oyFOFaV928h+6p01LGywcQdsgFPDyHSjXnF9t1fnLN4vhz5vU
-9QiVY7ldr+3PfxxpnRAAABAQDI87mazAnKsSJtkl70etg7vexltSxqkbhV/Hd5TYVMn19v
-U6rIjCY7q2xlHaUJdDNggwMpU679amkRP5BFPk9G4u8Xf5MyyDQpv+Hgpl7H0B1qEeJsBs
-huxURrX3EhLmb3300onEZXvlRwKOelIoVx5RZLUPhY0SbjBgH4sbO9NlYzs8xp4nLyK/jq
-DsqzmkkoQvK8zMybs3JwtGmTjDHvzmgcBgpm+s9gXyDJn3xe4CBW7v92N5/7eUehdXwomL
-4eDs/gR9QRb71bBSv9UaF5nK/TGJlysiM71JhBa2lmXlWg7KhALAu3SdTxxdvw0oyY+q6F
-E8zC0ljNMId8imVoAAABAQDi0m1WAW02b/IqwsJz0CAJXGpvhnhTSak3muZhTM0LPoavIe
-ow9KYL5Or4kQvbQXraYcaaRTp1Q9yJOrnXszAUWCGzqe38bLsI/SoRynLQaGEhdy1gr4tP
-WFKw0M34AshzyauEwHAVJIm6KZW/LgHsrxSXPFAABNQkQtB4olokNgSb/bysgo06OkkMBr
-N8mfY40m90RjTIvg3Cwei7xmna67MO1Bxzis8c7qQlf4hshFwnp+ixeLuhteYG1/DGxkfz
-yZ4UYXVKiuq3Xq+jFpYbKJeU17HL5TQFX34KCMN0BsMCcHLJ222IZkvFZj50715Z+jh/Vp
-xa6et++PX26BnVAAABAQDFQc26F12qnGA4heqa7Ig9WspGLEB4x5+cANOKaT/LtUFYWaYd
-O7Meeijkk+C1XcoOMac62QysRmjk8JEWdxgLiCFuKLp+6vxZLmEXBrBF0guHGv4/HlbpYn
-CxpMenxr0dhhwd2Flr1VtiadzEAfnc6MZE4byU8vVssYXORpVYIU46uI2OzIalnBhTXJYW
-KuamE/R+O1+dqnGgevQqUBkA5p4k8w+RMEY3hvSuNd6TFy5uoLXkCyOOpo60fGFVqMZyvJ
-IgU1et0umOA/JHrGzKHAl7WibRo4kf5MrPRLiDeDu92mqpwOl9NQjuTMR0OdZcXwOdrlG+
-daAgz5LetOKrAAAADnJvb3RAdWJ1bnR1LTE1AQIDBA==
------END OPENSSH PRIVATE KEY-----
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-RSA-KeyPair.pub
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-RSA-KeyPair.pub b/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-RSA-KeyPair.pub
deleted file mode 100644
index c59321c..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-RSA-KeyPair.pub
+++ /dev/null
@@ -1 +0,0 @@
-ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCuxjvYVRScrzIuppjdaSi28wWbZqvKhVU9toeJcyqYpNNA8EWyHB5GT7AQkETu2Hh8irYy5O76EzERDz6hgnjsprX4X3pATUXS67VO55z9ml64SJexMEClZnlJoAypDeAx+V+qCn3qKlXdI6kvIQb0YipUwlg0T+CDAhXtRzjJsLbVnLcjtA58eGjwF5oyNX/vTeuW7ZRazxPCOjtUxYu+cYxSM0zM0aqX1ai4sTMeyGaRRkFF0q+dOOxP2/HWrk7Jxof+AeizbcRs3OfnStuIib3c9xn+i0YGON9sdyxGaPFmGoLMuyzk2v3c8sh483wJ7P3VLtouMLBGLi6ArErQRnA0ecc+LTJHR4jV4x8ku211ies5mJLERVsKxmIN9PJu4FhQNofYS2CbMf03bkhl4k6OuMTrw1mGLBedoige9+mQ9ORYxIYuwtjcqDR1jBRsDL0SuqRL8wQyY3vQ7EjGpMiEVJnRu/cUFtlNmeX8YARaZX4pKNREq4mVL0QFgThm/NiEaLoruDchAlnCtyrLqzy7bivm6oBAKKF+5YVpuridJgHXNzNDtCr5XzB6ZqTqWCgUarh/nyTcVSGQlvBBEUmhBi9owjrd2Wz2rrtuShyPJywY+9YNTsgT11WzfrPsV6+VCOygfACvj7RPU8Wyoef74onSncCWikhXs5tLRw== root@ubuntu-15

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/util/EDDSAProviderTest-EDDSA-OpenSSH-KeyPair
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/util/EDDSAProviderTest-EDDSA-OpenSSH-KeyPair b/sshd-core/src/test/resources/org/apache/sshd/common/util/EDDSAProviderTest-EDDSA-OpenSSH-KeyPair
deleted file mode 100644
index 59af379..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/util/EDDSAProviderTest-EDDSA-OpenSSH-KeyPair
+++ /dev/null
@@ -1,7 +0,0 @@
------BEGIN OPENSSH PRIVATE KEY-----
-b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
-QyNTUxOQAAACB0Tsaj0scs8fYKMlnz2Mncky545NoICP9eGMGIpo5G3QAAAJjCVtyJwlbc
-iQAAAAtzc2gtZWQyNTUxOQAAACB0Tsaj0scs8fYKMlnz2Mncky545NoICP9eGMGIpo5G3Q
-AAAEDjQpuV2OWHZVy7R09w6bw2DnBa1UdZrsAmQ7dPyxasx3ROxqPSxyzx9goyWfPYydyT
-Lnjk2ggI/14YwYimjkbdAAAADnJvb3RAdWJ1bnR1LTE1AQIDBAUGBw==
------END OPENSSH PRIVATE KEY-----

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/util/EDDSAProviderTest-EDDSA-OpenSSH-KeyPair.pub
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/util/EDDSAProviderTest-EDDSA-OpenSSH-KeyPair.pub b/sshd-core/src/test/resources/org/apache/sshd/common/util/EDDSAProviderTest-EDDSA-OpenSSH-KeyPair.pub
deleted file mode 100644
index 128e883..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/util/EDDSAProviderTest-EDDSA-OpenSSH-KeyPair.pub
+++ /dev/null
@@ -1 +0,0 @@
-ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHROxqPSxyzx9goyWfPYydyTLnjk2ggI/14YwYimjkbd root@ubuntu-15
\ No newline at end of file


[40/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common utilities code from sshd-core into sshd-common (new artifact)

Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/EventListenerUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/EventListenerUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/util/EventListenerUtils.java
new file mode 100644
index 0000000..1620bad
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/EventListenerUtils.java
@@ -0,0 +1,212 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util;
+
+import java.lang.reflect.Proxy;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.EventListener;
+import java.util.Objects;
+import java.util.Set;
+import java.util.TreeSet;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class EventListenerUtils {
+    /**
+     * A special &quot;comparator&quot; whose only purpose is to ensure
+     * there are no same references in a listener's set - to be used
+     * in conjunction with a {@code TreeSet} as its comparator
+     */
+    @SuppressWarnings("checkstyle:anoninnerlength")
+    public static final Comparator<EventListener> LISTENER_INSTANCE_COMPARATOR = (l1, l2) -> {
+        if (l1 == l2) {
+            return 0;
+        } else if (l1 == null) {
+            return 1;
+        } else if (l2 == null) {
+            return -1;
+        }
+
+        Class<?> c1 = l1.getClass();
+        Class<?> c2 = l2.getClass();
+        boolean checkHashCodes = true;
+        if (Proxy.isProxyClass(c1)) {
+            if (Proxy.isProxyClass(c2)) {
+                checkHashCodes = false; // cannot call hashCode on a proxy
+            } else {
+                return 1;
+            }
+        } else if (Proxy.isProxyClass(c2)) {
+            return -1;
+        }
+
+        if (checkHashCodes) {
+            int nRes = Integer.compare(l1.hashCode(), l2.hashCode());
+            if (nRes != 0) {
+                return nRes;
+            }
+        }
+
+        int nRes = Integer.compare(System.identityHashCode(l1), System.identityHashCode(l2));
+        if (nRes != 0) {
+            return nRes;
+        }
+
+        if (c1 != c2) {
+            return c1.getName().compareTo(c2.getName());
+        }
+
+        String s1 = Objects.toString(l1.toString(), "");
+        String s2 = Objects.toString(l2.toString(), "");
+        nRes = s1.compareTo(s2);
+        if (nRes != 0) {
+            return nRes;
+        }
+        throw new UnsupportedOperationException("Ran out of options to compare instance of " + s1 + " vs. " + s2);
+    };
+
+    private EventListenerUtils() {
+        throw new UnsupportedOperationException("No instance");
+    }
+
+    /**
+     * @param <L> Type of {@link SshdEventListener} contained in the set
+     * @param listeners The listeners to pre-add to the create set - ignored
+     * if (@code null}/empty
+     * @return A (synchronized) {@link Set} for containing the listeners ensuring
+     * that if same listener instance is added repeatedly only <U>one</U>
+     * instance is actually contained
+     */
+    public static <L extends SshdEventListener> Set<L> synchronizedListenersSet(Collection<? extends L> listeners) {
+        Set<L> s = EventListenerUtils.synchronizedListenersSet();
+        if (GenericUtils.size(listeners) > 0) {
+            s.addAll(listeners);
+        }
+
+        return s;
+    }
+
+    /**
+     * @param <L> Type of {@link SshdEventListener} contained in the set
+     * @return A (synchronized) {@link Set} for containing the listeners ensuring
+     * that if same listener instance is added repeatedly only <U>one</U>
+     * instance is actually contained
+     * @see #LISTENER_INSTANCE_COMPARATOR
+     */
+    public static <L extends SshdEventListener> Set<L> synchronizedListenersSet() {
+        return Collections.synchronizedSet(new TreeSet<L>(LISTENER_INSTANCE_COMPARATOR));
+    }
+
+    /**
+     * Provides proxy wrapper around an {@link Iterable} container of listener
+     * interface implementation. <b>Note:</b> a listener interface is one whose
+     * invoked methods return <u>only</u> {@code void}.
+     *
+     * @param <T>          Generic listener type
+     * @param listenerType The expected listener <u>interface</u>
+     * @param listeners    An {@link Iterable} container of listeners to be invoked.
+     *                     <p>
+     *                     <b>Note(s):</b>
+     *                     </p>
+     *                     <ul>
+     *                     <li><p>
+     *                     The invocation order is same as the {@link Iterable} container
+     *                     </p></li>
+     *
+     *                     <li><p>
+     *                     If any of the invoked listener methods throws an exception, the
+     *                     rest of the listener are <u>not</u> invoked and the exception is
+     *                     propagated to the caller
+     *                     </p></li>
+     *
+     *                     <li><p>
+     *                     It is up to the <u>caller</u> to ensure that the container does
+     *                     not change while the proxy is invoked
+     *                     </p></li>
+     *                     </ul>
+     * @return A proxy wrapper implementing the same interface, but delegating
+     * the calls to the container
+     * @see #proxyWrapper(Class, ClassLoader, Iterable)
+     */
+    public static <T extends SshdEventListener> T proxyWrapper(Class<T> listenerType, Iterable<? extends T> listeners) {
+        return proxyWrapper(listenerType, listenerType.getClassLoader(), listeners);
+    }
+
+    /**
+     * Provides proxy wrapper around an {@link Iterable} container of listener
+     * interface implementation. <b>Note:</b> a listener interface is one whose
+     * invoked methods return <u>only</u> {@code void}.
+     *
+     * @param <T>          Generic {@link SshdEventListener} type
+     * @param listenerType The expected listener <u>interface</u>
+     * @param loader       The {@link ClassLoader} to use for the proxy
+     * @param listeners    An {@link Iterable} container of listeners to be invoked.
+     *                     <p>
+     *                     <b>Note(s):</b>
+     *                     </p>
+     *                     <ul>
+     *                     <li><p>
+     *                     The invocation order is same as the {@link Iterable} container
+     *                     </p></li>
+     *
+     *                     <li><p>
+     *                     If any of the invoked listener methods throws an exception, the
+     *                     rest of the listener are <u>not</u> invoked and the exception is
+     *                     propagated to the caller
+     *                     </p></li>
+     *
+     *                     <li><p>
+     *                     It is up to the <u>caller</u> to ensure that the container does
+     *                     not change while the proxy is invoked
+     *                     </p></li>
+     *                     </ul>
+     * @return A proxy wrapper implementing the same interface, but delegating
+     * the calls to the container
+     * @throws IllegalArgumentException if <tt>listenerType</tt> is not an interface
+     *                                  or a {@code null} container has been provided
+     * @see #proxyWrapper(Class, ClassLoader, Iterable)
+     */
+    public static <T extends SshdEventListener> T proxyWrapper(Class<T> listenerType, ClassLoader loader, final Iterable<? extends T> listeners) {
+        Objects.requireNonNull(listenerType, "No listener type specified");
+        ValidateUtils.checkTrue(listenerType.isInterface(), "Target proxy is not an interface: %s", listenerType.getSimpleName());
+        Objects.requireNonNull(listeners, "No listeners container provided");
+
+        Object wrapper = Proxy.newProxyInstance(loader, new Class<?>[]{listenerType}, (proxy, method, args) -> {
+            Throwable err = null;
+            for (T l : listeners) {
+                try {
+                    method.invoke(l, args);
+                } catch (Throwable t) {
+                    Throwable e = GenericUtils.peelException(t);
+                    err = GenericUtils.accumulateException(err, e);
+                }
+            }
+
+            if (err != null) {
+                throw err;
+            }
+
+            return null;    // we assume always void return value...
+        });
+        return listenerType.cast(wrapper);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/EventNotifier.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/EventNotifier.java b/sshd-common/src/main/java/org/apache/sshd/common/util/EventNotifier.java
new file mode 100644
index 0000000..c041f1b
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/EventNotifier.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util;
+
+/**
+ * Notify about the occurrence of an event
+ *
+ * @param <E> type of event being notified
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FunctionalInterface
+public interface EventNotifier<E> {
+    /**
+     * @param event The event
+     * @throws Exception If failed to process the event notification
+     */
+    void notifyEvent(E event) throws Exception;
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/GenericUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/GenericUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/util/GenericUtils.java
new file mode 100644
index 0000000..c924d1e
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/GenericUtils.java
@@ -0,0 +1,915 @@
+/*
+ * 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.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.UndeclaredThrowableException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.NavigableMap;
+import java.util.NavigableSet;
+import java.util.Objects;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.concurrent.ExecutionException;
+import java.util.function.BinaryOperator;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+import java.util.stream.Collector;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+
+import javax.management.MBeanException;
+import javax.management.ReflectionException;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class GenericUtils {
+
+    public static final byte[] EMPTY_BYTE_ARRAY = {};
+    public static final char[] EMPTY_CHAR_ARRAY = {};
+    public static final String[] EMPTY_STRING_ARRAY = {};
+    public static final Object[] EMPTY_OBJECT_ARRAY = {};
+
+    /**
+     * A value indicating a {@code null} value - to be used as a placeholder
+     * where {@code null}s are not allowed
+     */
+    public static final Object NULL = new Object();
+
+    /**
+     * The complement of {@link String#CASE_INSENSITIVE_ORDER}
+     */
+    public static final Comparator<String> CASE_SENSITIVE_ORDER = (s1, s2) -> {
+        if (s1 == s2) {
+            return 0;
+        } else {
+            return s1.compareTo(s2);
+        }
+    };
+
+    public static final String QUOTES = "\"'";
+
+    @SuppressWarnings("rawtypes")
+    private static final Supplier CASE_INSENSITIVE_MAP_FACTORY = () -> new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+
+    private GenericUtils() {
+        throw new UnsupportedOperationException("No instance");
+    }
+
+    public static String trimToEmpty(String s) {
+        if (s == null) {
+            return "";
+        } else {
+            return s.trim();
+        }
+    }
+
+    public static String replaceWhitespaceAndTrim(String s) {
+        if (s != null) {
+            s = s.replace('\t', ' ');
+        }
+
+        return trimToEmpty(s);
+    }
+
+    /**
+     * @param s The {@link String} value to calculate the hash code on - may
+     * be {@code null}/empty in which case a value of zero is returned
+     * @return The calculated hash code
+     * @see #hashCode(String, Boolean)
+     */
+    public static int hashCode(String s) {
+        return hashCode(s, null);
+    }
+
+    /**
+     * @param s The {@link String} value to calculate the hash code on - may
+     * be {@code null}/empty in which case a value of zero is returned
+     * @param useUppercase Whether to convert the string to uppercase, lowercase
+     * or not at all:
+     * <UL>
+     *      <LI>{@code null} - no conversion</LI>
+     *      <LI>{@link Boolean#TRUE} - get hash code of uppercase</LI>
+     *      <LI>{@link Boolean#FALSE} - get hash code of lowercase</LI>
+     * </UL>
+     * @return The calculated hash code
+     */
+    public static int hashCode(String s, Boolean useUppercase) {
+        if (isEmpty(s)) {
+            return 0;
+        } else if (useUppercase == null) {
+            return s.hashCode();
+        } else if (useUppercase.booleanValue()) {
+            return s.toUpperCase().hashCode();
+        } else {
+            return s.toLowerCase().hashCode();
+        }
+    }
+
+    public static int safeCompare(String s1, String s2, boolean caseSensitive) {
+        if (isSameReference(s1, s2)) {
+            return 0;
+        } else if (s1 == null) {
+            return +1;    // push null(s) to end
+        } else if (s2 == null) {
+            return -1;    // push null(s) to end
+        } else if (caseSensitive) {
+            return s1.compareTo(s2);
+        } else {
+            return s1.compareToIgnoreCase(s2);
+        }
+    }
+
+    public static <T> boolean isSameReference(T o1, T o2) {
+        return o1 == o2;
+    }
+
+    public static int length(CharSequence cs) {
+        return cs == null ? 0 : cs.length();
+    }
+
+    public static boolean isEmpty(CharSequence cs) {
+        return length(cs) <= 0;
+    }
+
+    public static boolean isNotEmpty(CharSequence cs) {
+        return !isEmpty(cs);
+    }
+
+    public static int indexOf(CharSequence cs, char c) {
+        int len = length(cs);
+        for (int pos = 0; pos < len; pos++) {
+            char ch = cs.charAt(pos);
+            if (ch == c) {
+                return pos;
+            }
+        }
+
+        return -1;
+    }
+
+    public static int lastIndexOf(CharSequence cs, char c) {
+        int len = length(cs);
+        for (int pos = len - 1; pos >= 0; pos--) {
+            char ch = cs.charAt(pos);
+            if (ch == c) {
+                return pos;
+            }
+        }
+
+        return -1;
+    }
+
+    // a List would be better, but we want to be compatible with String.split(...)
+    public static String[] split(String s, char ch) {
+        if (isEmpty(s)) {
+            return EMPTY_STRING_ARRAY;
+        }
+
+        int lastPos = 0;
+        int curPos = s.indexOf(ch);
+        if (curPos < 0) {
+            return new String[]{s};
+        }
+
+        Collection<String> values = new LinkedList<>();
+        do {
+            String v = s.substring(lastPos, curPos);
+            values.add(v);
+
+            // skip separator
+            lastPos = curPos + 1;
+            if (lastPos >= s.length()) {
+                break;
+            }
+
+            curPos = s.indexOf(ch, lastPos);
+            if (curPos < lastPos) {
+                break;  // no more separators
+            }
+        } while (curPos < s.length());
+
+        // check if any leftovers
+        if (lastPos < s.length()) {
+            String v = s.substring(lastPos);
+            values.add(v);
+        }
+
+        return values.toArray(new String[values.size()]);
+    }
+
+    public static <T> String join(T[] values, char ch) {
+        return join(isEmpty(values) ? Collections.<T>emptyList() : Arrays.asList(values), ch);
+    }
+
+    public static String join(Iterable<?> iter, char ch) {
+        return join((iter == null) ? null : iter.iterator(), ch);
+    }
+
+    public static String join(Iterator<?> iter, char ch) {
+        if ((iter == null) || (!iter.hasNext())) {
+            return "";
+        }
+
+        StringBuilder sb = new StringBuilder();
+        do {    // we already asked hasNext...
+            Object o = iter.next();
+            if (sb.length() > 0) {
+                sb.append(ch);
+            }
+            sb.append(Objects.toString(o));
+        } while (iter.hasNext());
+
+        return sb.toString();
+    }
+
+    public static <T> String join(T[] values, CharSequence sep) {
+        return join(isEmpty(values) ? Collections.<T>emptyList() : Arrays.asList(values), sep);
+    }
+
+    public static String join(Iterable<?> iter, CharSequence sep) {
+        return join((iter == null) ? null : iter.iterator(), sep);
+    }
+
+    public static String join(Iterator<?> iter, CharSequence sep) {
+        if ((iter == null) || (!iter.hasNext())) {
+            return "";
+        }
+
+        StringBuilder sb = new StringBuilder();
+        do {    // we already asked hasNext...
+            Object o = iter.next();
+            if (sb.length() > 0) {
+                sb.append(sep);
+            }
+            sb.append(Objects.toString(o));
+        } while (iter.hasNext());
+
+        return sb.toString();
+    }
+
+    public static int size(Collection<?> c) {
+        return c == null ? 0 : c.size();
+    }
+
+    public static boolean isEmpty(Collection<?> c) {
+        return (c == null) || c.isEmpty();
+    }
+
+    public static boolean isNotEmpty(Collection<?> c) {
+        return !isEmpty(c);
+    }
+
+    public static int size(Map<?, ?> m) {
+        return m == null ? 0 : m.size();
+    }
+
+    public static boolean isEmpty(Map<?, ?> m) {
+        return (m == null) || m.isEmpty();
+    }
+
+    public static boolean isNotEmpty(Map<?, ?> m) {
+        return !isEmpty(m);
+    }
+
+    @SafeVarargs
+    public static <T> int length(T... a) {
+        return a == null ? 0 : a.length;
+    }
+
+    public static <T> boolean isEmpty(Iterable<? extends T> iter) {
+        if (iter == null) {
+            return true;
+        } else if (iter instanceof Collection<?>) {
+            return isEmpty((Collection<?>) iter);
+        } else {
+            return isEmpty(iter.iterator());
+        }
+    }
+
+    public static <T> boolean isNotEmpty(Iterable<? extends T> iter) {
+        return !isEmpty(iter);
+    }
+
+    public static <T> boolean isEmpty(Iterator<? extends T> iter) {
+        return iter == null || !iter.hasNext();
+    }
+
+    public static <T> boolean isNotEmpty(Iterator<? extends T> iter) {
+        return !isEmpty(iter);
+    }
+
+    @SafeVarargs
+    public static <T> boolean isEmpty(T... a) {
+        return length(a) <= 0;
+    }
+
+    public static int length(char[] chars) {
+        return (chars == null) ? 0 : chars.length;
+    }
+
+    public static boolean isEmpty(char[] chars) {
+        return length(chars) <= 0;
+    }
+
+    /**
+     * Compares 2 character arrays - <B>Note:</B> {@code null} and empty
+     * are considered <U>equal</U>
+     *
+     * @param c1 1st array
+     * @param c2 2nd array
+     * @return Negative is 1st array comes first in lexicographical order,
+     * positive if 2nd array comes first and zero if equal
+     */
+    public static int compare(char[] c1, char[] c2) {
+        int l1 = length(c1);
+        int l2 = length(c2);
+        int cmpLen = Math.min(l1, l2);
+        for (int index = 0; index < cmpLen; index++) {
+            char c11 = c1[index];
+            char c22 = c2[index];
+            int nRes = Character.compare(c11, c22);
+            if (nRes != 0) {
+                return nRes;
+            }
+        }
+
+        int nRes = Integer.compare(l1, l2);
+        if (nRes != 0) {
+            return nRes;
+        }
+
+        return 0;
+    }
+
+    @SafeVarargs    // there is no EnumSet.of(...) so we have to provide our own
+    public static <E extends Enum<E>> Set<E> of(E... values) {
+        return of(isEmpty(values) ? Collections.emptySet() : Arrays.asList(values));
+    }
+
+    public static <E extends Enum<E>> Set<E> of(Collection<? extends E> values) {
+        if (isEmpty(values)) {
+            return Collections.emptySet();
+        }
+
+        Set<E> result = null;
+        for (E v : values) {
+            /*
+             * A trick to compensate for the fact that we do not have
+             * the enum Class to invoke EnumSet.noneOf
+             */
+            if (result == null) {
+                result = EnumSet.of(v);
+            } else {
+                result.add(v);
+            }
+        }
+
+        return result;
+    }
+
+    public static <T> boolean containsAny(Collection<? extends T> coll, Iterable<? extends T> values) {
+        if (isEmpty(coll)) {
+            return false;
+        }
+
+        for (T v : values) {
+            if (coll.contains(v)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    public static <T> void forEach(Iterable<T> values, Consumer<T> consumer) {
+        if (isNotEmpty(values)) {
+            values.forEach(consumer);
+        }
+    }
+
+    public static <T, U> List<U> map(Collection<T> values, Function<? super T, ? extends U> mapper) {
+        return stream(values).map(mapper).collect(Collectors.toList());
+    }
+
+    public static <T, U> NavigableSet<U> mapSort(
+            Collection<T> values, Function<? super T, ? extends U> mapper, Comparator<U> comparator) {
+        return stream(values).map(mapper).collect(toSortedSet(comparator));
+    }
+
+    public static <T, K, U> NavigableMap<K, U> toSortedMap(
+            Iterable<T> values, Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper, Comparator<K> comparator) {
+        return stream(values).collect(toSortedMap(keyMapper, valueMapper, comparator));
+    }
+
+    public static <T, K, U> Collector<T, ?, NavigableMap<K, U>> toSortedMap(
+            Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper, Comparator<K> comparator) {
+        return Collectors.toMap(keyMapper, valueMapper, throwingMerger(), () -> new TreeMap<>(comparator));
+    }
+
+    private static <T> BinaryOperator<T> throwingMerger() {
+        return (u, v) -> {
+            throw new IllegalStateException(String.format("Duplicate key %s", u));
+        };
+    }
+
+    public static <T> Collector<T, ?, NavigableSet<T>> toSortedSet(Comparator<T> comparator) {
+        return Collectors.toCollection(() -> new TreeSet<>(comparator));
+    }
+
+    public static <T> Stream<T> stream(Iterable<T> values) {
+        if (isEmpty(values)) {
+            return Stream.empty();
+        } else if (values instanceof Collection<?>) {
+            return ((Collection<T>) values).stream();
+        } else {
+            return StreamSupport.stream(values.spliterator(), false);
+        }
+    }
+
+    @SafeVarargs
+    public static <T> List<T> unmodifiableList(T... values) {
+        return unmodifiableList(asList(values));
+    }
+
+    public static <T> List<T> unmodifiableList(Collection<? extends T> values) {
+        if (isEmpty(values)) {
+            return Collections.emptyList();
+        } else {
+            return Collections.unmodifiableList(new ArrayList<>(values));
+        }
+    }
+
+    public static <T> List<T> unmodifiableList(Stream<T> values) {
+        return unmodifiableList(values.collect(Collectors.toList()));
+    }
+
+    @SafeVarargs
+    public static <T> List<T> asList(T... values) {
+        return isEmpty(values) ? Collections.emptyList() : Arrays.asList(values);
+    }
+
+    @SafeVarargs
+    public static <T> Set<T> asSet(T... values) {
+        return new HashSet<>(asList(values));
+    }
+
+    @SafeVarargs
+    public static <V extends Comparable<V>> NavigableSet<V> asSortedSet(V... values) {
+        return asSortedSet(Comparator.naturalOrder(), values);
+    }
+
+    public static <V extends Comparable<V>> NavigableSet<V> asSortedSet(Collection<? extends V> values) {
+        return asSortedSet(Comparator.naturalOrder(), values);
+    }
+
+    /**
+     * @param <V>    The element type
+     * @param comp   The (non-{@code null}) {@link Comparator} to use
+     * @param values The values to be added (ignored if {@code null})
+     * @return A {@link NavigableSet} containing the values (if any) sorted
+     * using the provided comparator
+     */
+    @SafeVarargs
+    public static <V> NavigableSet<V> asSortedSet(Comparator<? super V> comp, V... values) {
+        return asSortedSet(comp, isEmpty(values) ? Collections.emptyList() : Arrays.asList(values));
+    }
+
+    /**
+     * @param <V>    The element type
+     * @param comp   The (non-{@code null}) {@link Comparator} to use
+     * @param values The values to be added (ignored if {@code null}/empty)
+     * @return A {@link NavigableSet} containing the values (if any) sorted
+     * using the provided comparator
+     */
+    public static <V> NavigableSet<V> asSortedSet(Comparator<? super V> comp, Collection<? extends V> values) {
+        NavigableSet<V> set = new TreeSet<>(Objects.requireNonNull(comp, "No comparator"));
+        if (size(values) > 0) {
+            set.addAll(values);
+        }
+        return set;
+    }
+
+    /**
+     * @param <V> Type of mapped value
+     * @return A {@link Supplier} that returns a <U>new</U> {@link NavigableMap}
+     * whenever its {@code get()} method is invoked
+     */
+    @SuppressWarnings("unchecked")
+    public static <V> Supplier<NavigableMap<String, V>> caseInsensitiveMap() {
+        return CASE_INSENSITIVE_MAP_FACTORY;
+    }
+
+    /**
+     * Flips between keys and values of an input map
+     *
+     * @param <K> Original map key type
+     * @param <V> Original map value type
+     * @param <M> Flipped map type
+     * @param map The original map to flip
+     * @param mapCreator The creator of the target map
+     * @param allowDuplicates Whether to ignore duplicates on flip
+     * @return The flipped map result
+     * @throws IllegalArgumentException if <tt>allowDuplicates</tt> is {@code false}
+     * and a duplicate value found in the original map.
+     */
+    public static <K, V, M extends Map<V, K>> M flipMap(
+            Map<? extends K, ? extends V> map, Supplier<? extends M> mapCreator, boolean allowDuplicates) {
+        M result = Objects.requireNonNull(mapCreator.get(), "No map created");
+        map.forEach((key, value) -> {
+            K prev = result.put(value, key);
+            if ((prev != null) && (!allowDuplicates)) {
+                ValidateUtils.throwIllegalArgumentException("Multiple values for key=%s: current=%s, previous=%s", value, key, prev);
+            }
+        });
+
+        return result;
+    }
+
+    @SafeVarargs
+    public static <K, V, M extends Map<K, V>> M mapValues(
+            Function<? super V, ? extends K> keyMapper, Supplier<? extends M> mapCreator, V... values) {
+        return mapValues(keyMapper, mapCreator, isEmpty(values) ? Collections.emptyList() : Arrays.asList(values));
+    }
+
+    /**
+     * Creates a map out of a group of values
+     *
+     * @param <K> The key type
+     * @param <V> The value type
+     * @param <M> The result {@link Map} type
+     * @param keyMapper The {@link Function} that generates a key for a given value.
+     * If the returned key is {@code null} then the value is not mapped
+     * @param mapCreator The {@link Supplier} used to create/retrieve the result map - provided
+     * non-empty group of values
+     * @param values The values to be mapped
+     * @return The resulting {@link Map} - <B>Note:</B> no validation is made to ensure
+     * that 2 (or more) values are not mapped to the same key
+     */
+    public static <K, V, M extends Map<K, V>> M mapValues(
+            Function<? super V, ? extends K> keyMapper, Supplier<? extends M> mapCreator, Collection<? extends V> values) {
+        M map = mapCreator.get();
+        for (V v : values) {
+            K k = keyMapper.apply(v);
+            if (k == null) {
+                continue;   // debug breakpoint
+            }
+            map.put(k, v);
+        }
+
+        return map;
+    }
+
+    @SafeVarargs
+    public static <T> T findFirstMatchingMember(Predicate<? super T> acceptor, T... values) {
+        return findFirstMatchingMember(acceptor, isEmpty(values) ? Collections.emptyList() : Arrays.asList(values));
+    }
+
+    public static <T> T findFirstMatchingMember(Predicate<? super T> acceptor, Collection<? extends T> values) {
+        List<T> matches = selectMatchingMembers(acceptor, values);
+        return GenericUtils.isEmpty(matches) ? null : matches.get(0);
+    }
+
+    /**
+     * Returns a list of all the values that were accepted by a predicate
+     *
+     * @param <T> The type of value being evaluated
+     * @param acceptor The {@link Predicate} to consult whether a member is selected
+     * @param values The values to be scanned
+     * @return A {@link List} of all the values that were accepted by the predicate
+     */
+    @SafeVarargs
+    public static <T> List<T> selectMatchingMembers(Predicate<? super T> acceptor, T... values) {
+        return selectMatchingMembers(acceptor, isEmpty(values) ? Collections.emptyList() : Arrays.asList(values));
+    }
+
+    /**
+     * Returns a list of all the values that were accepted by a predicate
+     *
+     * @param <T> The type of value being evaluated
+     * @param acceptor The {@link Predicate} to consult whether a member is selected
+     * @param values The values to be scanned
+     * @return A {@link List} of all the values that were accepted by the predicate
+     */
+    public static <T> List<T> selectMatchingMembers(Predicate<? super T> acceptor, Collection<? extends T> values) {
+        return GenericUtils.stream(values)
+                .filter(acceptor)
+                .collect(Collectors.toList());
+    }
+
+    /**
+     * @param s The {@link CharSequence} to be checked
+     * @return If the sequence contains any of the {@link #QUOTES}
+     * on <U>both</U> ends, then they are stripped, otherwise
+     * nothing is done
+     * @see #stripDelimiters(CharSequence, char)
+     */
+    public static CharSequence stripQuotes(CharSequence s) {
+        if (isEmpty(s)) {
+            return s;
+        }
+
+        for (int index = 0; index < QUOTES.length(); index++) {
+            char delim = QUOTES.charAt(index);
+            CharSequence v = stripDelimiters(s, delim);
+            if (v != s) {   // if stripped one don't continue
+                return v;
+            }
+        }
+
+        return s;
+    }
+
+    /**
+     * @param s     The {@link CharSequence} to be checked
+     * @param delim The expected delimiter
+     * @return If the sequence contains the delimiter on <U>both</U> ends,
+     * then it is are stripped, otherwise nothing is done
+     */
+    public static CharSequence stripDelimiters(CharSequence s, char delim) {
+        if (isEmpty(s) || (s.length() < 2)) {
+            return s;
+        }
+
+        int lastPos = s.length() - 1;
+        if ((s.charAt(0) != delim) || (s.charAt(lastPos) != delim)) {
+            return s;
+        } else {
+            return s.subSequence(1, lastPos);
+        }
+    }
+
+    public static RuntimeException toRuntimeException(Throwable t) {
+        return toRuntimeException(t, true);
+    }
+
+    /**
+     * Converts a thrown generic exception to a {@link RuntimeException}
+     *
+     * @param t The original thrown exception
+     * @param peelThrowable Whether to determine the root cause by &quot;peeling&quot;
+     * any enclosing exceptions
+     * @return The thrown cause if already a runtime exception, otherwise a
+     * runtime exception of the resolved exception as its cause
+     * @see #peelException(Throwable)
+     */
+    public static RuntimeException toRuntimeException(Throwable t, boolean peelThrowable) {
+        Throwable e = peelThrowable ? peelException(t) : t;
+        if (e instanceof RuntimeException) {
+            return (RuntimeException) e;
+        }
+
+        return new RuntimeException(e);
+    }
+
+    /**
+     * Attempts to get to the &quot;effective&quot; exception being thrown,
+     * by taking care of some known exceptions that wrap the original thrown
+     * one.
+     *
+     * @param t The original {@link Throwable} - ignored if {@code null}
+     * @return The effective exception - same as input if not a wrapper
+     */
+    public static Throwable peelException(Throwable t) {
+        // NOTE: check order is important - e.g., InvocationTargetException extends ReflectiveOperationException
+        if (t == null) {
+            return t;
+        } else if (t instanceof UndeclaredThrowableException) {
+            Throwable wrapped = ((UndeclaredThrowableException) t).getUndeclaredThrowable();
+            // according to the Javadoc it may be null, in which case 'getCause'
+            // might contain the information we need
+            if (wrapped != null) {
+                return peelException(wrapped);
+            }
+
+            wrapped = t.getCause();
+            if (wrapped != t) {     // make sure it is a real cause
+                return peelException(wrapped);
+            }
+        } else if (t instanceof InvocationTargetException) {
+            Throwable target = ((InvocationTargetException) t).getTargetException();
+            if (target != null) {
+                return peelException(target);
+            }
+        } else if (t instanceof ReflectionException) {
+            Throwable target = ((ReflectionException) t).getTargetException();
+            if (target != null) {
+                return peelException(target);
+            }
+        } else if (t instanceof ExecutionException) {
+            Throwable wrapped = resolveExceptionCause(t);
+            if (wrapped != null) {
+                return peelException(wrapped);
+            }
+        } else if (t instanceof MBeanException) {
+            Throwable target = ((MBeanException) t).getTargetException();
+            if (target != null) {
+                return peelException(target);
+            }
+        }
+
+        return t;   // no special handling required or available
+    }
+
+    /**
+     * @param t The original {@link Throwable} - ignored if {@code null}
+     * @return If {@link Throwable#getCause()} is non-{@code null} then
+     * the cause, otherwise the original exception - {@code null} if
+     * the original exception was {@code null}
+     */
+    public static Throwable resolveExceptionCause(Throwable t) {
+        if (t == null) {
+            return t;
+        }
+
+        Throwable c = t.getCause();
+        if (c == null) {
+            return t;
+        } else {
+            return c;
+        }
+    }
+
+    /**
+     * 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 <T>     The exception type
+     * @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;
+    }
+
+    public static IOException toIOException(Throwable e) {
+        if (e instanceof IOException) {
+            return (IOException) e;
+        } else {
+            return new IOException(e);
+        }
+    }
+
+    /**
+     * Wraps a value into a {@link Supplier}
+     * @param <T> Type of value being supplied
+     * @param value The value to be supplied
+     * @return The supplier wrapper
+     */
+    public static <T> Supplier<T> supplierOf(T value) {
+        return () -> value;
+    }
+
+    /**
+     * Resolves to an always non-{@code null} iterator
+     *
+     * @param <T> Type of value being iterated
+     * @param iterable The {@link Iterable} instance
+     * @return A non-{@code null} iterator which may be empty if no iterable
+     * instance or no iterator returned from it
+     * @see #iteratorOf(Iterator)
+     */
+    public static <T> Iterator<T> iteratorOf(Iterable<T> iterable) {
+        return iteratorOf((iterable == null) ? null : iterable.iterator());
+    }
+
+    /**
+     * @param <B> Generic base class
+     * @param <D> Generic child class
+     * @return An identity {@link Function} that returns its input child class as a base class
+     */
+    public static <B, D extends B> Function<D, B> downcast() {
+        return t -> t;
+    }
+
+    /**
+     * Resolves to an always non-{@code null} iterator
+     *
+     * @param <T> Type of value being iterated
+     * @param iter The {@link Iterator} instance
+     * @return  A non-{@code null} iterator which may be empty if no iterator instance
+     * @see Collections#emptyIterator()
+     */
+    public static <T> Iterator<T> iteratorOf(Iterator<T> iter) {
+        return (iter == null) ? Collections.emptyIterator() : iter;
+    }
+
+    public static <U, V> Iterable<V> wrapIterable(Iterable<? extends U> iter, Function<? super U, ? extends V> mapper) {
+        return () -> wrapIterator(iter, mapper);
+    }
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public static <U, V> Iterator<V> wrapIterator(Iterable<? extends U> iter, Function<? super U, ? extends V> mapper) {
+        return (Iterator) stream(iter).map(mapper).iterator();
+    }
+
+    public static <U, V> Iterator<V> wrapIterator(Iterator<? extends U> iter, Function<? super U, ? extends V> mapper) {
+        Iterator<? extends U> iterator = iteratorOf(iter);
+        return new Iterator<V>() {
+            @Override
+            public boolean hasNext() {
+                return iterator.hasNext();
+            }
+
+            @Override
+            public V next() {
+                U value = iterator.next();
+                return mapper.apply(value);
+            }
+        };
+    }
+
+    /**
+     * Wraps a group of {@link Supplier}s of {@link Iterable} instances into a &quot;unified&quot;
+     * {@link Iterable} of their values, in the same order as the suppliers - i.e., once the values
+     * from a specific supplier are exhausted, the next one is consulted, and so on, until all
+     * suppliers have been consulted
+     *
+     * @param <T> Type of value being iterated
+     * @param providers The providers - ignored if {@code null} (i.e., return an empty iterable instance)
+     * @return The wrapping instance
+     */
+    public static <T> Iterable<T> multiIterableSuppliers(Iterable<? extends Supplier<? extends Iterable<? extends T>>> providers) {
+        return () -> stream(providers).<T>flatMap(s -> stream(s.get())).map(Function.identity()).iterator();
+    }
+
+    public static <K, V> MapBuilder<K, V> mapBuilder() {
+        return new MapBuilder<>();
+    }
+
+    public static <K, V> MapBuilder<K, V> mapBuilder(Comparator<K> comparator) {
+        return new MapBuilder<>(comparator);
+    }
+
+    public static class MapBuilder<K, V> {
+        private Map<K, V> map;
+
+        public MapBuilder() {
+            this.map = new LinkedHashMap<>();
+        }
+
+        public MapBuilder(Comparator<? super K> comparator) {
+            this.map = new TreeMap<>(comparator);
+        }
+
+        public MapBuilder<K, V> put(K k, V v) {
+            map.put(k, v);
+            return this;
+        }
+
+        public Map<K, V> build() {
+            return map;
+        }
+
+        public Map<K, V> immutable() {
+            return Collections.unmodifiableMap(map);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/IgnoringEmptyMap.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/IgnoringEmptyMap.java b/sshd-common/src/main/java/org/apache/sshd/common/util/IgnoringEmptyMap.java
new file mode 100644
index 0000000..8f18bfe
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/IgnoringEmptyMap.java
@@ -0,0 +1,128 @@
+/*
+ * 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.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * A dummy map that ignores all {@code put/remove} calls
+ *
+ * @param <K> Key type
+ * @param <V> Value type
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class IgnoringEmptyMap<K, V> implements Map<K, V> {
+    @SuppressWarnings("rawtypes")
+    private static final IgnoringEmptyMap INSTANCE = new IgnoringEmptyMap();
+
+    public IgnoringEmptyMap() {
+        super();
+    }
+
+    @Override
+    public int size() {
+        return 0;
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return true;
+    }
+
+    @Override
+    public boolean containsValue(Object value) {
+        Objects.requireNonNull(value, "No value provided");
+        return false;
+    }
+
+    @Override
+    public boolean containsKey(Object key) {
+        Objects.requireNonNull(key, "No key provided");
+        return false;
+    }
+
+    @Override
+    public V get(Object key) {
+        Objects.requireNonNull(key, "No key provided");
+        return null;
+    }
+
+    @Override
+    public V put(K key, V value) {
+        Objects.requireNonNull(key, "No key provided");
+        Objects.requireNonNull(value, "No value provided");
+        return null;
+    }
+
+    @Override
+    public V remove(Object key) {
+        Objects.requireNonNull(key, "No key provided");
+        return null;
+    }
+
+    @Override
+    public void putAll(Map<? extends K, ? extends V> m) {
+        // ignored
+    }
+
+    @Override
+    public void clear() {
+        // ignored
+    }
+
+    @Override
+    public Set<K> keySet() {
+        return Collections.emptySet();
+    }
+
+    @Override
+    public Collection<V> values() {
+        return Collections.emptyList();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        return o instanceof IgnoringEmptyMap<?, ?>;
+    }
+
+    @Override
+    public int hashCode() {
+        return 0;
+    }
+
+    @Override
+    public String toString() {
+        return "{}";
+    }
+
+    @Override
+    public Set<Entry<K, V>> entrySet() {
+        return Collections.emptySet();
+    }
+
+    @SuppressWarnings("unchecked")
+    public static <K, V> IgnoringEmptyMap<K, V> getInstance() {
+        return INSTANCE;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/Int2IntFunction.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/Int2IntFunction.java b/sshd-common/src/main/java/org/apache/sshd/common/util/Int2IntFunction.java
new file mode 100644
index 0000000..490abb0
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/Int2IntFunction.java
@@ -0,0 +1,66 @@
+/*
+ * 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.function.IntUnaryOperator;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class Int2IntFunction {
+    private Int2IntFunction() {
+        throw new UnsupportedOperationException("No instance");
+    }
+
+    public static IntUnaryOperator sub(int delta) {
+        return add(0 - delta);
+    }
+
+    public static IntUnaryOperator add(int delta) {
+        if (delta == 0) {
+            return IntUnaryOperator.identity();
+        } else {
+            return value -> value + delta;
+        }
+    }
+
+    public static IntUnaryOperator mul(int factor) {
+        if (factor == 0) {
+            return constant(0);
+        } else if (factor == 1) {
+            return IntUnaryOperator.identity();
+        } else {
+            return value -> value * factor;
+        }
+    }
+
+    public static IntUnaryOperator constant(int v) {
+        return value -> v;
+    }
+
+    public static IntUnaryOperator div(int factor) {
+        if (factor == 1) {
+            return IntUnaryOperator.identity();
+        } else {
+            ValidateUtils.checkTrue(factor != 0, "Zero division factor");
+            return value -> value / factor;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/Invoker.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/Invoker.java b/sshd-common/src/main/java/org/apache/sshd/common/util/Invoker.java
new file mode 100644
index 0000000..71cbebd
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/Invoker.java
@@ -0,0 +1,116 @@
+/*
+ * 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.AbstractMap.SimpleImmutableEntry;
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * The complement to the {@code Callable} interface - accepts one argument
+ * and possibly throws somethind
+ *
+ * @param <ARG> Argument type
+ * @param <RET> Return type
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FunctionalInterface
+public interface Invoker<ARG, RET> {
+    RET invoke(ARG arg) throws Throwable;
+
+    static <ARG> Invoker<ARG, Void> wrapAll(Collection<? extends Invoker<? super ARG, ?>> invokers) {
+        return arg -> {
+            invokeAll(arg, invokers);
+            return null;
+        };
+    }
+
+    /**
+     * Invokes <U>all</U> the instances ignoring the return value. Any
+     * intermediate exceptions are accumulated and thrown at the end.
+     *
+     * @param <ARG> Argument type
+     * @param arg The argument to pass to the {@link #invoke(Object)} method
+     * @param invokers The invokers to scan - ignored if {@code null}/empty
+     * (also ignores {@code null} members)
+     * @throws Throwable If invocation failed
+     */
+    static <ARG> void invokeAll(ARG arg, Collection<? extends Invoker<? super ARG, ?>> invokers) throws Throwable {
+        if (GenericUtils.isEmpty(invokers)) {
+            return;
+        }
+
+        Throwable err = null;
+        for (Invoker<? super ARG, ?> i : invokers) {
+            if (i == null) {
+                continue;
+            }
+
+            try {
+                i.invoke(arg);
+            } catch (Throwable t) {
+                err = GenericUtils.accumulateException(err, t);
+            }
+        }
+
+        if (err != null) {
+            throw err;
+        }
+    }
+
+    static <ARG> Invoker<ARG, Void> wrapFirst(Collection<? extends Invoker<? super ARG, ?>> invokers) {
+        return arg -> {
+            Map.Entry<Invoker<? super ARG, ?>, Throwable> result = invokeTillFirstFailure(arg, invokers);
+            if (result != null) {
+                throw result.getValue();
+            }
+            return null;
+        };
+    }
+
+    /**
+     * Invokes all instances until 1st failure (if any)
+     *
+     * @param <ARG> Argument type
+     * @param arg The argument to pass to the {@link #invoke(Object)} method
+     * @param invokers The invokers to scan - ignored if {@code null}/empty
+     * (also ignores {@code null} members)
+     * @return A {@link SimpleImmutableEntry} representing the <U>first</U> failed
+     * invocation - {@code null} if all were successful (or none invoked).
+     */
+    static <ARG> SimpleImmutableEntry<Invoker<? super ARG, ?>, Throwable> invokeTillFirstFailure(ARG arg, Collection<? extends Invoker<? super ARG, ?>> invokers) {
+        if (GenericUtils.isEmpty(invokers)) {
+            return null;
+        }
+
+        for (Invoker<? super ARG, ?> i : invokers) {
+            if (i == null) {
+                continue;
+            }
+
+            try {
+                i.invoke(arg);
+            } catch (Throwable t) {
+                return new SimpleImmutableEntry<>(i, t);
+            }
+        }
+
+        return null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/MapEntryUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/MapEntryUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/util/MapEntryUtils.java
new file mode 100644
index 0000000..6bcc93a
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/MapEntryUtils.java
@@ -0,0 +1,51 @@
+/*
+ * 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.Comparator;
+import java.util.Map;
+
+/**
+ * Represents an un-modifiable pair of values
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class MapEntryUtils {
+    @SuppressWarnings({"rawtypes", "unchecked"})
+    private static final Comparator<Map.Entry<Comparable, ?>> BY_KEY_COMPARATOR = (o1, o2) -> {
+        Comparable k1 = o1.getKey();
+        Comparable k2 = o2.getKey();
+        return k1.compareTo(k2);
+    };
+
+    private MapEntryUtils() {
+        throw new UnsupportedOperationException("No instance");
+    }
+
+    /**
+     * @param <K> The {@link Comparable} key type
+     * @param <V> The associated entry value
+     * @return A {@link Comparator} for {@link java.util.Map.Entry}-ies that
+     * compares the key values
+     */
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public static <K extends Comparable<K>, V> Comparator<Map.Entry<K, V>> byKeyEntryComparator() {
+        return (Comparator) BY_KEY_COMPARATOR;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/NumberUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/NumberUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/util/NumberUtils.java
new file mode 100644
index 0000000..c8e1817
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/NumberUtils.java
@@ -0,0 +1,310 @@
+/*
+ * 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.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.IntStream;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class NumberUtils {
+    /**
+     * A {@link List} of all the {@link Class} types used to represent the
+     * primitive numerical values
+     */
+    public static final List<Class<?>> NUMERIC_PRIMITIVE_CLASSES =
+        GenericUtils.unmodifiableList(
+            Byte.TYPE, Short.TYPE, Integer.TYPE, Long.TYPE, Float.TYPE, Double.TYPE);
+
+    /**
+     * A {@link List} containing all the pure powers of 2 for a {@code long}
+     * value. The value at index <I>n</I> is 2 to the power of <I>n</I>
+     */
+    public static final List<Long> POWERS_OF_TWO =
+        GenericUtils.unmodifiableList(IntStream.range(0, 64).mapToObj(i -> 1L << i));
+
+    private NumberUtils() {
+        throw new UnsupportedOperationException("No instance");
+    }
+
+    public static boolean isPowerOf2(long value) {
+        for (Long l : POWERS_OF_TWO) {
+            if (value == l) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    public static long getNextPowerOf2(long value) {
+        long j = 1L;
+        while (j < value) {
+            j <<= 1;
+        }
+        return j;
+    }
+
+    public static int getNextPowerOf2(int value) {
+        int j = 1;
+        while (j < value) {
+            j <<= 1;
+        }
+        return j;
+    }
+
+    public static int hashCode(long... values) {
+        return Arrays.hashCode(values);
+    }
+
+    public static int hashCode(int... values) {
+        return Arrays.hashCode(values);
+    }
+
+    public static int hashCode(byte... values) {
+        return Arrays.hashCode(values);
+    }
+
+    public static int hashCode(byte[] a, int offset, int len) {
+        if (len == 0) {
+            return 0;
+        }
+
+        int result = 1;
+        for (int pos = offset, count = 0; count < len; pos++, count++) {
+            byte element = a[pos];
+            result = 31 * result + element;
+        }
+
+        return result;
+    }
+
+    public static int diffOffset(byte[] a1, int startPos1, byte[] a2, int startPos2, int len) {
+        for (int pos1 = startPos1, pos2 = startPos2, count = 0; count < len; pos1++, pos2++, count++) {
+            byte v1 = a1[pos1];
+            byte v2 = a2[pos2];
+            if (v1 != v2) {
+                return count;
+            }
+        }
+
+        return -1;
+    }
+
+    /**
+     * @param clazz The {@link Class} to examine - ignored if {@code null}
+     * @return If the class is a {@link Number} or one of the primitive numerical types
+     * @see #NUMERIC_PRIMITIVE_CLASSES
+     */
+    public static boolean isNumericClass(Class<?> clazz) {
+        if (clazz == null) {
+            return false;
+        }
+
+        // turns out that the primitive types are not assignable to Number
+        if (Number.class.isAssignableFrom(clazz)) {
+            return true;
+        }
+
+        return NUMERIC_PRIMITIVE_CLASSES.indexOf(clazz) >= 0;
+    }
+
+    /**
+     * Converts a {@link Number} into an {@link Integer} if not already such
+     *
+     * @param n The {@link Number} - ignored if {@code null}
+     * @return The equivalent {@link Integer} value
+     */
+    public static Integer toInteger(Number n) {
+        if (n == null) {
+            return null;
+        } else if (n instanceof Integer) {
+            return (Integer) n;
+        } else {
+            return 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(v);
+        }
+
+        return l;
+    }
+
+    /**
+     * Checks if optional sign and all others are '0'-'9'
+     * @param cs The {@link CharSequence} to check
+     * @return {@code true} if valid integer number
+     */
+    public static boolean isIntegerNumber(CharSequence cs) {
+        if (GenericUtils.isEmpty(cs)) {
+            return false;
+        }
+
+        for (int index = 0; index < cs.length(); index++) {
+            char c = cs.charAt(0);
+            if ((c >= '0') && (c <= '9')) {
+                continue;
+            }
+
+            if ((c == '+') || (c == '-')) {
+                if (index == 0) {
+                    continue;
+                }
+            }
+
+            return false;
+        }
+
+        return true;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/ObjectBuilder.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/ObjectBuilder.java b/sshd-common/src/main/java/org/apache/sshd/common/util/ObjectBuilder.java
new file mode 100644
index 0000000..23884a2
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/ObjectBuilder.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util;
+
+import java.util.function.Supplier;
+
+/**
+ * A generic builder interface
+ *
+ * @param <T> Type of object being built
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FunctionalInterface
+public interface ObjectBuilder<T> extends Supplier<T> {
+    @Override
+    default T get() {
+        return build();
+    }
+
+    T build();
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/OsUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/OsUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/util/OsUtils.java
new file mode 100644
index 0000000..f0b2c7e
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/OsUtils.java
@@ -0,0 +1,257 @@
+/*
+ * 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.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Operating system dependent utility methods.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class OsUtils {
+
+    /**
+     * Property that can be used to override the reported value from {@link #getCurrentUser()}.
+     * If not set then &quot;user.name&quot; system property is used
+     */
+    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";
+
+    public static final String ROOT_USER = "root";
+
+    public static final List<String> LINUX_COMMAND =
+            Collections.unmodifiableList(Arrays.asList(LINUX_SHELL_COMMAND_NAME, "-i", "-l"));
+    public static final List<String> WINDOWS_COMMAND =
+            Collections.unmodifiableList(Collections.singletonList(WINDOWS_SHELL_COMMAND_NAME));
+
+    private static final AtomicReference<String> CURRENT_USER_HOLDER = new AtomicReference<>(null);
+    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");
+    }
+
+    /**
+     * @return true if the host is a UNIX system (and not Windows).
+     */
+    public static boolean isUNIX() {
+        return !isWin32();
+    }
+
+    /**
+     * @return true if the host is Windows (and not UNIX).
+     * @see #OS_TYPE_OVERRIDE_PROP
+     * @see #setWin32(Boolean)
+     */
+    public static boolean isWin32() {
+        Boolean typeValue;
+        synchronized (OS_TYPE_HOLDER) {
+            typeValue = OS_TYPE_HOLDER.get();
+            if (typeValue != null) {    // is it the 1st time
+                return typeValue;
+            }
+
+            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;
+    }
+
+    /**
+     * 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() {
+        return resolveInteractiveCommand(isWin32());
+    }
+
+    public static List<String> resolveInteractiveCommand(boolean isWin32) {
+        if (isWin32) {
+            return WINDOWS_COMMAND;
+        } else {
+            return LINUX_COMMAND;
+        }
+    }
+
+    /**
+     * Get current user name
+     *
+     * @return Current user
+     * @see #CURRENT_USER_OVERRIDE_PROP
+     */
+    public static String getCurrentUser() {
+        String username = null;
+        synchronized (CURRENT_USER_HOLDER) {
+            username = CURRENT_USER_HOLDER.get();
+            if (username != null) {  // have we already resolved it ?
+                return username;
+            }
+
+            username = getCanonicalUser(System.getProperty(CURRENT_USER_OVERRIDE_PROP, System.getProperty("user.name")));
+            ValidateUtils.checkNotNullAndNotEmpty(username, "No username available");
+            CURRENT_USER_HOLDER.set(username);
+        }
+
+        return username;
+    }
+
+    /**
+     * Remove {@code Windows} domain and/or group prefix as well as &quot;(User);&quot suffix
+     *
+     * @param user The original username - ignored if {@code null}/empty
+     * @return The canonical user - unchanged if {@code Unix} O/S
+     */
+    public static String getCanonicalUser(String user) {
+        if (GenericUtils.isEmpty(user)) {
+            return user;
+        }
+
+        // Windows owner sometime has the domain and/or group prepended to it
+        if (isWin32()) {
+            int pos = user.lastIndexOf('\\');
+            if (pos > 0) {
+                user = user.substring(pos + 1);
+            }
+
+            pos = user.indexOf(' ');
+            if (pos > 0) {
+                user = user.substring(0, pos).trim();
+            }
+        }
+
+        return user;
+    }
+
+    /**
+     * Attempts to resolve canonical group name for {@code Windows}
+     *
+     * @param group The original group name - used if not {@code null}/empty
+     * @param user The owner name - sometimes it contains a group name
+     * @return The canonical group name
+     */
+    public static String resolveCanonicalGroup(String group, String user) {
+        if (isUNIX()) {
+            return group;
+        }
+
+        // we reach this code only for Windows
+        if (GenericUtils.isEmpty(group)) {
+            int pos = GenericUtils.isEmpty(user) ? -1 : user.lastIndexOf('\\');
+            return (pos > 0) ? user.substring(0, pos) : group;
+        }
+
+        int pos = group.indexOf(' ');
+        return (pos < 0) ? group : group.substring(0, pos).trim();
+    }
+
+    /**
+     * Can be used to programmatically set the username reported by {@link #getCurrentUser()}
+     * @param username The username to set - if {@code null} then {@link #CURRENT_USER_OVERRIDE_PROP}
+     * will be consulted
+     */
+    public static void setCurrentUser(String username) {
+        synchronized (CURRENT_USER_HOLDER) {
+            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);
+        }
+    }
+
+    /**
+     * @param path The original path
+     * @return A path that can be compared with another one where case
+     * sensitivity of the underlying O/S has been taken into account -
+     * never {@code null}
+     */
+    public static String getComparablePath(String path) {
+        String p = (path == null) ? "" : path;
+        return isWin32() ? p.toLowerCase() : p;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/Readable.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/Readable.java b/sshd-common/src/main/java/org/apache/sshd/common/util/Readable.java
new file mode 100644
index 0000000..3075eb3
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/Readable.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util;
+
+import java.nio.ByteBuffer;
+import java.util.Objects;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface Readable {
+
+    int available();
+
+    void getRawBytes(byte[] data, int offset, int len);
+
+    /**
+     * Wrap a {@link ByteBuffer} as a {@link Readable} instance
+     *
+     * @param buffer The {@link ByteBuffer} to wrap - never {@code null}
+     * @return The {@link Readable} wrapper
+     */
+    static Readable readable(ByteBuffer buffer) {
+        Objects.requireNonNull(buffer, "No buffer to wrap");
+        return new Readable() {
+            @Override
+            public int available() {
+                return buffer.remaining();
+            }
+
+            @Override
+            public void getRawBytes(byte[] data, int offset, int len) {
+                buffer.get(data, offset, len);
+            }
+        };
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/ReflectionUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/ReflectionUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/util/ReflectionUtils.java
new file mode 100644
index 0000000..84a84f9
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/ReflectionUtils.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util;
+
+import java.lang.reflect.Field;
+import java.util.Collection;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class ReflectionUtils {
+    public static final Function<Field, String> FIELD_NAME_EXTRACTOR = f -> (f == null) ? null : f.getName();
+
+    private ReflectionUtils() {
+        throw new UnsupportedOperationException("No instance");
+    }
+
+    public static Collection<Field> getMatchingFields(Class<?> clazz, Predicate<? super Field> acceptor) {
+        return GenericUtils.selectMatchingMembers(acceptor, clazz.getFields());
+    }
+
+    public static Collection<Field> getMatchingDeclaredFields(Class<?> clazz, Predicate<? super Field> acceptor) {
+        return GenericUtils.selectMatchingMembers(acceptor, clazz.getDeclaredFields());
+    }
+
+    public static boolean isClassAvailable(ClassLoader cl, String className) {
+        try {
+            cl.loadClass(className);
+            return true;
+        } catch (Throwable ignored) {
+            return false;
+        }
+    }
+}


[07/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common utilities code from sshd-core into sshd-common (new artifact)

Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/GenericUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/GenericUtilsTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/GenericUtilsTest.java
deleted file mode 100644
index a83b9de..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/GenericUtilsTest.java
+++ /dev/null
@@ -1,173 +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.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.NoSuchElementException;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class GenericUtilsTest extends BaseTestSupport {
-    public GenericUtilsTest() {
-        super();
-    }
-
-    @Test
-    public void testSplitAndJoin() {
-        List<String> expected = Collections.unmodifiableList(
-                Arrays.asList(getClass().getPackage().getName().replace('.', '/'), getClass().getSimpleName(), getCurrentTestName()));
-
-        // NOTE: we also test characters that have meaning in String.split(...) as regex ones
-        for (char ch : new char[]{',', '.', '*', '?'}) {
-            String sep = String.valueOf(ch);
-            String s = GenericUtils.join(expected, sep);
-            String[] actual = GenericUtils.split(s, ch);
-            assertEquals("Mismatched split length for separator=" + sep, expected.size(), GenericUtils.length((Object[]) actual));
-
-            for (int index = 0; index < actual.length; index++) {
-                String e = expected.get(index);
-                String a = actual[index];
-                if (!e.endsWith(a)) {
-                    fail("Mismatched value at index=" + index + " for separator=" + sep + ": expected=" + e + ", actual=" + a);
-                }
-            }
-        }
-    }
-
-    @Test
-    public void testStripQuotes() {
-        String expected = getCurrentTestName();
-        assertSame("Unexpected un-quoted stripping", expected, GenericUtils.stripQuotes(expected));
-
-        StringBuilder sb = new StringBuilder(2 + expected.length()).append('|').append(expected).append('|');
-        for (int index = 0; index < GenericUtils.QUOTES.length(); index++) {
-            char delim = GenericUtils.QUOTES.charAt(index);
-            sb.setCharAt(0, delim);
-            sb.setCharAt(sb.length() - 1, delim);
-
-            CharSequence actual = GenericUtils.stripQuotes(sb);
-            assertEquals("Mismatched result for delim (" + delim + ")", expected, actual.toString());
-        }
-    }
-
-    @Test
-    public void testStripOnlyFirstLayerQuotes() {
-        StringBuilder sb = new StringBuilder().append("||").append(getCurrentTestName()).append("||");
-        char[] delims = {'\'', '"', '"', '\''};
-        for (int index = 0; index < delims.length; index += 2) {
-            char topDelim = delims[index];
-            char innerDelim = delims[index + 1];
-            sb.setCharAt(0, topDelim);
-            sb.setCharAt(1, innerDelim);
-            sb.setCharAt(sb.length() - 2, innerDelim);
-            sb.setCharAt(sb.length() - 1, topDelim);
-
-            CharSequence expected = sb.subSequence(1, sb.length() - 1);
-            CharSequence actual = GenericUtils.stripQuotes(sb);
-            assertEquals("Mismatched result for delim (" + topDelim + "/" + innerDelim + ")", expected.toString(), actual.toString());
-        }
-    }
-
-    @Test
-    public void testStripDelimiters() {
-        String expected = getCurrentTestName();
-        final char delim = '|';
-        assertSame("Unexpected un-delimited stripping", expected, GenericUtils.stripDelimiters(expected, delim));
-
-        CharSequence actual = GenericUtils.stripDelimiters(
-                new StringBuilder(2 + expected.length()).append(delim).append(expected).append(delim), delim);
-        assertEquals("Mismatched stripped values", expected, actual.toString());
-    }
-
-    @Test
-    public void testStripDelimitersOnlyIfOnBothEnds() {
-        final char delim = '$';
-        StringBuilder expected = new StringBuilder().append(delim).append(getCurrentTestName()).append(delim);
-        for (int index : new int[]{0, expected.length() - 1}) {
-            // restore original delimiters
-            expected.setCharAt(0, delim);
-            expected.setCharAt(expected.length() - 1, delim);
-            // trash one end
-            expected.setCharAt(index, (char) (delim + 1));
-
-            assertSame("Mismatched result for delim at index=" + index, expected, GenericUtils.stripDelimiters(expected, delim));
-        }
-    }
-
-    @Test
-    public void testAccumulateExceptionOnNullValues() {
-        assertNull("Unexpected null/null result", GenericUtils.accumulateException(null, null));
-
-        Throwable expected = new NoSuchMethodException(getClass().getName() + "#" + getCurrentTestName());
-        assertSame("Mismatched null/extra result", expected, GenericUtils.accumulateException(null, expected));
-        assertSame("Mismatched current/null result", expected, GenericUtils.accumulateException(expected, null));
-    }
-
-    @Test
-    public void testAccumulateExceptionOnExistingCurrent() {
-        RuntimeException[] expected = new RuntimeException[]{
-            new IllegalArgumentException(getCurrentTestName()),
-            new ClassCastException(getClass().getName()),
-            new NoSuchElementException(getClass().getPackage().getName())
-        };
-        RuntimeException current = new UnsupportedOperationException("top");
-        for (RuntimeException extra : expected) {
-            RuntimeException actual = GenericUtils.accumulateException(current, extra);
-            assertSame("Mismatched returned actual exception", current, actual);
-        }
-
-        Throwable[] actual = current.getSuppressed();
-        assertArrayEquals("Suppressed", expected, actual);
-    }
-
-    @Test
-    public void testNullOrEmptyCharArrayComparison() {
-        char[][] values = new char[][]{null, GenericUtils.EMPTY_CHAR_ARRAY};
-        for (char[] c1 : values) {
-            for (char[] c2 : values) {
-                assertEquals(((c1 == null) ? "null" : "empty") + " vs. " + ((c2 == null) ? "null" : "empty"), 0, GenericUtils.compare(c1, c2));
-            }
-        }
-    }
-
-    @Test
-    public void testCharArrayComparison() {
-        String s1 = getClass().getSimpleName();
-        char[] c1 = s1.toCharArray();
-        assertEquals("Same value equality", 0, GenericUtils.compare(c1, s1.toCharArray()));
-
-        String s2 = getCurrentTestName();
-        char[] c2 = s2.toCharArray();
-        assertEquals("s1 vs. s2", Integer.signum(s1.compareTo(s2)), Integer.signum(GenericUtils.compare(c1, c2)));
-        assertEquals("s2 vs. s1", Integer.signum(s2.compareTo(s1)), Integer.signum(GenericUtils.compare(c2, c1)));
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/Int2IntFunctionTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/Int2IntFunctionTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/Int2IntFunctionTest.java
deleted file mode 100644
index 941190b..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/Int2IntFunctionTest.java
+++ /dev/null
@@ -1,154 +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.util.Random;
-import java.util.function.IntUnaryOperator;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class Int2IntFunctionTest extends BaseTestSupport {
-    public Int2IntFunctionTest() {
-        super();
-    }
-
-    @Test
-    public void testAdd() {
-        int factor = Byte.SIZE;
-        IntUnaryOperator func = Int2IntFunction.add(factor);
-        for (int index = 1, sum = 0; index <= Byte.SIZE; index++) {
-            sum = func.applyAsInt(sum);
-            assertEquals(factor * index, sum);
-        }
-    }
-
-    @Test
-    public void testAddIdentity() {
-        IntUnaryOperator func = Int2IntFunction.add(0);
-        Random rnd = new Random(System.nanoTime());
-        for (int index = 1; index <= Byte.SIZE; index++) {
-            int expected = rnd.nextInt();
-            int actual = func.applyAsInt(expected);
-            assertEquals(expected, actual);
-        }
-    }
-
-    @Test
-    public void testSub() {
-        int factor = Byte.SIZE;
-        IntUnaryOperator func = Int2IntFunction.sub(factor);
-        for (int index = 1, sum = 0; index <= Byte.SIZE; index++) {
-            sum = func.applyAsInt(sum);
-            assertEquals(factor * index * -1, sum);
-        }
-    }
-
-    @Test
-    public void testSubIdentity() {
-        IntUnaryOperator func = Int2IntFunction.sub(0);
-        Random rnd = new Random(System.nanoTime());
-        for (int index = 1; index <= Byte.SIZE; index++) {
-            int expected = rnd.nextInt();
-            int actual = func.applyAsInt(expected);
-            assertEquals(expected, actual);
-        }
-    }
-
-    @Test
-    public void testMul() {
-        int factor = 2;
-        IntUnaryOperator func = Int2IntFunction.mul(factor);
-        for (int index = 1, mul = 1, expected = factor; index <= Byte.SIZE; index++, expected *= factor) {
-            mul = func.applyAsInt(mul);
-            assertEquals(expected, mul);
-        }
-    }
-
-    @Test
-    public void testMulIdentity() {
-        IntUnaryOperator func = Int2IntFunction.mul(1);
-        Random rnd = new Random(System.nanoTime());
-        for (int index = 1; index <= Byte.SIZE; index++) {
-            int expected = rnd.nextInt();
-            int actual = func.applyAsInt(expected);
-            assertEquals(expected, actual);
-        }
-    }
-
-    @Test
-    public void testMulZero() {
-        IntUnaryOperator func = Int2IntFunction.mul(0);
-        Random rnd = new Random(System.nanoTime());
-        for (int index = 1; index <= Byte.SIZE; index++) {
-            int value = rnd.nextInt();
-            int actual = func.applyAsInt(value);
-            assertEquals(Integer.toString(value), 0, actual);
-        }
-    }
-
-    @Test
-    public void testConstant() {
-        int expected = 377347;
-        IntUnaryOperator func = Int2IntFunction.constant(expected);
-        Random rnd = new Random(System.nanoTime());
-        for (int index = 1; index <= Byte.SIZE; index++) {
-            int value = rnd.nextInt();
-            int actual = func.applyAsInt(value);
-            assertEquals(Integer.toString(value), expected, actual);
-        }
-    }
-
-    @Test
-    public void testDiv() {
-        int factor = 2;
-        IntUnaryOperator func = Int2IntFunction.div(factor);
-        for (int index = 1, quot = 65536, expected = quot / factor; index <= Byte.SIZE; index++, expected /= factor) {
-            quot = func.applyAsInt(quot);
-            assertEquals(expected, quot);
-        }
-    }
-
-    @Test
-    public void testDivIdentity() {
-        IntUnaryOperator func = Int2IntFunction.div(1);
-        Random rnd = new Random(System.nanoTime());
-        for (int index = 1; index <= Byte.SIZE; index++) {
-            int expected = rnd.nextInt();
-            int actual = func.applyAsInt(expected);
-            assertEquals(expected, actual);
-        }
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void testDivZeroFactor() {
-        IntUnaryOperator func = Int2IntFunction.div(0);
-        fail("Unexpected success: " + func);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/NumberUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/NumberUtilsTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/NumberUtilsTest.java
deleted file mode 100644
index 8109b55..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/NumberUtilsTest.java
+++ /dev/null
@@ -1,77 +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.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class NumberUtilsTest extends BaseTestSupport {
-    public NumberUtilsTest() {
-        super();
-    }
-
-    @Test
-    public void testPowersOf2List() {
-        assertEquals("Mismatched values size for " + NumberUtils.POWERS_OF_TWO, Long.SIZE, GenericUtils.size(NumberUtils.POWERS_OF_TWO));
-        long expected = 1L;
-        for (int index = 0; index < Long.SIZE; index++, expected <<= 1) {
-            Long actual = NumberUtils.POWERS_OF_TWO.get(index);
-            assertEquals("Mismatched value at index=" + index, Long.toHexString(expected), Long.toHexString(actual));
-        }
-    }
-
-    @Test
-    public void testNextPowerOf2() {
-        for (Long v : NumberUtils.POWERS_OF_TWO) {
-            long expected = v;
-            if (expected > 2L) {
-                assertEquals("Mismatched lower bound value", expected, NumberUtils.getNextPowerOf2(expected - 1L));
-            }
-
-            if (expected > 0L) {    // avoid the negative value
-                assertEquals("Mismatched exact value", expected, NumberUtils.getNextPowerOf2(expected));
-            }
-        }
-    }
-
-    @Test
-    public void testToInteger() {
-        assertNull("Unexpected null value", NumberUtils.toInteger(null));
-        for (Number n : new Number[]{
-                Byte.valueOf(Byte.MAX_VALUE), Short.valueOf(Short.MIN_VALUE),
-                Integer.valueOf(Short.MAX_VALUE), Long.valueOf(82007160L)}) {
-            Integer i = NumberUtils.toInteger(n);
-            if (n instanceof Integer) {
-                assertSame("Unexpected conversion", n, i);
-            } else {
-                assertEquals("Mismatched values", n.intValue(), i.intValue());
-            }
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/OsUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/OsUtilsTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/OsUtilsTest.java
deleted file mode 100644
index 62081af..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/OsUtilsTest.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.common.util;
-
-import java.util.Objects;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class OsUtilsTest extends BaseTestSupport {
-    public OsUtilsTest() {
-        super();
-    }
-
-    @Test
-    public void testSetOsTypeByProperty() {
-        try {
-            for (String osType : new String[]{"Some-Windows", "Some-Linux"}) {
-                OsUtils.setWin32(null); // force re-detection
-
-                try {
-                    boolean expected = osType.contains("Windows");
-                    System.setProperty(OsUtils.OS_TYPE_OVERRIDE_PROP, osType);
-                    boolean actual = OsUtils.isWin32();
-                    assertEquals(osType, expected, actual);
-                } finally {
-                    System.clearProperty(OsUtils.OS_TYPE_OVERRIDE_PROP);
-                }
-            }
-        } finally {
-            OsUtils.setWin32(null); // force re-detection
-        }
-    }
-
-    @Test
-    public void testSetOsTypeProgrammatically() {
-        try {
-            for (boolean expected : new boolean[]{true, false}) {
-                OsUtils.setWin32(expected); // force value
-                assertEquals("Mismatched detection value", expected, OsUtils.isWin32());
-            }
-        } finally {
-            OsUtils.setWin32(null); // force re-detection
-        }
-    }
-
-    @Test
-    public void testSetCurrentUserByProperty() {
-        try {
-            for (String expected : new String[]{getClass().getSimpleName(), getCurrentTestName()}) {
-                OsUtils.setCurrentUser(null); // force re-detection
-
-                try {
-                    System.setProperty(OsUtils.CURRENT_USER_OVERRIDE_PROP, expected);
-                    String actual = OsUtils.getCurrentUser();
-                    assertEquals("Mismatched reported current user", expected, actual);
-                } finally {
-                    System.clearProperty(OsUtils.CURRENT_USER_OVERRIDE_PROP);
-                }
-            }
-        } finally {
-            OsUtils.setCurrentUser(null); // force re-detection
-        }
-    }
-
-    @Test
-    public void testSetCurrentUserProgrammatically() {
-        try {
-            for (String expected : new String[]{getClass().getSimpleName(), getCurrentTestName()}) {
-                OsUtils.setCurrentUser(expected); // force value
-                assertEquals("Mismatched detection value", expected, OsUtils.getCurrentUser());
-            }
-        } finally {
-            OsUtils.setCurrentUser(null); // force re-detection
-        }
-    }
-
-    @Test
-    public void testSetJavaVersionByProperty() {
-        try {
-            for (String value : new String[]{"7.3.6_5", "37.77.34_7-" + getCurrentTestName()}) {
-                OsUtils.setJavaVersion(null); // force re-detection
-
-                try {
-                    System.setProperty(OsUtils.JAVA_VERSION_OVERRIDE_PROP, value);
-                    String expected = value.replace('_', '.');
-                    String actual = Objects.toString(OsUtils.getJavaVersion(), null);
-                    assertTrue("Mismatched reported version value: " + actual, expected.startsWith(actual));
-                } finally {
-                    System.clearProperty(OsUtils.JAVA_VERSION_OVERRIDE_PROP);
-                }
-            }
-        } finally {
-            OsUtils.setJavaVersion(null); // force re-detection
-        }
-    }
-
-    @Test
-    public void testSetJavaVersionProgrammatically() {
-        try {
-            for (VersionInfo expected : new VersionInfo[]{VersionInfo.parse("7.3.6.5"), VersionInfo.parse("37.77.34.7")}) {
-                OsUtils.setJavaVersion(expected); // force value
-                assertEquals("Mismatched detection value", expected, OsUtils.getJavaVersion());
-            }
-        } finally {
-            OsUtils.setJavaVersion(null); // force re-detection
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/SecurityUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/SecurityUtilsTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/SecurityUtilsTest.java
deleted file mode 100644
index c4e521c..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/SecurityUtilsTest.java
+++ /dev/null
@@ -1,232 +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.IOException;
-import java.nio.file.Path;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.interfaces.DSAPrivateKey;
-import java.security.interfaces.DSAPublicKey;
-import java.security.interfaces.ECPrivateKey;
-import java.security.interfaces.ECPublicKey;
-import java.security.interfaces.RSAPrivateKey;
-import java.security.interfaces.RSAPublicKey;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-import org.apache.sshd.common.cipher.BuiltinCiphers;
-import org.apache.sshd.common.cipher.ECCurves;
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.config.keys.loader.KeyPairResourceLoader;
-import org.apache.sshd.common.keyprovider.AbstractResourceKeyPairProvider;
-import org.apache.sshd.common.keyprovider.ClassLoadableResourceKeyPairProvider;
-import org.apache.sshd.common.keyprovider.FileKeyPairProvider;
-import org.apache.sshd.common.util.security.SecurityProviderRegistrar;
-import org.apache.sshd.common.util.security.SecurityUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.AfterClass;
-import org.junit.Assume;
-import org.junit.BeforeClass;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-@SuppressWarnings("checkstyle:MethodCount")
-public class SecurityUtilsTest extends BaseTestSupport {
-    public static final String BC_NAMED_USAGE_PROP =
-            SecurityProviderRegistrar.CONFIG_PROP_BASE
-          + "." + SecurityUtils.BOUNCY_CASTLE
-          + "." + SecurityProviderRegistrar.NAMED_PROVIDER_PROPERTY;
-
-    private static final String DEFAULT_PASSWORD = "super secret passphrase";
-    private static final FilePasswordProvider TEST_PASSWORD_PROVIDER = file -> DEFAULT_PASSWORD;
-
-    public SecurityUtilsTest() {
-        super();
-    }
-
-    // NOTE: Using the BouncyCastle provider instead of the name does not work as expected so we take no chances
-    @BeforeClass
-    public static void useNamedBouncyCastleProvider() {
-        System.setProperty(BC_NAMED_USAGE_PROP, Boolean.TRUE.toString());
-    }
-
-    @AfterClass
-    public static void unsetBouncyCastleProviderUsagePreference() {
-        System.clearProperty(BC_NAMED_USAGE_PROP);
-    }
-
-    @Test
-    public void testLoadEncryptedDESPrivateKey() throws Exception {
-        testLoadEncryptedRSAPrivateKey("DES-EDE3");
-    }
-
-    @Test
-    public void testLoadEncryptedAESPrivateKey() {
-        for (BuiltinCiphers c : new BuiltinCiphers[]{
-            BuiltinCiphers.aes128cbc, BuiltinCiphers.aes192cbc, BuiltinCiphers.aes256cbc
-        }) {
-            if (!c.isSupported()) {
-                System.out.println("Skip unsupported encryption scheme: " + c.getName());
-                continue;
-            }
-
-            try {
-                testLoadEncryptedRSAPrivateKey("AES-" + c.getKeySize());
-            } catch (Exception e) {
-                fail("Failed (" + e.getClass().getSimpleName() + " to load key for " + c.getName() + ": " + e.getMessage());
-            }
-        }
-    }
-
-    private KeyPair testLoadEncryptedRSAPrivateKey(String algorithm) throws IOException, GeneralSecurityException {
-        return testLoadRSAPrivateKey(DEFAULT_PASSWORD.replace(' ', '-') + "-RSA-" + algorithm.toUpperCase() + "-key");
-    }
-
-    @Test
-    public void testLoadUnencryptedRSAPrivateKey() throws Exception {
-        testLoadRSAPrivateKey(getClass().getSimpleName() + "-RSA-KeyPair");
-    }
-
-    @Test
-    public void testLoadUnencryptedDSSPrivateKey() throws Exception {
-        testLoadDSSPrivateKey(getClass().getSimpleName() + "-DSA-KeyPair");
-    }
-
-    private KeyPair testLoadDSSPrivateKey(String name) throws Exception {
-        return testLoadPrivateKey(name, DSAPublicKey.class, DSAPrivateKey.class);
-    }
-
-    @Test
-    public void testLoadUnencryptedECPrivateKey() throws Exception {
-        Assume.assumeTrue("EC not supported", SecurityUtils.isECCSupported());
-        for (ECCurves c : ECCurves.VALUES) {
-            if (!c.isSupported()) {
-                System.out.println("Skip unsupported curve: " + c.getName());
-                continue;
-            }
-
-            testLoadECPrivateKey(getClass().getSimpleName() + "-EC-" + c.getKeySize() + "-KeyPair");
-        }
-    }
-
-    private KeyPair testLoadECPrivateKey(String name) throws IOException, GeneralSecurityException {
-        return testLoadPrivateKey(name, ECPublicKey.class, ECPrivateKey.class);
-    }
-
-    private KeyPair testLoadRSAPrivateKey(String name) throws IOException, GeneralSecurityException {
-        return testLoadPrivateKey(name, RSAPublicKey.class, RSAPrivateKey.class);
-    }
-
-    private KeyPair testLoadPrivateKey(String name, Class<? extends PublicKey> pubType, Class<? extends PrivateKey> prvType)
-            throws IOException, GeneralSecurityException {
-        Path folder = getTestResourcesFolder();
-        Path file = folder.resolve(name);
-        KeyPair kpFile = testLoadPrivateKeyFile(file, pubType, prvType);
-        if (SecurityUtils.isBouncyCastleRegistered()) {
-            KeyPairResourceLoader bcLoader = SecurityUtils.getBouncycastleKeyPairResourceParser();
-            Collection<KeyPair> kpList = bcLoader.loadKeyPairs(file, TEST_PASSWORD_PROVIDER);
-            assertEquals(name + ": Mismatched loaded BouncyCastle keys count", 1, GenericUtils.size(kpList));
-
-            KeyPair kpBC = kpList.iterator().next();
-            assertTrue(name + ": Mismatched BouncyCastle vs. file values", KeyUtils.compareKeyPairs(kpFile, kpBC));
-        }
-
-        Class<?> clazz = getClass();
-        Package pkg = clazz.getPackage();
-        KeyPair kpResource = testLoadPrivateKeyResource(pkg.getName().replace('.', '/') + "/" + name, pubType, prvType);
-        assertTrue(name + ": Mismatched key file vs. resource values", KeyUtils.compareKeyPairs(kpFile, kpResource));
-        return kpResource;
-    }
-
-    private static KeyPair testLoadPrivateKeyResource(String name, Class<? extends PublicKey> pubType, Class<? extends PrivateKey> prvType) {
-        return testLoadPrivateKey(name, new ClassLoadableResourceKeyPairProvider(name), pubType, prvType);
-    }
-
-    private static KeyPair testLoadPrivateKeyFile(Path file, Class<? extends PublicKey> pubType, Class<? extends PrivateKey> prvType) {
-        return testLoadPrivateKey(file.toString(), new FileKeyPairProvider(file), pubType, prvType);
-    }
-
-    private static KeyPair testLoadPrivateKey(String resourceKey, AbstractResourceKeyPairProvider<?> provider,
-            Class<? extends PublicKey> pubType, Class<? extends PrivateKey> prvType) {
-        provider.setPasswordFinder(TEST_PASSWORD_PROVIDER);
-        Iterable<KeyPair> iterator = provider.loadKeys();
-        List<KeyPair> pairs = new ArrayList<>();
-        for (KeyPair kp : iterator) {
-            pairs.add(kp);
-        }
-
-        assertEquals("Mismatched loaded pairs count for " + resourceKey, 1, pairs.size());
-
-        KeyPair kp = pairs.get(0);
-        PublicKey pub = kp.getPublic();
-        assertNotNull("No public key extracted", pub);
-        assertTrue("Not an " + pubType.getSimpleName() + " public key for " + resourceKey, pubType.isAssignableFrom(pub.getClass()));
-
-        PrivateKey prv = kp.getPrivate();
-        assertNotNull("No private key extracted", prv);
-        assertTrue("Not an " + prvType.getSimpleName() + " private key for " + resourceKey, prvType.isAssignableFrom(prv.getClass()));
-
-        return kp;
-    }
-
-    @Test
-    public void testSetMaxDHGroupExchangeKeySizeByProperty() {
-        try {
-            for (int expected = SecurityUtils.MIN_DHGEX_KEY_SIZE; expected <= SecurityUtils.MAX_DHGEX_KEY_SIZE; expected += 1024) {
-                SecurityUtils.setMaxDHGroupExchangeKeySize(0);  // force detection
-                try {
-                    System.setProperty(SecurityUtils.MAX_DHGEX_KEY_SIZE_PROP, Integer.toString(expected));
-                    assertTrue("DH group not supported for key size=" + expected, SecurityUtils.isDHGroupExchangeSupported());
-                    assertEquals("Mismatched values", expected, SecurityUtils.getMaxDHGroupExchangeKeySize());
-                } finally {
-                    System.clearProperty(SecurityUtils.MAX_DHGEX_KEY_SIZE_PROP);
-                }
-            }
-        } finally {
-            SecurityUtils.setMaxDHGroupExchangeKeySize(0);  // force detection
-        }
-    }
-
-    @Test
-    public void testSetMaxDHGroupExchangeKeySizeProgrammatically() {
-        try {
-            for (int expected = SecurityUtils.MIN_DHGEX_KEY_SIZE; expected <= SecurityUtils.MAX_DHGEX_KEY_SIZE; expected += 1024) {
-                SecurityUtils.setMaxDHGroupExchangeKeySize(expected);
-                assertTrue("DH group not supported for key size=" + expected, SecurityUtils.isDHGroupExchangeSupported());
-                assertEquals("Mismatched values", expected, SecurityUtils.getMaxDHGroupExchangeKeySize());
-            }
-        } finally {
-            SecurityUtils.setMaxDHGroupExchangeKeySize(0);  // force detection
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/SelectorUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/SelectorUtilsTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/SelectorUtilsTest.java
deleted file mode 100644
index e13fa9b..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/SelectorUtilsTest.java
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sshd.common.util;
-
-import java.io.File;
-import java.util.Random;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.Assume;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class SelectorUtilsTest extends BaseTestSupport {
-    public SelectorUtilsTest() {
-        super();
-    }
-
-    @Test
-    public void testApplyLinuxSeparatorSlashifyRules() {
-        testApplySlashifyRules('/');
-    }
-
-    @Test
-    public void testApplyWindowsSeparatorSlashifyRules() {
-        testApplySlashifyRules('\\');
-    }
-
-    private void testApplySlashifyRules(char slash) {
-        for (String expected : new String[]{
-            null, "", getCurrentTestName(),
-            getClass().getSimpleName() + Character.toString(slash) + getCurrentTestName(),
-            Character.toString(slash)  + getClass().getSimpleName(),
-            Character.toString(slash)  + getClass().getSimpleName() + Character.toString(slash)  + getCurrentTestName()
-        }) {
-            String actual = SelectorUtils.applySlashifyRules(expected, slash);
-            assertSame("Mismatched results for '" + expected + "'", expected, actual);
-        }
-
-        String[] comps = {getClass().getSimpleName(),  getCurrentTestName()};
-        Random rnd = new Random(System.nanoTime());
-        StringBuilder sb = new StringBuilder(Byte.MAX_VALUE);
-        for (int index = 0; index < Long.SIZE; index++) {
-            if (sb.length() > 0) {
-                sb.setLength(0);        // start from scratch
-            }
-
-            boolean prepend = rnd.nextBoolean();
-            if (prepend) {
-                slashify(sb, rnd, slash);
-            }
-
-            sb.append(comps[0]);
-            for (int j = 1; j < comps.length; j++) {
-                slashify(sb, rnd, slash);
-                sb.append(comps[j]);
-            }
-
-            boolean append = rnd.nextBoolean();
-            if (append) {
-                slashify(sb, rnd, slash);
-            }
-
-            String path = sb.toString();
-            sb.setLength(0);
-            if (prepend) {
-                sb.append(slash);
-            }
-
-            sb.append(comps[0]);
-            for (int j = 1; j < comps.length; j++) {
-                sb.append(slash).append(comps[j]);
-            }
-
-            if (append) {
-                sb.append(slash).append('.');
-            }
-
-            String expected = sb.toString();
-            String actual = SelectorUtils.applySlashifyRules(path, slash);
-            assertEquals("Mismatched results for path=" + path, expected, actual);
-        }
-    }
-
-    private static int slashify(StringBuilder sb, Random rnd, char slash) {
-        int slashes = 1 /* at least one slash */ + rnd.nextInt(Byte.SIZE);
-        for (int k = 0; k < slashes; k++) {
-            sb.append(slash);
-        }
-
-        return slashes;
-    }
-
-    @Test
-    public void testTranslateToFileSystemPath() {
-        String path = getClass().getPackage().getName().replace('.', File.separatorChar)
-                    + File.separator + getClass().getSimpleName()
-                    + File.separator + getCurrentTestName();
-        for (String expected : new String[] {null, "", path}) {
-            String actual = SelectorUtils.translateToFileSystemPath(expected, File.separator, File.separator);
-            assertSame("Mismatched instance for translated result", expected, actual);
-        }
-
-        for (String fsSeparator : new String[] {String.valueOf('.'), "##"}) {
-            String expected = path.replace(File.separator, fsSeparator);
-            String actual = SelectorUtils.translateToFileSystemPath(path, File.separator, fsSeparator);
-            assertEquals("Mismatched translation result for separator='" + fsSeparator + "'", expected, actual);
-
-            actual = SelectorUtils.translateToFileSystemPath(actual, fsSeparator, File.separator);
-            assertEquals("Mismatched translation revert for separator='" + fsSeparator + "'", path, actual);
-        }
-    }
-
-    @Test
-    public void testAbsoluteWindowsPathTranslation() {
-        Assume.assumeTrue("Not tested on Windows", OsUtils.isWin32());
-        String expected = detectTargetFolder().toString();
-        for (String prefix : new String[]{"", "/"}) {
-            String actual = SelectorUtils.translateToLocalPath(prefix + expected.replace('/', File.separatorChar));
-            assertEquals("Mismatched result for prefix='" + prefix + "'", expected, actual);
-        }
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/ThreadUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/ThreadUtilsTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/ThreadUtilsTest.java
deleted file mode 100644
index 50fda34..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/ThreadUtilsTest.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.common.util;
-
-import java.util.Collection;
-
-import org.apache.sshd.common.util.threads.CloseableExecutorService;
-import org.apache.sshd.common.util.threads.ThreadUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class ThreadUtilsTest extends BaseTestSupport {
-    public ThreadUtilsTest() {
-        super();
-    }
-
-    @Test
-    public void testProtectExecutorServiceShutdown() {
-        for (boolean shutdownOnExit : new boolean[]{true, false}) {
-            assertNull("Unexpected instance for shutdown=" + shutdownOnExit, ThreadUtils.protectExecutorServiceShutdown(null, shutdownOnExit));
-        }
-
-        CloseableExecutorService service = ThreadUtils.newSingleThreadExecutor("pool");
-        try {
-            assertSame("Unexpected wrapped instance", service, ThreadUtils.protectExecutorServiceShutdown(service, true));
-
-            CloseableExecutorService wrapped = ThreadUtils.protectExecutorServiceShutdown(service, false);
-            try {
-                assertNotSame("No wrapping occurred", service, wrapped);
-
-                wrapped.shutdown();
-                assertTrue("Wrapped service not shutdown", wrapped.isShutdown());
-                assertFalse("Protected service is shutdown", service.isShutdown());
-
-                Collection<?> running = wrapped.shutdownNow();
-                assertTrue("Non-empty runners list", running.isEmpty());
-                assertTrue("Wrapped service not shutdownNow", wrapped.isShutdown());
-                assertFalse("Protected service is shutdownNow", service.isShutdown());
-            } finally {
-                wrapped.shutdownNow();  // just in case
-            }
-        } finally {
-            service.shutdownNow();  // just in case...
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/ValidateUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/ValidateUtilsTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/ValidateUtilsTest.java
deleted file mode 100644
index 80c2e7f..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/ValidateUtilsTest.java
+++ /dev/null
@@ -1,44 +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.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class ValidateUtilsTest extends BaseTestSupport {
-    public ValidateUtilsTest() {
-        super();
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void checkNotNull() {
-        ValidateUtils.checkNotNull(getClass(), getCurrentTestName());
-        ValidateUtils.checkNotNull(null, getCurrentTestName());
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/VersionInfoTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/VersionInfoTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/VersionInfoTest.java
deleted file mode 100644
index 3b742a9..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/VersionInfoTest.java
+++ /dev/null
@@ -1,52 +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.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class VersionInfoTest extends BaseTestSupport {
-    public VersionInfoTest() {
-        super();
-    }
-
-    @Test
-    public void testLessThan4Components() {
-        VersionInfo expected = new VersionInfo(73, 65);
-        VersionInfo actual = VersionInfo.parse(NumberUtils.join('.', expected.getMajorVersion(), expected.getMinorVersion()));
-        assertEquals("Mismatched result", expected, actual);
-    }
-
-    @Test
-    public void testMoreThan4Components() {
-        VersionInfo expected = new VersionInfo(7, 3, 6, 5);
-        VersionInfo actual = VersionInfo.parse(expected.toString() + ".3.7.7.7.3.4.7");
-        assertEquals("Mismatched result", expected, actual);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/buffer/BufferTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/buffer/BufferTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/buffer/BufferTest.java
deleted file mode 100644
index be3f796..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/buffer/BufferTest.java
+++ /dev/null
@@ -1,103 +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.buffer;
-
-import java.io.ByteArrayOutputStream;
-import java.io.DataOutputStream;
-import java.nio.charset.StandardCharsets;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class BufferTest extends BaseTestSupport {
-    public BufferTest() {
-        super();
-    }
-
-    @Test
-    public void testGetLong() throws Exception {
-        long expected = 1234567890123456789L;
-
-        try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) {
-            try (DataOutputStream ds = new DataOutputStream(stream)) {
-                ds.writeLong(expected);
-            }
-
-            Buffer buffer = new ByteArrayBuffer(stream.toByteArray());
-            assertEquals("Mismatched recovered value", expected, buffer.getLong());
-        }
-    }
-
-    @Test
-    public void testPutCharsWithNullOrEmptyValue() {
-        Buffer buffer = new ByteArrayBuffer(Integer.SIZE);
-        for (char[] chars : new char[][]{null, GenericUtils.EMPTY_CHAR_ARRAY}) {
-            buffer.putChars(chars);
-
-            String value = buffer.getString();
-            assertEquals("Mismatched value for " + ((chars == null) ? "null" : "empty") + " characters", "", value);
-        }
-    }
-
-    @Test
-    public void testPutCharsOnNonEmptyValue() {
-        String expected = getCurrentTestName();
-        Buffer buffer = new ByteArrayBuffer(expected.length() + Byte.SIZE);
-        buffer.putChars(expected.toCharArray());
-
-        String actual = buffer.getString();
-        assertEquals("Mismatched recovered values", expected, actual);
-    }
-
-    @Test
-    public void testPutAndWipeChars() {
-        String expected = getCurrentTestName();
-        char[] chars = expected.toCharArray();
-        Buffer buffer = new ByteArrayBuffer(chars.length + Byte.SIZE);
-        buffer.putAndWipeChars(chars);
-
-        String actual = buffer.getString();
-        assertEquals("Mismatched recovered values", expected, actual);
-
-        for (int index = 0; index < chars.length; index++) {
-            assertEquals("Character not wiped at index=" + index, 0, chars[index]);
-        }
-    }
-
-    @Test
-    public void testPutAndWipeBytes() {
-        String expected = getCurrentTestName();
-        byte[] bytes = expected.getBytes(StandardCharsets.UTF_8);
-        Buffer buffer = new ByteArrayBuffer(bytes.length + Byte.SIZE);
-        buffer.putAndWipeBytes(bytes);
-        String actual = buffer.getString();
-        assertEquals("Mismatched recovered values", expected, actual);
-
-        for (int index = 0; index < bytes.length; index++) {
-            assertEquals("Value not wiped at index=" + index, 0, bytes[index]);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/buffer/BufferUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/buffer/BufferUtilsTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/buffer/BufferUtilsTest.java
deleted file mode 100644
index b106bc6..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/buffer/BufferUtilsTest.java
+++ /dev/null
@@ -1,73 +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.buffer;
-
-import java.nio.charset.StandardCharsets;
-import java.util.Random;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class BufferUtilsTest extends BaseTestSupport {
-    public BufferUtilsTest() {
-        super();
-    }
-
-    @Test
-    public void testHexEncodeDecode() {
-        String expValue = getClass().getName() + "#" + getCurrentTestName();
-        byte[] expData = expValue.getBytes(StandardCharsets.UTF_8);
-        for (char sep : new char[]{BufferUtils.EMPTY_HEX_SEPARATOR, ':'}) {
-            String hexData = BufferUtils.toHex(sep, expData);
-            byte[] actData = BufferUtils.decodeHex(sep, hexData);
-            String actValue = new String(actData, StandardCharsets.UTF_8);
-            String sepName = (BufferUtils.EMPTY_HEX_SEPARATOR == sep) ? "EMPTY" : Character.toString(sep);
-            outputDebugMessage("Decode(sep=%s) expected=%s, actual=%s", sepName, expValue, actValue);
-            assertArrayEquals("Mismatched result for sep='" + sepName + "'", expData, actData);
-        }
-    }
-
-    @Test
-    public void testGetCompactClone() {
-        byte[] expected = getCurrentTestName().getBytes(StandardCharsets.UTF_8);
-        final int testOffset = Byte.SIZE / 2;
-        byte[] data = new byte[expected.length + 2 * testOffset];
-        Random rnd = new Random(System.nanoTime());
-        rnd.nextBytes(data);
-        System.arraycopy(expected, 0, data, testOffset, expected.length);
-
-        Buffer buf = ByteArrayBuffer.getCompactClone(data, testOffset, expected.length);
-        assertEquals("Mismatched cloned buffer read position", 0, buf.rpos());
-        assertEquals("Mismatched cloned buffer available size", expected.length, buf.available());
-
-        byte[] actual = buf.array();
-        assertNotSame("Original data not cloned", data, actual);
-        assertArrayEquals("Mismatched cloned contents", expected, actual);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/closeable/CloseableUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/closeable/CloseableUtilsTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/closeable/CloseableUtilsTest.java
deleted file mode 100644
index 8a6cb18..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/closeable/CloseableUtilsTest.java
+++ /dev/null
@@ -1,163 +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.closeable;
-
-import java.io.IOException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import org.apache.sshd.common.Closeable;
-import org.apache.sshd.common.future.CloseFuture;
-import org.apache.sshd.common.future.DefaultCloseFuture;
-import org.apache.sshd.common.future.SshFutureListener;
-import org.apache.sshd.common.util.threads.ThreadUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class CloseableUtilsTest extends BaseTestSupport {
-    public CloseableUtilsTest() {
-        super();
-    }
-
-    @Test
-    public void testCloseImmediateNotCalledIfAlreadyClosed() throws IOException {
-        Closeable closeable = new IoBaseCloseable() {
-            @Override
-            public CloseFuture close(boolean immediately) {
-                fail("Unexpected call to close(" + immediately + ")");
-                return null;
-            }
-
-            @Override
-            public void addCloseFutureListener(SshFutureListener<CloseFuture> listener) {
-                fail("Unexpected call to addCloseFutureListener");
-            }
-
-            @Override
-            public void removeCloseFutureListener(SshFutureListener<CloseFuture> listener) {
-                fail("Unexpected call to removeCloseFutureListener");
-            }
-
-            @Override
-            public boolean isClosed() {
-                return true;
-            }
-
-            @Override
-            public boolean isClosing() {
-                return false;
-            }
-        };
-        closeable.close();
-    }
-
-    @Test
-    public void testCloseImmediateNotCalledIfIsClosing() throws IOException {
-        Closeable closeable = new IoBaseCloseable() {
-            @Override
-            public CloseFuture close(boolean immediately) {
-                fail("Unexpected call to close(" + immediately + ")");
-                return null;
-            }
-
-            @Override
-            public void addCloseFutureListener(SshFutureListener<CloseFuture> listener) {
-                fail("Unexpected call to addCloseFutureListener");
-            }
-
-            @Override
-            public void removeCloseFutureListener(SshFutureListener<CloseFuture> listener) {
-                fail("Unexpected call to removeCloseFutureListener");
-            }
-
-            @Override
-            public boolean isClosed() {
-                return false;
-            }
-
-            @Override
-            public boolean isClosing() {
-                return true;
-            }
-        };
-        closeable.close();
-    }
-
-    @Test
-    public void testCloseImmediateCalledAndWait() throws Exception {
-        DefaultCloseFuture future = new DefaultCloseFuture(this, this);
-        AtomicInteger callsCount = new AtomicInteger(0);
-        Closeable closeable = new IoBaseCloseable() {
-            @Override
-            public CloseFuture close(boolean immediately) {
-                assertTrue("Closure is not immediate", immediately);
-                assertEquals("Multiple close immediate calls", 1, callsCount.incrementAndGet());
-                return future;
-            }
-
-            @Override
-            public void addCloseFutureListener(SshFutureListener<CloseFuture> listener) {
-                fail("Unexpected call to addCloseFutureListener");
-            }
-
-            @Override
-            public void removeCloseFutureListener(SshFutureListener<CloseFuture> listener) {
-                fail("Unexpected call to removeCloseFutureListener");
-            }
-
-            @Override
-            public boolean isClosed() {
-                return false;
-            }
-
-            @Override
-            public boolean isClosing() {
-                return false;
-            }
-        };
-
-        ExecutorService service = ThreadUtils.newSingleThreadExecutor(getCurrentTestName());
-        try {
-            Future<?> task = service.submit((Runnable) () -> {
-                try {
-                    closeable.close();
-                } catch (IOException e) {
-                    throw new RuntimeException(e);
-                }
-            });
-            future.setClosed();  // signal close complete
-            task.get(5L, TimeUnit.SECONDS);  // make sure #await call terminated
-            assertEquals("Close immediate not called", 1, callsCount.get());
-        } finally {
-            service.shutdownNow();
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/io/EmptyInputStreamTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/io/EmptyInputStreamTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/io/EmptyInputStreamTest.java
deleted file mode 100644
index 24f18e3..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/io/EmptyInputStreamTest.java
+++ /dev/null
@@ -1,120 +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.io;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-import org.apache.sshd.common.util.buffer.BufferUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class EmptyInputStreamTest extends BaseTestSupport {
-    public EmptyInputStreamTest() {
-        super();
-    }
-
-    @Test
-    public void testEmptyInputStream() throws IOException {
-        try (EmptyInputStream in = new EmptyInputStream()) {
-            testEmptyInputStream(in, false);
-        }
-    }
-
-    @Test
-    public void testCloseableEmptyInputStream() throws IOException {
-        try (EmptyInputStream in = new CloseableEmptyInputStream()) {
-            testEmptyInputStream(in, true);
-        }
-    }
-
-    private void testEmptyInputStream(InputStream in, boolean failAfterClose) throws IOException {
-        testEmptyInputStream("open", in, false);
-        in.close();
-        testEmptyInputStream("closed", in, failAfterClose);
-    }
-
-    private void testEmptyInputStream(String message, InputStream in, boolean errorExpected) {
-        assertFalse(message + ": unexpected markSupported()", in.markSupported());
-        try {
-            in.mark(Long.SIZE);
-            fail(message + ": unexpected mark success");
-        } catch (UnsupportedOperationException e) {
-            // expected
-        }
-
-        try {
-            int len = in.available();
-            assertFalse(message + ": Unexpected success in available(): " + len, errorExpected);
-            assertEquals(message + ": Mismatched available() result", 0, len);
-        } catch (IOException e) {
-            assertTrue(message + ": Unexpected error on available(): " + e.getMessage(), errorExpected);
-        }
-
-        try {
-            int data = in.read();
-            assertFalse(message + ": Unexpected success in read(): " + data, errorExpected);
-            assertEquals(message + ": Mismatched read() result", -1, data);
-        } catch (IOException e) {
-            assertTrue(message + ": Unexpected error on read(): " + e.getMessage(), errorExpected);
-        }
-
-        byte[] bytes = new byte[Byte.SIZE];
-        try {
-            int len = in.read(bytes);
-            assertFalse(message + ": Unexpected success in read([]): " + BufferUtils.toHex(':', bytes), errorExpected);
-            assertEquals(message + ": Mismatched read([]) result", -1, len);
-        } catch (IOException e) {
-            assertTrue(message + ": Unexpected error on read([]): " + e.getMessage(), errorExpected);
-        }
-
-        try {
-            int len = in.read(bytes, 0, bytes.length);
-            assertFalse(message + ": Unexpected success in read([],int,int): " + BufferUtils.toHex(':', bytes), errorExpected);
-            assertEquals(message + ": Mismatched read([],int,int) result", -1, len);
-        } catch (IOException e) {
-            assertTrue(message + ": Unexpected error on read([],int,int): " + e.getMessage(), errorExpected);
-        }
-
-        try {
-            long len = in.skip(Byte.MAX_VALUE);
-            assertFalse(message + ": Unexpected success in skip(): " + len, errorExpected);
-            assertEquals(message + ": Mismatched skip() result", 0L, len);
-        } catch (IOException e) {
-            assertTrue(message + ": Unexpected error on skip(): " + e.getMessage(), errorExpected);
-        }
-
-        try {
-            in.reset();
-            assertFalse(message + ": Unexpected success in reset()", errorExpected);
-        } catch (IOException e) {
-            assertTrue(message + ": Unexpected error on reset(): " + e.getMessage(), errorExpected);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/io/IoUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/io/IoUtilsTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/io/IoUtilsTest.java
deleted file mode 100644
index 6d7a8fa..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/io/IoUtilsTest.java
+++ /dev/null
@@ -1,61 +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.io;
-
-import java.nio.file.LinkOption;
-
-import org.apache.sshd.common.util.NumberUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class IoUtilsTest extends BaseTestSupport {
-    public IoUtilsTest() {
-        super();
-    }
-
-    @Test
-    public void testFollowLinks() {
-        assertTrue("Null ?", IoUtils.followLinks((LinkOption[]) null));
-        assertTrue("Empty ?", IoUtils.followLinks(IoUtils.EMPTY_LINK_OPTIONS));
-        assertFalse("No-follow ?", IoUtils.followLinks(IoUtils.getLinkOptions(false)));
-    }
-
-    @Test
-    public void testGetEOLBytes() {
-        byte[] expected = IoUtils.getEOLBytes();
-        assertTrue("Empty bytes", NumberUtils.length(expected) > 0);
-
-        for (int index = 1; index < Byte.SIZE; index++) {
-            byte[] actual = IoUtils.getEOLBytes();
-            assertNotSame("Same bytes received at iteration " + index, expected, actual);
-            assertArrayEquals("Mismatched bytes at iteration " + index, expected, actual);
-        }
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/io/LimitInputStreamTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/io/LimitInputStreamTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/io/LimitInputStreamTest.java
deleted file mode 100644
index 0f179b8..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/io/LimitInputStreamTest.java
+++ /dev/null
@@ -1,118 +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.io;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Path;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class LimitInputStreamTest extends BaseTestSupport {
-    public LimitInputStreamTest() {
-        super();
-    }
-
-    @Test
-    public void testReadLimit() throws IOException {
-        Path targetPath = detectTargetFolder();
-        Path rootFolder = assertHierarchyTargetFolderExists(targetPath.resolve(getClass().getSimpleName()));
-        Path inputFile = rootFolder.resolve(getCurrentTestName() + ".bin");
-        byte[] data = (getClass().getName() + "#" + getCurrentTestName()).getBytes(StandardCharsets.UTF_8);
-        Files.write(inputFile, data);
-
-        try (InputStream in = Files.newInputStream(inputFile)) {
-            int maxLen = data.length / 2;
-            byte[] expected = new byte[maxLen];
-            System.arraycopy(data, 0, expected, 0, expected.length);
-
-            byte[] actual = new byte[expected.length];
-            try (LimitInputStream limited = new LimitInputStream(in, expected.length)) {
-                assertTrue("Limited stream not marked as open", limited.isOpen());
-                assertEquals("Mismatched initial available data size", expected.length, limited.available());
-
-                int readLen = limited.read(actual);
-                assertEquals("Incomplete actual data read", actual.length, readLen);
-                assertArrayEquals("Mismatched read data", expected, actual);
-                assertEquals("Mismatched remaining available data size", 0, limited.available());
-
-                readLen = limited.read();
-                assertTrue("Unexpected success to read one more byte: " + readLen, readLen < 0);
-
-                readLen = limited.read(actual);
-                assertTrue("Unexpected success to read extra buffer: " + readLen, readLen < 0);
-
-                limited.close();
-                assertFalse("Limited stream still marked as open", limited.isOpen());
-
-                try {
-                    readLen = limited.read();
-                    fail("Unexpected one byte read success after close");
-                } catch (IOException e) {
-                    // expected
-                }
-
-                try {
-                    readLen = limited.read(actual);
-                    fail("Unexpected buffer read success after close: " + readLen);
-                } catch (IOException e) {
-                    // expected
-                }
-
-                try {
-                    readLen = limited.read(actual);
-                    fail("Unexpected buffer read success after close: " + readLen);
-                } catch (IOException e) {
-                    // expected
-                }
-
-                try {
-                    readLen = (int) limited.skip(Byte.SIZE);
-                    fail("Unexpected skip success after close: " + readLen);
-                } catch (IOException e) {
-                    // expected
-                }
-
-                try {
-                    readLen = limited.available();
-                    fail("Unexpected available success after close: " + readLen);
-                } catch (IOException e) {
-                    // expected
-                }
-            }
-
-            // make sure underlying stream not closed
-            int readLen = in.read(actual);
-            assertEquals("Incomplete extra data read", Math.min(actual.length, data.length - expected.length), readLen);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/io/ModifiableFileWatcherTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/io/ModifiableFileWatcherTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/io/ModifiableFileWatcherTest.java
deleted file mode 100644
index 70c548b..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/io/ModifiableFileWatcherTest.java
+++ /dev/null
@@ -1,82 +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.io;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.attribute.PosixFilePermission;
-import java.util.Collection;
-import java.util.Date;
-import java.util.Map;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.OsUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class ModifiableFileWatcherTest extends BaseTestSupport {
-    public ModifiableFileWatcherTest() {
-        super();
-    }
-
-    @Test   // see SSHD-606
-    public void testValidateStrictConfigFilePermissions() throws IOException {
-        Path file = getTempTargetRelativeFile(getClass().getSimpleName(), getCurrentTestName());
-        outputDebugMessage("%s deletion result=%s", file, Files.deleteIfExists(file));
-        assertNull("Unexpected violation for non-existent file: " + file, ModifiableFileWatcher.validateStrictConfigFilePermissions(file));
-
-        assertHierarchyTargetFolderExists(file.getParent());
-        try (OutputStream output = Files.newOutputStream(file)) {
-            output.write((getClass().getName() + "#" + getCurrentTestName() + "@" + new Date(System.currentTimeMillis())).getBytes(StandardCharsets.UTF_8));
-        }
-
-        Collection<PosixFilePermission> perms = IoUtils.getPermissions(file);
-        if (GenericUtils.isEmpty(perms)) {
-            assertNull("Unexpected violation for no permissions file: " + file, ModifiableFileWatcher.validateStrictConfigFilePermissions(file));
-        } else if (OsUtils.isUNIX()) {
-            Map.Entry<String, Object> violation = null;
-            for (PosixFilePermission p : ModifiableFileWatcher.STRICTLY_PROHIBITED_FILE_PERMISSION) {
-                if (perms.contains(p)) {
-                    violation = ModifiableFileWatcher.validateStrictConfigFilePermissions(file);
-                    assertNotNull("Unexpected success for permission=" + p + " of file " + file + " permissions=" + perms, violation);
-                    break;
-                }
-            }
-
-            if (violation == null) {    // we do not expected a failure if no permissions have been violated
-                assertNull("Unexpected UNIX violation for file " + file + " permissions=" + perms, ModifiableFileWatcher.validateStrictConfigFilePermissions(file));
-            }
-        } else {
-            assertNull("Unexpected Windows violation for file " + file + " permissions=" + perms, ModifiableFileWatcher.validateStrictConfigFilePermissions(file));
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/io/NoCloseInputStreamTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/io/NoCloseInputStreamTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/io/NoCloseInputStreamTest.java
deleted file mode 100644
index 2990fa6..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/io/NoCloseInputStreamTest.java
+++ /dev/null
@@ -1,89 +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.io;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.Date;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class NoCloseInputStreamTest extends BaseTestSupport {
-    public NoCloseInputStreamTest() {
-        super();
-    }
-
-    @Test
-    public void testCanKeepReadingAfterClose() throws IOException {
-        byte[] expected = (getClass().getName() + "#" + getCurrentTestName() + "@" + new Date()).getBytes(StandardCharsets.UTF_8);
-        Path dir = createTempClassFolder();
-        Path file = Files.write(dir.resolve(getCurrentTestName() + ".txt"), expected);
-        try (InputStream fileStream = Files.newInputStream(file);
-             InputStream shielded = new NoCloseInputStream(fileStream)) {
-            int index = 0;
-
-            for (; index < (expected.length / 2); index++) {
-                shielded.close();
-
-                int readValue = shielded.read();
-                if (readValue == -1) {
-                    fail("Premature EOF after shield read of " + index + " bytes");
-                }
-
-                byte expValue = expected[index];
-                byte actValue = (byte) (readValue & 0xFF);
-                if (expValue != actValue) {
-                    fail("Mismatched shielded read value after " + index + " bytes");
-                }
-            }
-
-            for (; index < expected.length; index++) {
-                int readValue = fileStream.read();
-                if (readValue == -1) {
-                    fail("Premature EOF after original read of " + index + " bytes");
-                }
-                byte expValue = expected[index];
-                byte actValue = (byte) (readValue & 0xFF);
-                if (expValue != actValue) {
-                    fail("Mismatched original read value after " + index + " bytes");
-                }
-            }
-
-            int readValue = shielded.read();
-            assertEquals("Shielded EOF not signalled", -1, readValue);
-
-            readValue = fileStream.read();
-            assertEquals("Original EOF not signalled", -1, readValue);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/io/NoCloseOutputStreamTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/io/NoCloseOutputStreamTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/io/NoCloseOutputStreamTest.java
deleted file mode 100644
index fc211c3..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/io/NoCloseOutputStreamTest.java
+++ /dev/null
@@ -1,69 +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.io;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.Date;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class NoCloseOutputStreamTest extends BaseTestSupport {
-    public NoCloseOutputStreamTest() {
-        super();
-    }
-
-    @Test
-    public void testCanKeepWritingAfterClose() throws IOException {
-        Path dir = createTempClassFolder();
-        Path file = dir.resolve(getCurrentTestName() + ".txt");
-        Files.deleteIfExists(file);
-
-        String expectedOutput = getClass().getName() + "#" + getCurrentTestName() + "@" + new Date();
-        byte[] expected = expectedOutput.getBytes(StandardCharsets.UTF_8);
-        try (OutputStream fileStream = Files.newOutputStream(file);
-             OutputStream shielded = new NoCloseOutputStream(fileStream)) {
-            int index = 0;
-            for (; index < (expected.length / 2); index++) {
-                shielded.close();
-                shielded.write(expected[index] & 0xFF);
-            }
-
-            fileStream.write(expected, index, expected.length - index);
-        }
-
-        byte[] actual = Files.readAllBytes(file);
-        String actualOutput = new String(actual, StandardCharsets.UTF_8);
-        assertEquals(expectedOutput, actualOutput);
-    }
-}


[38/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common utilities code from sshd-core into sshd-common (new artifact)

Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/BufferUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/BufferUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/BufferUtils.java
new file mode 100644
index 0000000..c13cd9e
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/BufferUtils.java
@@ -0,0 +1,604 @@
+/*
+ * 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.buffer;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.StreamCorruptedException;
+import java.util.function.IntUnaryOperator;
+import java.util.logging.Level;
+
+import org.apache.sshd.common.PropertyResolver;
+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;
+import org.apache.sshd.common.util.logging.SimplifiedLog;
+
+/**
+ * TODO Add javadoc
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class BufferUtils {
+    public static final char DEFAULT_HEX_SEPARATOR = ' ';
+    public static final char EMPTY_HEX_SEPARATOR = '\0';
+    public static final String HEX_DIGITS = "0123456789abcdef";
+
+    public static final String HEXDUMP_CHUNK_SIZE = "sshd-hexdump-chunk-size";
+    public static final int DEFAULT_HEXDUMP_CHUNK_SIZE = 64;
+    public static final Level DEFAULT_HEXDUMP_LEVEL = Level.FINEST;
+
+    public static final IntUnaryOperator DEFAULT_BUFFER_GROWTH_FACTOR = BufferUtils::getNextPowerOf2;
+
+    /**
+     * Maximum value of a {@code uint32} field
+     */
+    public static final long MAX_UINT32_VALUE = 0x0FFFFFFFFL;
+
+    /**
+     * Maximum value of a {@code uint8} field
+     */
+    public static final int MAX_UINT8_VALUE = 0x0FF;
+
+    /**
+     * Private Constructor
+     */
+    private BufferUtils() {
+        throw new UnsupportedOperationException("No instance allowed");
+    }
+
+    public static void dumpHex(SimplifiedLog logger, Level level, String prefix, PropertyResolver resolver, char sep, byte... data) {
+        dumpHex(logger, level, prefix, resolver, sep, data, 0, NumberUtils.length(data));
+    }
+
+    public static void dumpHex(SimplifiedLog logger, Level level, String prefix, PropertyResolver resolver, char sep, byte[] data, int offset, int len) {
+        dumpHex(logger, level, prefix, sep, resolver.getIntProperty(HEXDUMP_CHUNK_SIZE, DEFAULT_HEXDUMP_CHUNK_SIZE), data, offset, len);
+    }
+
+    public static void dumpHex(SimplifiedLog logger, Level level, String prefix, char sep, int chunkSize, byte... data) {
+        dumpHex(logger, level, prefix, sep, chunkSize, data, 0, NumberUtils.length(data));
+    }
+
+    public static void dumpHex(SimplifiedLog logger, Level level, String prefix, char sep, int chunkSize, byte[] data, int offset, int len) {
+        if ((logger == null) || (level == null) || (!logger.isEnabled(level))) {
+            return;
+        }
+
+        StringBuilder sb = new StringBuilder(chunkSize * 3 /* HEX */ + prefix.length() + Long.SIZE /* some extra */);
+        sb.append(prefix);
+        for (int remainLen = len, chunkIndex = 1, curOffset = offset, totalLen = 0; remainLen > 0; chunkIndex++) {
+            sb.setLength(prefix.length());    // reset for next chunk
+
+            sb.append(" [chunk #").append(chunkIndex).append(']');
+
+            int dumpSize = Math.min(chunkSize, remainLen);
+            totalLen += dumpSize;
+            sb.append('(').append(totalLen).append('/').append(len).append(')');
+
+            try {
+                appendHex(sb.append(' '), data, curOffset, dumpSize, sep);
+            } catch (IOException e) {   // unexpected
+                sb.append(e.getClass().getSimpleName()).append(": ").append(e.getMessage());
+            }
+
+            // Pad the last (incomplete) line to align its data view
+            for (int index = dumpSize; index < chunkSize; index++) {
+                if (sep != EMPTY_HEX_SEPARATOR) {
+                    sb.append(' ');
+                }
+                sb.append("  ");
+            }
+
+            sb.append("    ");
+            for (int pos = curOffset, l = 0; l < dumpSize; pos++, l++) {
+                int b = data[pos] & 0xFF;
+                if ((b > ' ') && (b < 0x7E)) {
+                    sb.append((char) b);
+                } else {
+                    sb.append('.');
+                }
+            }
+
+            logger.log(level, sb.toString());
+            remainLen -= dumpSize;
+            curOffset += dumpSize;
+        }
+    }
+
+    public static String toHex(byte... array) {
+        return toHex(array, 0, NumberUtils.length(array));
+    }
+
+    public static String toHex(char sep, byte... array) {
+        return toHex(array, 0, NumberUtils.length(array), sep);
+    }
+
+    public static String toHex(byte[] array, int offset, int len) {
+        return toHex(array, offset, len, DEFAULT_HEX_SEPARATOR);
+    }
+
+    public static String toHex(byte[] array, int offset, int len, char sep) {
+        if (len <= 0) {
+            return "";
+        }
+
+        try {
+            return appendHex(new StringBuilder(len * 3 /* 2 HEX + sep */), array, offset, len, sep).toString();
+        } catch (IOException e) {   // unexpected
+            return e.getClass().getSimpleName() + ": " + e.getMessage();
+        }
+    }
+
+    public static <A extends Appendable> A appendHex(A sb, char sep, byte... array) throws IOException {
+        return appendHex(sb, array, 0, NumberUtils.length(array), sep);
+    }
+
+    public static <A extends Appendable> A appendHex(A sb, byte[] array, int offset, int len, char sep) throws IOException {
+        if (len <= 0) {
+            return sb;
+        }
+
+        for (int curOffset = offset, maxOffset = offset + len; curOffset < maxOffset; curOffset++) {
+            byte b = array[curOffset];
+            if ((curOffset > offset) && (sep != EMPTY_HEX_SEPARATOR)) {
+                sb.append(sep);
+            }
+            sb.append(HEX_DIGITS.charAt((b >> 4) & 0x0F));
+            sb.append(HEX_DIGITS.charAt(b & 0x0F));
+        }
+
+        return sb;
+    }
+
+    /**
+     * @param separator The separator between the HEX values - may be {@link #EMPTY_HEX_SEPARATOR}
+     * @param csq The {@link CharSequence} containing the HEX encoded bytes
+     * @return The decoded bytes
+     * @throws IllegalArgumentException If invalid HEX sequence length
+     * @throws NumberFormatException If invalid HEX characters found
+     * @see #decodeHex(char, CharSequence, int, int)
+     */
+    public static byte[] decodeHex(char separator, CharSequence csq) {
+        return decodeHex(separator, csq, 0, GenericUtils.length(csq));
+    }
+
+    /**
+     * @param separator The separator between the HEX values - may be {@link #EMPTY_HEX_SEPARATOR}
+     * @param csq The {@link CharSequence} containing the HEX encoded bytes
+     * @param start Start offset of the HEX sequence (inclusive)
+     * @param end End offset of the HEX sequence (exclusive)
+     * @return The decoded bytes
+     * @throws IllegalArgumentException If invalid HEX sequence length
+     * @throws NumberFormatException If invalid HEX characters found
+     */
+    public static byte[] decodeHex(char separator, CharSequence csq, int start, int end) {
+        int len = end - start;
+        ValidateUtils.checkTrue(len >= 0, "Bad HEX sequence length: %d", len);
+        if (len == 0) {
+            return GenericUtils.EMPTY_BYTE_ARRAY;
+        }
+
+        int delta = 2;
+        byte[] bytes;
+        if (separator != EMPTY_HEX_SEPARATOR) {
+            // last character cannot be the separator
+            ValidateUtils.checkTrue((len % 3) == 2, "Invalid separated HEX sequence length: %d", len);
+            bytes = new byte[(len + 1) / 3];
+            delta++;
+        } else {
+            ValidateUtils.checkTrue((len & 0x01) == 0, "Invalid contiguous HEX sequence length: %d", len);
+            bytes = new byte[len >>> 1];
+        }
+
+        int writeLen = 0;
+        for (int curPos = start; curPos < end; curPos += delta, writeLen++) {
+            bytes[writeLen] = fromHex(csq.charAt(curPos), csq.charAt(curPos + 1));
+        }
+        assert writeLen == bytes.length;
+
+        return bytes;
+    }
+
+    /**
+     * @param <S> The {@link OutputStream} generic type
+     * @param stream The target {@link OutputStream}
+     * @param separator The separator between the HEX values - may be {@link #EMPTY_HEX_SEPARATOR}
+     * @param csq The {@link CharSequence} containing the HEX encoded bytes
+     * @return The number of bytes written to the stream
+     * @throws IOException If failed to write
+     * @throws IllegalArgumentException If invalid HEX sequence length
+     * @throws NumberFormatException If invalid HEX characters found
+     * @see #decodeHex(OutputStream, char, CharSequence, int, int)
+     */
+    public static <S extends OutputStream> int decodeHex(S stream, char separator, CharSequence csq) throws IOException {
+        return decodeHex(stream, separator, csq, 0, GenericUtils.length(csq));
+    }
+
+    /**
+     * @param <S> The {@link OutputStream} generic type
+     * @param stream The target {@link OutputStream}
+     * @param separator The separator between the HEX values - may be {@link #EMPTY_HEX_SEPARATOR}
+     * @param csq The {@link CharSequence} containing the HEX encoded bytes
+     * @param start Start offset of the HEX sequence (inclusive)
+     * @param end End offset of the HEX sequence (exclusive)
+     * @return The number of bytes written to the stream
+     * @throws IOException If failed to write
+     * @throws IllegalArgumentException If invalid HEX sequence length
+     * @throws NumberFormatException If invalid HEX characters found
+     */
+    public static <S extends OutputStream> int decodeHex(S stream, char separator, CharSequence csq, int start, int end) throws IOException {
+        int len = end - start;
+        ValidateUtils.checkTrue(len >= 0, "Bad HEX sequence length: %d", len);
+
+        int delta = 2;
+        if (separator != EMPTY_HEX_SEPARATOR) {
+            // last character cannot be the separator
+            ValidateUtils.checkTrue((len % 3) == 2, "Invalid separated HEX sequence length: %d", len);
+            delta++;
+        } else {
+            ValidateUtils.checkTrue((len & 0x01) == 0, "Invalid contiguous HEX sequence length: %d", len);
+        }
+
+        int writeLen = 0;
+        for (int curPos = start; curPos < end; curPos += delta, writeLen++) {
+            stream.write(fromHex(csq.charAt(curPos), csq.charAt(curPos + 1)) & 0xFF);
+        }
+
+        return writeLen;
+    }
+
+    public static byte fromHex(char hi, char lo) throws NumberFormatException {
+        int hiValue = HEX_DIGITS.indexOf(((hi >= 'A') && (hi <= 'F')) ? ('a' + (hi - 'A')) : hi);
+        int loValue = HEX_DIGITS.indexOf(((lo >= 'A') && (lo <= 'F')) ? ('a' + (lo - 'A')) : lo);
+        if ((hiValue < 0) || (loValue < 0)) {
+            throw new NumberFormatException("fromHex(" + new String(new char[]{hi, lo}) + ") non-HEX characters");
+        }
+
+        return (byte) ((hiValue << 4) + loValue);
+    }
+
+    /**
+     * Read a 32-bit value in network order
+     *
+     * @param input The {@link InputStream}
+     * @param buf   Work buffer to use
+     * @return The read 32-bit value
+     * @throws IOException If failed to read 4 bytes or not enough room in
+     * @see #readInt(InputStream, byte[], int, int)
+     */
+    public static int readInt(InputStream input, byte[] buf) throws IOException {
+        return readInt(input, buf, 0, NumberUtils.length(buf));
+    }
+
+    /**
+     * Read a 32-bit value in network order
+     *
+     * @param input  The {@link InputStream}
+     * @param buf    Work buffer to use
+     * @param offset Offset in buffer to us
+     * @param len    Available length - must have at least 4 bytes available
+     * @return The read 32-bit value
+     * @throws IOException If failed to read 4 bytes or not enough room in
+     *                     work buffer
+     * @see #readUInt(InputStream, byte[], int, int)
+     */
+    public static int readInt(InputStream input, byte[] buf, int offset, int len) throws IOException {
+        return (int) readUInt(input, buf, offset, len);
+    }
+
+    /**
+     * Read a 32-bit value in network order
+     *
+     * @param input The {@link InputStream}
+     * @param buf   Work buffer to use
+     * @return The read 32-bit value
+     * @throws IOException If failed to read 4 bytes or not enough room in
+     * @see #readUInt(InputStream, byte[], int, int)
+     */
+    public static long readUInt(InputStream input, byte[] buf) throws IOException {
+        return readUInt(input, buf, 0, NumberUtils.length(buf));
+    }
+
+    /**
+     * Read a 32-bit value in network order
+     *
+     * @param input  The {@link InputStream}
+     * @param buf    Work buffer to use
+     * @param offset Offset in buffer to us
+     * @param len    Available length - must have at least 4 bytes available
+     * @return The read 32-bit value
+     * @throws IOException If failed to read 4 bytes or not enough room in
+     *                     work buffer
+     * @see #getUInt(byte[], int, int)
+     */
+    public static long readUInt(InputStream input, byte[] buf, int offset, int len) throws IOException {
+        try {
+            if (len < Integer.BYTES) {
+                throw new IllegalArgumentException("Not enough data for a UINT: required=" + Integer.BYTES + ", available=" + len);
+            }
+
+            IoUtils.readFully(input, buf, offset, Integer.BYTES);
+            return getUInt(buf, offset, len);
+        } catch (RuntimeException | Error e) {
+            throw new StreamCorruptedException("Failed (" + e.getClass().getSimpleName() + ")"
+                    + " to read UINT value: " + e.getMessage());
+        }
+    }
+
+    /**
+     * @param buf A buffer holding a 32-bit unsigned integer in <B>big endian</B>
+     *            format. <B>Note:</B> if more than 4 bytes are available, then only the
+     *            <U>first</U> 4 bytes in the buffer will be used
+     * @return The result as a {@code long} whose 32 high-order bits are zero
+     * @see #getUInt(byte[], int, int)
+     */
+    public static long getUInt(byte... buf) {
+        return getUInt(buf, 0, NumberUtils.length(buf));
+    }
+
+    /**
+     * @param buf A buffer holding a 32-bit unsigned integer in <B>big endian</B>
+     *            format.
+     * @param off The offset of the data in the buffer
+     * @param len The available data length. <B>Note:</B> if more than 4 bytes
+     *            are available, then only the <U>first</U> 4 bytes in the buffer will be
+     *            used (starting at the specified <tt>offset</tt>)
+     * @return The result as a {@code long} whose 32 high-order bits are zero
+     */
+    public static long getUInt(byte[] buf, int off, int len) {
+        if (len < Integer.BYTES) {
+            throw new IllegalArgumentException("Not enough data for a UINT: required=" + Integer.BYTES + ", available=" + len);
+        }
+
+        long l = (buf[off] << 24) & 0xff000000L;
+        l |= (buf[off + 1] << 16) & 0x00ff0000L;
+        l |= (buf[off + 2] << 8) & 0x0000ff00L;
+        l |= (buf[off + 3]) & 0x000000ffL;
+        return l;
+    }
+
+    /**
+     * Writes a 32-bit value in network order (i.e., MSB 1st)
+     *
+     * @param output The {@link OutputStream} to write the value
+     * @param value  The 32-bit value
+     * @param buf    A work buffer to use - must have enough space to contain 4 bytes
+     * @throws IOException If failed to write the value or work buffer to small
+     * @see #writeInt(OutputStream, int, byte[], int, int)
+     */
+    public static void writeInt(OutputStream output, int value, byte[] buf) throws IOException {
+        writeUInt(output, value, buf, 0, NumberUtils.length(buf));
+    }
+
+    /**
+     * Writes a 32-bit value in network order (i.e., MSB 1st)
+     *
+     * @param output The {@link OutputStream} to write the value
+     * @param value  The 32-bit value
+     * @param buf    A work buffer to use - must have enough space to contain 4 bytes
+     * @param off    The offset to write the value
+     * @param len    The available space
+     * @throws IOException If failed to write the value or work buffer to small
+     * @see #writeUInt(OutputStream, long, byte[], int, int)
+     */
+    public static void writeInt(OutputStream output, int value, byte[] buf, int off, int len) throws IOException {
+        writeUInt(output, value & 0xFFFFFFFFL, buf, off, len);
+    }
+
+    /**
+     * Writes a 32-bit value in network order (i.e., MSB 1st)
+     *
+     * @param output The {@link OutputStream} to write the value
+     * @param value  The 32-bit value
+     * @param buf    A work buffer to use - must have enough space to contain 4 bytes
+     * @throws IOException If failed to write the value or work buffer to small
+     * @see #writeUInt(OutputStream, long, byte[], int, int)
+     */
+    public static void writeUInt(OutputStream output, long value, byte[] buf) throws IOException {
+        writeUInt(output, value, buf, 0, NumberUtils.length(buf));
+    }
+
+    /**
+     * Writes a 32-bit value in network order (i.e., MSB 1st)
+     *
+     * @param output The {@link OutputStream} to write the value
+     * @param value  The 32-bit value
+     * @param buf    A work buffer to use - must have enough space to contain 4 bytes
+     * @param off    The offset to write the value
+     * @param len    The available space
+     * @throws IOException If failed to write the value or work buffer to small
+     * @see #putUInt(long, byte[], int, int)
+     */
+    public static void writeUInt(OutputStream output, long value, byte[] buf, int off, int len) throws IOException {
+        try {
+            int writeLen = putUInt(value, buf, off, len);
+            output.write(buf, off, writeLen);
+        } catch (RuntimeException | Error e) {
+            throw new StreamCorruptedException("Failed (" + e.getClass().getSimpleName() + ")"
+                    + " to write UINT value=" + value + ": " + e.getMessage());
+        }
+    }
+
+    /**
+     * Writes a 32-bit value in network order (i.e., MSB 1st)
+     *
+     * @param value The 32-bit value
+     * @param buf   The buffer
+     * @return The number of bytes used in the buffer
+     * @throws IllegalArgumentException if not enough space available
+     * @see #putUInt(long, byte[], int, int)
+     */
+    public static int putUInt(long value, byte[] buf) {
+        return putUInt(value, buf, 0, NumberUtils.length(buf));
+    }
+
+    /**
+     * Writes a 32-bit value in network order (i.e., MSB 1st)
+     *
+     * @param value The 32-bit value
+     * @param buf   The buffer
+     * @param off   The offset to write the value
+     * @param len   The available space
+     * @return The number of bytes used in the buffer
+     * @throws IllegalArgumentException if not enough space available
+     */
+    public static int putUInt(long value, byte[] buf, int off, int len) {
+        if (len < Integer.BYTES) {
+            throw new IllegalArgumentException("Not enough data for a UINT: required=" + Integer.BYTES + ", available=" + len);
+        }
+
+        buf[off] = (byte) ((value >> 24) & 0xFF);
+        buf[off + 1] = (byte) ((value >> 16) & 0xFF);
+        buf[off + 2] = (byte) ((value >> 8) & 0xFF);
+        buf[off + 3] = (byte) (value & 0xFF);
+
+        return Integer.BYTES;
+    }
+
+    public static boolean equals(byte[] a1, byte[] a2) {
+        int len1 = NumberUtils.length(a1);
+        int len2 = NumberUtils.length(a2);
+        if (len1 != len2) {
+            return false;
+        } else {
+            return equals(a1, 0, a2, 0, len1);
+        }
+    }
+
+    @SuppressWarnings("PMD.AssignmentInOperand")
+    public static boolean equals(byte[] a1, int a1Offset, byte[] a2, int a2Offset, int length) {
+        int len1 = NumberUtils.length(a1);
+        int len2 = NumberUtils.length(a2);
+        if ((len1 < (a1Offset + length)) || (len2 < (a2Offset + length))) {
+            return false;
+        }
+
+        while (length-- > 0) {
+            if (a1[a1Offset++] != a2[a2Offset++]) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    public static int getNextPowerOf2(int value) {
+        // for 0-7 return 8
+        return (value < Byte.SIZE) ? Byte.SIZE : NumberUtils.getNextPowerOf2(value);
+    }
+
+    /**
+     * Used for encodings where we don't know the data length before adding it
+     * to the buffer. The idea is to place a 32-bit &quot;placeholder&quot;,
+     * encode the data and then return back to the placeholder and update the
+     * length. The method calculates the encoded data length, moves the write
+     * position to the specified placeholder position, updates the length value
+     * and then moves the write position it back to its original value.
+     *
+     * @param buffer The {@link Buffer}
+     * @param lenPos The offset in the buffer where the length placeholder is
+     *               to be update - <B>Note:</B> assumption is that the encoded data starts
+     *               <U>immediately</U> after the placeholder
+     * @return The amount of data that has been encoded
+     */
+    public static int updateLengthPlaceholder(Buffer buffer, int lenPos) {
+        int startPos = lenPos + Integer.BYTES;
+        int endPos = buffer.wpos();
+        int dataLength = endPos - startPos;
+        // NOTE: although data length is defined as UINT32, we do not expected sizes above Integer.MAX_VALUE
+        ValidateUtils.checkTrue(dataLength >= 0, "Illegal data length: %d", dataLength);
+        buffer.wpos(lenPos);
+        buffer.putInt(dataLength);
+        buffer.wpos(endPos);
+        return dataLength;
+    }
+
+    /**
+     * Updates a 32-bit &quot;placeholder&quot; location for data length - moves
+     * the write position to the specified placeholder position, updates the length
+     * value and then moves the write position it back to its original value.
+     *
+     * @param buffer     The {@link Buffer}
+     * @param lenPos     The offset in the buffer where the length placeholder is
+     *                   to be update - <B>Note:</B> assumption is that the encoded data starts
+     *                   <U>immediately</U> after the placeholder
+     * @param dataLength The length to update
+     */
+    public static void updateLengthPlaceholder(Buffer buffer, int lenPos, int dataLength) {
+        int curPos = buffer.wpos();
+        buffer.wpos(lenPos);
+        buffer.putInt(dataLength);
+        buffer.wpos(curPos);
+    }
+
+    /**
+     * Invokes {@link Buffer#clear()}
+     *
+     * @param <B>    The generic buffer type
+     * @param buffer A {@link Buffer} instance - ignored if {@code null}
+     * @return The same as the input instance
+     */
+    public static <B extends Buffer> B clear(B buffer) {
+        if (buffer != null) {
+            buffer.clear();
+        }
+
+        return buffer;
+    }
+
+    public static long validateInt32Value(long value, String message) {
+        ValidateUtils.checkTrue(isValidInt32Value(value), message, value);
+        return value;
+    }
+
+    public static long validateInt32Value(long value, String format, Object arg) {
+        ValidateUtils.checkTrue(isValidInt32Value(value), format, arg);
+        return value;
+    }
+
+    public static long validateInt32Value(long value, String format, Object... args) {
+        ValidateUtils.checkTrue(isValidInt32Value(value), format, args);
+        return value;
+    }
+
+    public static boolean isValidInt32Value(long value) {
+        return (value >= Integer.MIN_VALUE) && (value <= Integer.MAX_VALUE);
+    }
+
+    public static long validateUint32Value(long value, String message) {
+        ValidateUtils.checkTrue(isValidUint32Value(value), message, value);
+        return value;
+    }
+
+    public static long validateUint32Value(long value, String format, Object arg) {
+        ValidateUtils.checkTrue(isValidUint32Value(value), format, arg);
+        return value;
+    }
+
+    public static long validateUint32Value(long value, String format, Object... args) {
+        ValidateUtils.checkTrue(isValidUint32Value(value), format, args);
+        return value;
+    }
+
+    public static boolean isValidUint32Value(long value) {
+        return (value >= 0L) && (value <= MAX_UINT32_VALUE);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/ByteArrayBuffer.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/ByteArrayBuffer.java b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/ByteArrayBuffer.java
new file mode 100644
index 0000000..6655ec1
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/ByteArrayBuffer.java
@@ -0,0 +1,253 @@
+/*
+ * 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.buffer;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.function.IntUnaryOperator;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.NumberUtils;
+import org.apache.sshd.common.util.Readable;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * Provides an implementation of {@link Buffer} using a backing byte array
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class ByteArrayBuffer extends Buffer {
+    public static final int DEFAULT_SIZE = 256;
+    public static final int MAX_LEN = 65536;
+
+    private byte[] data;
+    private int rpos;
+    private int wpos;
+
+    public ByteArrayBuffer() {
+        this(DEFAULT_SIZE);
+    }
+
+    public ByteArrayBuffer(int size) {
+        this(size, true);
+    }
+
+    public ByteArrayBuffer(int size, boolean roundOff) {
+        this(new byte[roundOff ? BufferUtils.getNextPowerOf2(size) : size], false);
+    }
+
+    public ByteArrayBuffer(byte[] data) {
+        this(data, 0, data.length, true);
+    }
+
+    public ByteArrayBuffer(byte[] data, boolean read) {
+        this(data, 0, data.length, read);
+    }
+
+    public ByteArrayBuffer(byte[] data, int off, int len) {
+        this(data, off, len, true);
+    }
+
+    public ByteArrayBuffer(byte[] data, int off, int len, boolean read) {
+        this.data = data;
+        this.rpos = off;
+        this.wpos = (read ? len : 0) + off;
+    }
+
+    @Override
+    public int rpos() {
+        return rpos;
+    }
+
+    @Override
+    public void rpos(int rpos) {
+        this.rpos = rpos;
+    }
+
+    @Override
+    public int wpos() {
+        return wpos;
+    }
+
+    @Override
+    public void wpos(int wpos) {
+        if (wpos > this.wpos) {
+            ensureCapacity(wpos - this.wpos);
+        }
+        this.wpos = wpos;
+    }
+
+    @Override
+    public int available() {
+        return wpos - rpos;
+    }
+
+    @Override
+    public int capacity() {
+        return data.length - wpos;
+    }
+
+    @Override
+    public byte[] array() {
+        return data;
+    }
+
+    @Override
+    public void compact() {
+        int avail = available();
+        if (avail > 0) {
+            System.arraycopy(data, rpos, data, 0, avail);
+        }
+        wpos -= rpos;
+        rpos = 0;
+    }
+
+    @Override
+    public void clear(boolean wipeData) {
+        rpos = 0;
+        wpos = 0;
+
+        if (wipeData) {
+            Arrays.fill(data, (byte) 0);
+        }
+    }
+
+    @Override
+    public byte getByte() {
+        ensureAvailable(Byte.BYTES);
+        return data[rpos++];
+    }
+
+    @Override
+    public void putByte(byte b) {
+        ensureCapacity(Byte.BYTES);
+        data[wpos++] = b;
+    }
+
+    @Override
+    public int putBuffer(Readable buffer, boolean expand) {
+        int r = expand ? buffer.available() : Math.min(buffer.available(), capacity());
+        ensureCapacity(r);
+        buffer.getRawBytes(data, wpos, r);
+        wpos += r;
+        return r;
+    }
+
+    @Override
+    public void putBuffer(ByteBuffer buffer) {
+        int required = buffer.remaining();
+        ensureCapacity(required + Integer.SIZE);
+        putInt(required);
+        buffer.get(data, wpos, required);
+        wpos += required;
+    }
+
+    @Override
+    public void putRawBytes(byte[] d, int off, int len) {
+        ValidateUtils.checkTrue(len >= 0, "Negative raw bytes length: %d", len);
+        ensureCapacity(len);
+        System.arraycopy(d, off, data, wpos, len);
+        wpos += len;
+    }
+
+    @Override
+    public String getString(Charset charset) {
+        int len = getInt();
+        if (len < 0) {
+            throw new BufferException("Bad item length: " + len);
+        }
+        ensureAvailable(len);
+
+        Objects.requireNonNull(charset, "No charset specified");
+        String s = new String(data, rpos, len, charset);
+        rpos += len;
+        return s;
+    }
+
+    @Override
+    public void getRawBytes(byte[] buf, int off, int len) {
+        ensureAvailable(len);
+        copyRawBytes(0, buf, off, len);
+        rpos += len;
+    }
+
+    @Override
+    protected void copyRawBytes(int offset, byte[] buf, int pos, int len) {
+        System.arraycopy(data, rpos + offset, buf, pos, len);
+    }
+
+    @Override
+    public void ensureCapacity(int capacity, IntUnaryOperator growthFactor) {
+        ValidateUtils.checkTrue(capacity >= 0, "Negative capacity requested: %d", capacity);
+
+        int maxSize = size();
+        int curPos = wpos();
+        int remaining = maxSize - curPos;
+        if (remaining < capacity) {
+            int minimum = curPos + capacity;
+            int actual = growthFactor.applyAsInt(minimum);
+            if (actual < minimum) {
+                throw new IllegalStateException("ensureCapacity(" + capacity + ") actual (" + actual + ") below min. (" + minimum + ")");
+            }
+            byte[] tmp = new byte[actual];
+            System.arraycopy(data, 0, tmp, 0, data.length);
+            data = tmp;
+        }
+    }
+
+    @Override
+    protected int size() {
+        return data.length;
+    }
+
+    /**
+     * Creates a compact buffer (i.e., one that starts at offset zero) containing a <U>copy</U>
+     * of the original data
+     *
+     * @param data   The original data buffer
+     * @return A {@link ByteArrayBuffer} containing a <U>copy</U> of the original data
+     * starting at zero read position
+     * @see #getCompactClone(byte[], int, int)
+     */
+    public static ByteArrayBuffer getCompactClone(byte[] data) {
+        return getCompactClone(data, 0, NumberUtils.length(data));
+    }
+
+    /**
+     * Creates a compact buffer (i.e., one that starts at offset zero) containing a <U>copy</U>
+     * of the original data
+     *
+     * @param data   The original data buffer
+     * @param offset The offset of the valid data in the buffer
+     * @param len    The size (in bytes) of of the valid data in the buffer
+     * @return A {@link ByteArrayBuffer} containing a <U>copy</U> of the original data
+     * starting at zero read position
+     */
+    public static ByteArrayBuffer getCompactClone(byte[] data, int offset, int len) {
+        byte[] clone = (len > 0) ? new byte[len] : GenericUtils.EMPTY_BYTE_ARRAY;
+        if (len > 0) {
+            System.arraycopy(data, offset, clone, 0, len);
+        }
+
+        return new ByteArrayBuffer(clone, true);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/AbstractBufferPublicKeyParser.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/AbstractBufferPublicKeyParser.java b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/AbstractBufferPublicKeyParser.java
new file mode 100644
index 0000000..aaf9641
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/AbstractBufferPublicKeyParser.java
@@ -0,0 +1,88 @@
+/*
+ * 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.buffer.keys;
+
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.PublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Objects;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * @param <PUB> Type of {@link PublicKey} being extracted
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractBufferPublicKeyParser<PUB extends PublicKey> implements BufferPublicKeyParser<PUB> {
+    private final Class<PUB> keyClass;
+    private final Collection<String> supported;
+
+    protected AbstractBufferPublicKeyParser(Class<PUB> keyClass, String... supported) {
+        this(keyClass, GenericUtils.isEmpty(supported) ? Collections.emptyList() : Arrays.asList(supported));
+    }
+
+    protected AbstractBufferPublicKeyParser(Class<PUB> keyClass, Collection<String> supported) {
+        this.keyClass = Objects.requireNonNull(keyClass, "No key class");
+        this.supported = ValidateUtils.checkNotNullAndNotEmpty(supported, "No supported types for %s", keyClass.getSimpleName());
+    }
+
+    public Collection<String> getSupportedKeyTypes() {
+        return supported;
+    }
+
+    public final Class<PUB> getKeyClass() {
+        return keyClass;
+    }
+
+    @Override
+    public boolean isKeyTypeSupported(String keyType) {
+        Collection<String> keys = getSupportedKeyTypes();
+        return (GenericUtils.length(keyType) > 0)
+            && (GenericUtils.size(keys) > 0)
+            && keys.contains(keyType);
+    }
+
+    protected <S extends KeySpec> PUB generatePublicKey(String algorithm, S keySpec) throws GeneralSecurityException {
+        KeyFactory keyFactory = getKeyFactory(algorithm);
+        PublicKey key = keyFactory.generatePublic(keySpec);
+        Class<PUB> kc = getKeyClass();
+        if (!kc.isInstance(key)) {
+            throw new InvalidKeySpecException("Mismatched generated key types: expected=" + kc.getSimpleName() + ", actual=" + key);
+        }
+
+        return kc.cast(key);
+    }
+
+    protected KeyFactory getKeyFactory(String algorithm) throws GeneralSecurityException {
+        return SecurityUtils.getKeyFactory(algorithm);
+    }
+
+    @Override
+    public String toString() {
+        return getClass().getSimpleName() + " - supported=" + getSupportedKeyTypes();
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/BufferPublicKeyParser.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/BufferPublicKeyParser.java b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/BufferPublicKeyParser.java
new file mode 100644
index 0000000..6f7cde6
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/BufferPublicKeyParser.java
@@ -0,0 +1,111 @@
+/*
+ * 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.buffer.keys;
+
+import java.security.GeneralSecurityException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.util.Arrays;
+import java.util.Collection;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.buffer.Buffer;
+
+/**
+ * Parses a raw {@link PublicKey} from a {@link Buffer}
+ *
+ * @param <PUB> Type of {@link PublicKey} being extracted
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface BufferPublicKeyParser<PUB extends PublicKey> {
+
+    BufferPublicKeyParser<PublicKey> EMPTY = new BufferPublicKeyParser<PublicKey>() {
+        @Override
+        public boolean isKeyTypeSupported(String keyType) {
+            return false;
+        }
+
+        @Override
+        public PublicKey getRawPublicKey(String keyType, Buffer buffer) throws GeneralSecurityException {
+            throw new NoSuchAlgorithmException(keyType);
+        }
+
+        @Override
+        public String toString() {
+            return "EMPTY";
+        }
+    };
+
+    BufferPublicKeyParser<PublicKey> DEFAULT = aggregate(
+            Arrays.asList(
+                    RSABufferPublicKeyParser.INSTANCE,
+                    DSSBufferPublicKeyParser.INSTANCE,
+                    ECBufferPublicKeyParser.INSTANCE,
+                    ED25519BufferPublicKeyParser.INSTANCE));
+
+    /**
+     * @param keyType The key type - e.g., &quot;ssh-rsa&quot, &quot;ssh-dss&quot;
+     * @return {@code true} if this key type is supported by the parser
+     */
+    boolean isKeyTypeSupported(String keyType);
+
+    /**
+     * @param keyType The key type - e.g., &quot;ssh-rsa&quot, &quot;ssh-dss&quot;
+     * @param buffer The {@link Buffer} containing the encoded raw public key
+     * @return The decoded {@link PublicKey}
+     * @throws GeneralSecurityException If failed to generate the key
+     */
+    PUB getRawPublicKey(String keyType, Buffer buffer) throws GeneralSecurityException;
+
+    static BufferPublicKeyParser<PublicKey> aggregate(Collection<? extends BufferPublicKeyParser<? extends PublicKey>> parsers) {
+        if (GenericUtils.isEmpty(parsers)) {
+            return EMPTY;
+        }
+
+        return new BufferPublicKeyParser<PublicKey>() {
+            @Override
+            public boolean isKeyTypeSupported(String keyType) {
+                for (BufferPublicKeyParser<? extends PublicKey> p : parsers) {
+                    if (p.isKeyTypeSupported(keyType)) {
+                        return true;
+                    }
+                }
+
+                return false;
+            }
+
+            @Override
+            public PublicKey getRawPublicKey(String keyType, Buffer buffer) throws GeneralSecurityException {
+                for (BufferPublicKeyParser<? extends PublicKey> p : parsers) {
+                    if (p.isKeyTypeSupported(keyType)) {
+                        return p.getRawPublicKey(keyType, buffer);
+                    }
+                }
+
+                throw new NoSuchAlgorithmException("No aggregate matcher for " + keyType);
+            }
+
+            @Override
+            public String toString() {
+                return String.valueOf(parsers);
+            }
+        };
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/DSSBufferPublicKeyParser.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/DSSBufferPublicKeyParser.java b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/DSSBufferPublicKeyParser.java
new file mode 100644
index 0000000..4eec49a
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/DSSBufferPublicKeyParser.java
@@ -0,0 +1,52 @@
+/*
+ * 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.buffer.keys;
+
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.interfaces.DSAPublicKey;
+import java.security.spec.DSAPublicKeySpec;
+
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.buffer.Buffer;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class DSSBufferPublicKeyParser extends AbstractBufferPublicKeyParser<DSAPublicKey> {
+    public static final DSSBufferPublicKeyParser INSTANCE = new DSSBufferPublicKeyParser();
+
+    public DSSBufferPublicKeyParser() {
+        super(DSAPublicKey.class, KeyPairProvider.SSH_DSS);
+    }
+
+    @Override
+    public DSAPublicKey getRawPublicKey(String keyType, Buffer buffer) throws GeneralSecurityException {
+        ValidateUtils.checkTrue(isKeyTypeSupported(keyType), "Unsupported key type: %s", keyType);
+        BigInteger p = buffer.getMPInt();
+        BigInteger q = buffer.getMPInt();
+        BigInteger g = buffer.getMPInt();
+        BigInteger y = buffer.getMPInt();
+
+        return generatePublicKey(KeyUtils.DSS_ALGORITHM, new DSAPublicKeySpec(y, p, q, g));
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/ECBufferPublicKeyParser.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/ECBufferPublicKeyParser.java b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/ECBufferPublicKeyParser.java
new file mode 100644
index 0000000..df0115a
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/ECBufferPublicKeyParser.java
@@ -0,0 +1,81 @@
+/*
+ * 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.buffer.keys;
+
+import java.security.GeneralSecurityException;
+import java.security.NoSuchAlgorithmException;
+import java.security.interfaces.ECPublicKey;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPoint;
+import java.security.spec.ECPublicKeySpec;
+import java.security.spec.InvalidKeySpecException;
+
+import org.apache.sshd.common.cipher.ECCurves;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.buffer.Buffer;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class ECBufferPublicKeyParser extends AbstractBufferPublicKeyParser<ECPublicKey> {
+    public static final ECBufferPublicKeyParser INSTANCE = new ECBufferPublicKeyParser();
+
+    public ECBufferPublicKeyParser() {
+        super(ECPublicKey.class, ECCurves.KEY_TYPES);
+    }
+
+    @Override
+    public ECPublicKey getRawPublicKey(String keyType, Buffer buffer) throws GeneralSecurityException {
+        ValidateUtils.checkTrue(isKeyTypeSupported(keyType), "Unsupported key type: %s", keyType);
+        ECCurves curve = ECCurves.fromKeyType(keyType);
+        if (curve == null) {
+            throw new NoSuchAlgorithmException("Unsupported raw public algorithm: " + keyType);
+        }
+
+        String curveName = curve.getName();
+        ECParameterSpec params = curve.getParameters();
+        return getRawECKey(curveName, params, buffer);
+    }
+
+    protected ECPublicKey getRawECKey(String expectedCurve, ECParameterSpec spec, Buffer buffer) throws GeneralSecurityException {
+        String curveName = buffer.getString();
+        if (!expectedCurve.equals(curveName)) {
+            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 = buffer.getBytes();
+        ECPoint w;
+        try {
+            w = ECCurves.octetStringToEcPoint(octets);
+        } catch (RuntimeException e) {
+            throw new InvalidKeySpecException("getRawECKey(" + expectedCurve + ")"
+                    + " cannot (" + e.getClass().getSimpleName() + ")"
+                    + " retrieve W value: " + e.getMessage(),
+                    e);
+        }
+
+        return generatePublicKey(KeyUtils.EC_ALGORITHM, new ECPublicKeySpec(w, spec));
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/ED25519BufferPublicKeyParser.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/ED25519BufferPublicKeyParser.java b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/ED25519BufferPublicKeyParser.java
new file mode 100644
index 0000000..61ce6ab
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/ED25519BufferPublicKeyParser.java
@@ -0,0 +1,47 @@
+/*
+ * 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.buffer.keys;
+
+import java.security.GeneralSecurityException;
+import java.security.PublicKey;
+
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * TODO complete this when SSHD-440 is done
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class ED25519BufferPublicKeyParser extends AbstractBufferPublicKeyParser<PublicKey> {
+    public static final ED25519BufferPublicKeyParser INSTANCE = new ED25519BufferPublicKeyParser();
+
+    public ED25519BufferPublicKeyParser() {
+        super(PublicKey.class, KeyPairProvider.SSH_ED25519);
+    }
+
+    @Override
+    public PublicKey getRawPublicKey(String keyType, Buffer buffer) throws GeneralSecurityException {
+        ValidateUtils.checkTrue(isKeyTypeSupported(keyType), "Unsupported key type: %s", keyType);
+        byte[] seed = buffer.getBytes();
+        return SecurityUtils.generateEDDSAPublicKey(keyType, seed);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/RSABufferPublicKeyParser.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/RSABufferPublicKeyParser.java b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/RSABufferPublicKeyParser.java
new file mode 100644
index 0000000..363b07e
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/RSABufferPublicKeyParser.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.buffer.keys;
+
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.RSAPublicKeySpec;
+
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.buffer.Buffer;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class RSABufferPublicKeyParser extends AbstractBufferPublicKeyParser<RSAPublicKey> {
+    public static final RSABufferPublicKeyParser INSTANCE = new RSABufferPublicKeyParser();
+
+    public RSABufferPublicKeyParser() {
+        super(RSAPublicKey.class, KeyPairProvider.SSH_RSA);
+    }
+
+    @Override
+    public RSAPublicKey getRawPublicKey(String keyType, Buffer buffer) throws GeneralSecurityException {
+        ValidateUtils.checkTrue(isKeyTypeSupported(keyType), "Unsupported key type: %s", keyType);
+        BigInteger e = buffer.getMPInt();
+        BigInteger n = buffer.getMPInt();
+        return generatePublicKey(KeyUtils.RSA_ALGORITHM, new RSAPublicKeySpec(n, e));
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/AbstractCloseable.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/AbstractCloseable.java b/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/AbstractCloseable.java
new file mode 100644
index 0000000..6413ebb
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/AbstractCloseable.java
@@ -0,0 +1,162 @@
+/*
+ * 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.closeable;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.sshd.common.future.CloseFuture;
+import org.apache.sshd.common.future.DefaultCloseFuture;
+import org.apache.sshd.common.future.SshFuture;
+import org.apache.sshd.common.future.SshFutureListener;
+
+/**
+ * Provides some default implementations
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractCloseable extends IoBaseCloseable {
+
+    public enum State {
+        Opened, Graceful, Immediate, Closed
+    }
+
+    /**
+     * Lock object for this session state
+     */
+    protected final Object lock = new Object();
+
+    /**
+     * State of this object
+     */
+    protected final AtomicReference<AbstractCloseable.State> state = new AtomicReference<>(State.Opened);
+
+    /**
+     * A future that will be set 'closed' when the object is actually closed
+     */
+    protected final CloseFuture closeFuture;
+
+    protected AbstractCloseable() {
+        this("");
+    }
+
+    protected AbstractCloseable(String discriminator) {
+        super(discriminator);
+        closeFuture = new DefaultCloseFuture(discriminator, lock);
+    }
+
+    @Override
+    public void addCloseFutureListener(SshFutureListener<CloseFuture> listener) {
+        closeFuture.addListener(listener);
+    }
+
+    @Override
+    public void removeCloseFutureListener(SshFutureListener<CloseFuture> listener) {
+        closeFuture.removeListener(listener);
+    }
+
+    @Override
+    public final CloseFuture close(boolean immediately) {
+        boolean debugEnabled = log.isDebugEnabled();
+        if (immediately) {
+            if (state.compareAndSet(State.Opened, State.Immediate)
+                    || state.compareAndSet(State.Graceful, State.Immediate)) {
+                if (debugEnabled) {
+                    log.debug("close({}) Closing immediately", this);
+                }
+                preClose();
+                doCloseImmediately();
+                if (debugEnabled) {
+                    log.debug("close({})[Immediately] closed", this);
+                }
+            } else {
+                if (debugEnabled) {
+                    log.debug("close({})[Immediately] state already {}", this, state.get());
+                }
+            }
+        } else {
+            if (state.compareAndSet(State.Opened, State.Graceful)) {
+                if (debugEnabled) {
+                    log.debug("close({}) Closing gracefully", this);
+                }
+                preClose();
+                SshFuture<CloseFuture> grace = doCloseGracefully();
+                if (grace != null) {
+                    grace.addListener(future -> {
+                        if (state.compareAndSet(State.Graceful, State.Immediate)) {
+                            doCloseImmediately();
+                            if (debugEnabled) {
+                                log.debug("close({}][Graceful] - operationComplete() closed", AbstractCloseable.this);
+                            }
+                        }
+                    });
+                } else {
+                    if (state.compareAndSet(State.Graceful, State.Immediate)) {
+                        doCloseImmediately();
+                        if (debugEnabled) {
+                            log.debug("close({})[Graceful] closed", this);
+                        }
+                    }
+                }
+            } else {
+                if (debugEnabled) {
+                    log.debug("close({})[Graceful] state already {}", this, state.get());
+                }
+            }
+        }
+        return closeFuture;
+    }
+
+    @Override
+    public final boolean isClosed() {
+        return state.get() == State.Closed;
+    }
+
+    @Override
+    public final boolean isClosing() {
+        return state.get() != State.Opened;
+    }
+
+    /**
+     * preClose is guaranteed to be called before doCloseGracefully or doCloseImmediately.
+     * When preClose() is called, isClosing() == true
+     */
+    protected void preClose() {
+        // nothing
+    }
+
+    protected CloseFuture doCloseGracefully() {
+        return null;
+    }
+
+    /**
+     * <P>doCloseImmediately is called once and only once
+     * with state == Immediate</P>
+     *
+     * <P>Overriding methods should always call the base implementation.
+     * It may be called concurrently while preClose() or doCloseGracefully is executing</P>
+     */
+    protected void doCloseImmediately() {
+        closeFuture.setClosed();
+        state.set(State.Closed);
+    }
+
+    protected Builder builder() {
+        return new Builder(lock);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/AbstractInnerCloseable.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/AbstractInnerCloseable.java b/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/AbstractInnerCloseable.java
new file mode 100644
index 0000000..6518d23
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/AbstractInnerCloseable.java
@@ -0,0 +1,48 @@
+/*
+ * 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.closeable;
+
+import org.apache.sshd.common.Closeable;
+import org.apache.sshd.common.future.CloseFuture;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractInnerCloseable extends AbstractCloseable {
+    protected AbstractInnerCloseable() {
+        this("");
+    }
+
+    protected AbstractInnerCloseable(String discriminator) {
+        super(discriminator);
+    }
+
+    protected abstract Closeable getInnerCloseable();
+
+    @Override
+    protected final CloseFuture doCloseGracefully() {
+        return getInnerCloseable().close(false);
+    }
+
+    @Override
+    @SuppressWarnings("synthetic-access")
+    protected final void doCloseImmediately() {
+        getInnerCloseable().close(true).addListener(future -> AbstractInnerCloseable.super.doCloseImmediately());
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/Builder.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/Builder.java b/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/Builder.java
new file mode 100644
index 0000000..847d49c
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/Builder.java
@@ -0,0 +1,115 @@
+/*
+ * 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.closeable;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+import org.apache.sshd.common.Closeable;
+import org.apache.sshd.common.future.SshFuture;
+import org.apache.sshd.common.util.ObjectBuilder;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class Builder implements ObjectBuilder<Closeable> {
+    private final Object lock;
+    private final List<Closeable> closeables = new ArrayList<>();
+
+    public Builder(Object lock) {
+        this.lock = Objects.requireNonNull(lock, "No lock");
+    }
+
+    public Builder run(Object id, Runnable r) {
+        return close(new SimpleCloseable(id, lock) {
+            @Override
+            protected void doClose(boolean immediately) {
+                try {
+                    r.run();
+                } finally {
+                    super.doClose(immediately);
+                }
+            }
+        });
+    }
+
+    @SuppressWarnings("rawtypes")
+    public <T extends SshFuture> Builder when(SshFuture<T> future) {
+        if (future != null) {
+            when(future.getId(), Collections.singleton(future));
+        }
+        return this;
+    }
+
+    @SuppressWarnings("rawtypes")
+    @SafeVarargs
+    public final <T extends SshFuture> Builder when(SshFuture<T>... futures) {
+        return when(getClass().getSimpleName(), Arrays.asList(futures));
+    }
+
+    @SuppressWarnings("rawtypes")
+    public <T extends SshFuture> Builder when(Object id, Iterable<? extends SshFuture<T>> futures) {
+        return close(new FuturesCloseable<>(id, lock, futures));
+    }
+
+    public Builder sequential(Closeable... closeables) {
+        for (Closeable closeable : closeables) {
+            close(closeable);
+        }
+        return this;
+    }
+
+    public Builder sequential(Object id, Iterable<Closeable> closeables) {
+        return close(new SequentialCloseable(id, lock, closeables));
+    }
+
+    public Builder parallel(Closeable... closeables) {
+        if (closeables.length == 1) {
+            close(closeables[0]);
+        } else if (closeables.length > 0) {
+            parallel(getClass().getSimpleName(), Arrays.asList(closeables));
+        }
+        return this;
+    }
+
+    public Builder parallel(Object id, Iterable<? extends Closeable> closeables) {
+        return close(new ParallelCloseable(id, lock, closeables));
+    }
+
+    public Builder close(Closeable c) {
+        if (c != null) {
+            closeables.add(c);
+        }
+        return this;
+    }
+
+    @Override
+    public Closeable build() {
+        if (closeables.isEmpty()) {
+            return new SimpleCloseable(getClass().getSimpleName(), lock);
+        } else if (closeables.size() == 1) {
+            return closeables.get(0);
+        } else {
+            return new SequentialCloseable(getClass().getSimpleName(), lock, closeables);
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/FuturesCloseable.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/FuturesCloseable.java b/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/FuturesCloseable.java
new file mode 100644
index 0000000..af765b7
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/FuturesCloseable.java
@@ -0,0 +1,76 @@
+/*
+ * 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.closeable;
+
+import java.util.Collections;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.sshd.common.SshException;
+import org.apache.sshd.common.future.DefaultSshFuture;
+import org.apache.sshd.common.future.SshFuture;
+import org.apache.sshd.common.future.SshFutureListener;
+
+/**
+ * @param <T> Type of future
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class FuturesCloseable<T extends SshFuture> extends SimpleCloseable {
+
+    private final Iterable<? extends SshFuture<T>> futures;
+
+    public FuturesCloseable(Object id, Object lock, Iterable<? extends SshFuture<T>> futures) {
+        super(id, lock);
+        this.futures = (futures == null) ? Collections.emptyList() : futures;
+    }
+
+    @Override
+    protected void doClose(boolean immediately) {
+        if (immediately) {
+            for (SshFuture<?> f : futures) {
+                if (f instanceof DefaultSshFuture) {
+                    ((DefaultSshFuture<?>) f).setValue(new SshException("Closed"));
+                }
+            }
+            future.setClosed();
+        } else {
+            AtomicInteger count = new AtomicInteger(1);
+            boolean traceEnabled = log.isTraceEnabled();
+            SshFutureListener<T> listener = f -> {
+                int pendingCount = count.decrementAndGet();
+                if (traceEnabled) {
+                    log.trace("doClose(" + immediately + ") complete pending: " + pendingCount);
+                }
+                if (pendingCount == 0) {
+                    future.setClosed();
+                }
+            };
+
+            for (SshFuture<T> f : futures) {
+                if (f != null) {
+                    int pendingCount = count.incrementAndGet();
+                    if (traceEnabled) {
+                        log.trace("doClose(" + immediately + ") future pending: " + pendingCount);
+                    }
+                    f.addListener(listener);
+                }
+            }
+            listener.operationComplete(null);
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/IoBaseCloseable.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/IoBaseCloseable.java b/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/IoBaseCloseable.java
new file mode 100644
index 0000000..f4c6d1a
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/IoBaseCloseable.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util.closeable;
+
+import org.apache.sshd.common.Closeable;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class IoBaseCloseable extends AbstractLoggingBean implements Closeable {
+    protected IoBaseCloseable() {
+        this("");
+    }
+
+    protected IoBaseCloseable(String discriminator) {
+        super(discriminator);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/ParallelCloseable.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/ParallelCloseable.java b/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/ParallelCloseable.java
new file mode 100644
index 0000000..0900cd7
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/ParallelCloseable.java
@@ -0,0 +1,73 @@
+/*
+ * 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.closeable;
+
+import java.util.Collections;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.sshd.common.Closeable;
+import org.apache.sshd.common.future.CloseFuture;
+import org.apache.sshd.common.future.SshFutureListener;
+
+/**
+ * Waits for a group of {@link Closeable}s to complete in any order, then
+ * signals the completion by setting the &quot;parent&quot; future as closed
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class ParallelCloseable extends SimpleCloseable {
+    private final Iterable<? extends Closeable> closeables;
+
+    public ParallelCloseable(Object id, Object lock, Iterable<? extends Closeable> closeables) {
+        super(id, lock);
+        this.closeables = (closeables == null) ? Collections.emptyList() : closeables;
+    }
+
+    @Override
+    protected void doClose(boolean immediately) {
+        AtomicInteger count = new AtomicInteger(1);
+        boolean traceEnabled = log.isTraceEnabled();
+        SshFutureListener<CloseFuture> listener = f -> {
+            int pendingCount = count.decrementAndGet();
+            if (traceEnabled) {
+                log.trace("doClose(" + immediately + ") completed pending: " + pendingCount);
+            }
+            if (pendingCount == 0) {
+                future.setClosed();
+            }
+        };
+
+        for (Closeable c : closeables) {
+            if (c == null) {
+                continue;
+            }
+
+            int pendingCount = count.incrementAndGet();
+            if (traceEnabled) {
+                log.trace("doClose(" + immediately + ") pending closeables: " + pendingCount);
+            }
+            c.close(immediately).addListener(listener);
+        }
+        /*
+         * Trigger the last "decrementAndGet" so that the future is marked as closed
+         * when last "operationComplete" is invoked (which could be this call...)
+         */
+        listener.operationComplete(null);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/SequentialCloseable.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/SequentialCloseable.java b/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/SequentialCloseable.java
new file mode 100644
index 0000000..6af51b8
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/SequentialCloseable.java
@@ -0,0 +1,71 @@
+/*
+ * 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.closeable;
+
+import java.util.Collections;
+import java.util.Iterator;
+
+import org.apache.sshd.common.Closeable;
+import org.apache.sshd.common.future.CloseFuture;
+import org.apache.sshd.common.future.SshFutureListener;
+
+/**
+ * Waits for a group of {@link Closeable}s to complete in the given order, then
+ * signals the completion by setting the &quot;parent&quot; future as closed
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class SequentialCloseable extends SimpleCloseable {
+    private final Iterable<? extends Closeable> closeables;
+
+    public SequentialCloseable(Object id, Object lock, Iterable<? extends Closeable> closeables) {
+        super(id, lock);
+        this.closeables = (closeables == null) ? Collections.emptyList() : closeables;
+    }
+
+    @Override
+    protected void doClose(boolean immediately) {
+        Iterator<? extends Closeable> iterator = closeables.iterator();
+        SshFutureListener<CloseFuture> listener = new SshFutureListener<CloseFuture>() {
+            @SuppressWarnings("synthetic-access")
+            @Override
+            public void operationComplete(CloseFuture previousFuture) {
+                boolean traceEnabled = log.isTraceEnabled();
+                while (iterator.hasNext()) {
+                    Closeable c = iterator.next();
+                    if (c != null) {
+                        if (traceEnabled) {
+                            log.trace("doClose(" + immediately + ") closing " + c);
+                        }
+                        CloseFuture nextFuture = c.close(immediately);
+                        nextFuture.addListener(this);
+                        return;
+                    }
+                }
+                if (!iterator.hasNext()) {
+                    if (log.isDebugEnabled()) {
+                        log.debug("doClose(" + immediately + ") signal close complete");
+                    }
+                    future.setClosed();
+                }
+            }
+        };
+        listener.operationComplete(null);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/SimpleCloseable.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/SimpleCloseable.java b/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/SimpleCloseable.java
new file mode 100644
index 0000000..e360f13
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/SimpleCloseable.java
@@ -0,0 +1,71 @@
+/*
+ * 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.closeable;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.sshd.common.future.CloseFuture;
+import org.apache.sshd.common.future.DefaultCloseFuture;
+import org.apache.sshd.common.future.SshFutureListener;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class SimpleCloseable extends IoBaseCloseable {
+
+    protected final DefaultCloseFuture future;
+    protected final AtomicBoolean closing;
+
+    public SimpleCloseable(Object id, Object lock) {
+        future = new DefaultCloseFuture(id, lock);
+        closing = new AtomicBoolean(false);
+    }
+
+    @Override
+    public boolean isClosed() {
+        return future.isClosed();
+    }
+
+    @Override
+    public boolean isClosing() {
+        return closing.get();
+    }
+
+    @Override
+    public void addCloseFutureListener(SshFutureListener<CloseFuture> listener) {
+        future.addListener(listener);
+    }
+
+    @Override
+    public void removeCloseFutureListener(SshFutureListener<CloseFuture> listener) {
+        future.removeListener(listener);
+    }
+
+    @Override
+    public CloseFuture close(boolean immediately) {
+        if (closing.compareAndSet(false, true)) {
+            doClose(immediately);
+        }
+        return future;
+    }
+
+    protected void doClose(boolean immediately) {
+        future.setClosed();
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/io/CloseableEmptyInputStream.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/CloseableEmptyInputStream.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/CloseableEmptyInputStream.java
new file mode 100644
index 0000000..c9eca70
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/CloseableEmptyInputStream.java
@@ -0,0 +1,96 @@
+/*
+ * 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)) {
+            //noinspection UnnecessaryReturnStatement
+            return; // debug breakpoint
+        }
+    }
+}


[44/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common utilities code from sshd-core into sshd-common (new artifact)

Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/ECDSAPublicKeyEntryDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/ECDSAPublicKeyEntryDecoder.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/ECDSAPublicKeyEntryDecoder.java
new file mode 100644
index 0000000..397a007
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/ECDSAPublicKeyEntryDecoder.java
@@ -0,0 +1,178 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config.keys.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchProviderException;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPoint;
+import java.security.spec.ECPrivateKeySpec;
+import java.security.spec.ECPublicKeySpec;
+import java.security.spec.InvalidKeySpecException;
+import java.util.Objects;
+
+import org.apache.sshd.common.cipher.ECCurves;
+import org.apache.sshd.common.config.keys.KeyEntryResolver;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.util.buffer.BufferUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class ECDSAPublicKeyEntryDecoder extends AbstractPublicKeyEntryDecoder<ECPublicKey, ECPrivateKey> {
+    public static final ECDSAPublicKeyEntryDecoder INSTANCE = new ECDSAPublicKeyEntryDecoder();
+
+    // see rfc5480 section 2.2
+    public static final byte ECPOINT_UNCOMPRESSED_FORM_INDICATOR = 0x04;
+    public static final byte ECPOINT_COMPRESSED_VARIANT_2 = 0x02;
+    public static final byte ECPOINT_COMPRESSED_VARIANT_3 = 0x02;
+
+    public ECDSAPublicKeyEntryDecoder() {
+        super(ECPublicKey.class, ECPrivateKey.class, ECCurves.KEY_TYPES);
+    }
+
+    @Override
+    public ECPublicKey decodePublicKey(String keyType, InputStream keyData) throws IOException, GeneralSecurityException {
+        ECCurves curve = ECCurves.fromKeyType(keyType);
+        if (curve == null) {
+            throw new InvalidKeySpecException("Not an EC curve name: " + keyType);
+        }
+
+        if (!SecurityUtils.isECCSupported()) {
+            throw new NoSuchProviderException("ECC not supported");
+        }
+
+        String keyCurveName = curve.getName();
+        // see rfc5656 section 3.1
+        String encCurveName = KeyEntryResolver.decodeString(keyData);
+        if (!keyCurveName.equals(encCurveName)) {
+            throw new InvalidKeySpecException("Mismatched key curve name (" + keyCurveName + ") vs. encoded one (" + encCurveName + ")");
+        }
+
+        byte[] octets = KeyEntryResolver.readRLEBytes(keyData);
+        ECPoint w;
+        try {
+            w = ECCurves.octetStringToEcPoint(octets);
+            if (w == null) {
+                throw new InvalidKeySpecException("No ECPoint generated for curve=" + keyCurveName
+                        + " from octets=" + BufferUtils.toHex(':', octets));
+            }
+        } catch (RuntimeException e) {
+            throw new InvalidKeySpecException("Failed (" + e.getClass().getSimpleName() + ")"
+                    + " to generate ECPoint for curve=" + keyCurveName
+                    + " from octets=" + BufferUtils.toHex(':', octets)
+                    + ": " + e.getMessage());
+        }
+
+        ECParameterSpec paramSpec = curve.getParameters();
+        return generatePublicKey(new ECPublicKeySpec(w, paramSpec));
+    }
+
+    @Override
+    public ECPublicKey clonePublicKey(ECPublicKey key) throws GeneralSecurityException {
+        if (!SecurityUtils.isECCSupported()) {
+            throw new NoSuchProviderException("ECC not supported");
+        }
+
+        if (key == null) {
+            return null;
+        }
+
+        ECParameterSpec params = key.getParams();
+        if (params == null) {
+            throw new InvalidKeyException("Missing parameters in key");
+        }
+
+        return generatePublicKey(new ECPublicKeySpec(key.getW(), params));
+    }
+
+    @Override
+    public ECPrivateKey clonePrivateKey(ECPrivateKey key) throws GeneralSecurityException {
+        if (!SecurityUtils.isECCSupported()) {
+            throw new NoSuchProviderException("ECC not supported");
+        }
+
+        if (key == null) {
+            return null;
+        }
+
+        ECParameterSpec params = key.getParams();
+        if (params == null) {
+            throw new InvalidKeyException("Missing parameters in key");
+        }
+
+        return generatePrivateKey(new ECPrivateKeySpec(key.getS(), params));
+    }
+
+    @Override
+    public String encodePublicKey(OutputStream s, ECPublicKey key) throws IOException {
+        Objects.requireNonNull(key, "No public key provided");
+
+        ECParameterSpec params = Objects.requireNonNull(key.getParams(), "No EC parameters available");
+        ECCurves curve = Objects.requireNonNull(ECCurves.fromCurveParameters(params), "Cannot determine curve");
+        String keyType = curve.getKeyType();
+        String curveName = curve.getName();
+        KeyEntryResolver.encodeString(s, keyType);
+        // see rfc5656 section 3.1
+        KeyEntryResolver.encodeString(s, curveName);
+        ECCurves.ECPointCompression.UNCOMPRESSED.writeECPoint(s, curveName, key.getW());
+        return keyType;
+    }
+
+    @Override
+    public KeyFactory getKeyFactoryInstance() throws GeneralSecurityException {
+        if (SecurityUtils.isECCSupported()) {
+            return SecurityUtils.getKeyFactory(KeyUtils.EC_ALGORITHM);
+        } else {
+            throw new NoSuchProviderException("ECC not supported");
+        }
+    }
+
+    @Override
+    public KeyPair generateKeyPair(int keySize) throws GeneralSecurityException {
+        ECCurves curve = ECCurves.fromCurveSize(keySize);
+        if (curve == null) {
+            throw new InvalidKeySpecException("Unknown curve for key size=" + keySize);
+        }
+
+        KeyPairGenerator gen = getKeyPairGenerator();
+        gen.initialize(curve.getParameters());
+        return gen.generateKeyPair();
+    }
+
+    @Override
+    public KeyPairGenerator getKeyPairGenerator() throws GeneralSecurityException {
+        if (SecurityUtils.isECCSupported()) {
+            return SecurityUtils.getKeyPairGenerator(KeyUtils.EC_ALGORITHM);
+        } else {
+            throw new NoSuchProviderException("ECC not supported");
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/RSAPublicKeyDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/RSAPublicKeyDecoder.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/RSAPublicKeyDecoder.java
new file mode 100644
index 0000000..4550815
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/RSAPublicKeyDecoder.java
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config.keys.impl;
+
+import java.io.IOException;
+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;
+import java.util.Objects;
+
+import org.apache.sshd.common.config.keys.KeyEntryResolver;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class RSAPublicKeyDecoder extends AbstractPublicKeyEntryDecoder<RSAPublicKey, RSAPrivateKey> {
+    public static final RSAPublicKeyDecoder INSTANCE = new RSAPublicKeyDecoder();
+
+    public RSAPublicKeyDecoder() {
+        super(RSAPublicKey.class, RSAPrivateKey.class, Collections.unmodifiableList(Collections.singletonList(KeyPairProvider.SSH_RSA)));
+    }
+
+    @Override
+    public RSAPublicKey decodePublicKey(String keyType, InputStream keyData) throws IOException, GeneralSecurityException {
+        if (!KeyPairProvider.SSH_RSA.equals(keyType)) { // just in case we were invoked directly
+            throw new InvalidKeySpecException("Unexpected key type: " + keyType);
+        }
+
+        BigInteger e = KeyEntryResolver.decodeBigInt(keyData);
+        BigInteger n = KeyEntryResolver.decodeBigInt(keyData);
+
+        return generatePublicKey(new RSAPublicKeySpec(n, e));
+    }
+
+    @Override
+    public String encodePublicKey(OutputStream s, RSAPublicKey key) throws IOException {
+        Objects.requireNonNull(key, "No public key provided");
+        KeyEntryResolver.encodeString(s, KeyPairProvider.SSH_RSA);
+        KeyEntryResolver.encodeBigInt(s, key.getPublicExponent());
+        KeyEntryResolver.encodeBigInt(s, key.getModulus());
+
+        return KeyPairProvider.SSH_RSA;
+    }
+
+    @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(KeyUtils.RSA_ALGORITHM);
+    }
+
+    @Override
+    public KeyFactory getKeyFactoryInstance() throws GeneralSecurityException {
+        return SecurityUtils.getKeyFactory(KeyUtils.RSA_ALGORITHM);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/AESPrivateKeyObfuscator.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/AESPrivateKeyObfuscator.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/AESPrivateKeyObfuscator.java
new file mode 100644
index 0000000..4437945
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/AESPrivateKeyObfuscator.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.config.keys.loader;
+
+import java.security.GeneralSecurityException;
+import java.security.Key;
+import java.security.spec.InvalidKeySpecException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class AESPrivateKeyObfuscator extends AbstractPrivateKeyObfuscator {
+    public static final String CIPHER_NAME = "AES";
+    public static final AESPrivateKeyObfuscator INSTANCE = new AESPrivateKeyObfuscator();
+
+    public AESPrivateKeyObfuscator() {
+        super(CIPHER_NAME);
+    }
+
+    @Override
+    public List<Integer> getSupportedKeySizes() {
+        return getAvailableKeyLengths();
+    }
+
+    @Override
+    public byte[] applyPrivateKeyCipher(byte[] bytes, PrivateKeyEncryptionContext encContext, boolean encryptIt) throws GeneralSecurityException {
+        int keyLength = resolveKeyLength(encContext);
+        byte[] keyValue = deriveEncryptionKey(encContext, keyLength / Byte.SIZE);
+        return applyPrivateKeyCipher(bytes, encContext, keyLength, keyValue, encryptIt);
+    }
+
+    @Override
+    protected int resolveKeyLength(PrivateKeyEncryptionContext encContext) throws GeneralSecurityException {
+        String cipherType = encContext.getCipherType();
+        try {
+            int keyLength = Integer.parseInt(cipherType);
+            List<Integer> sizes = getSupportedKeySizes();
+            for (Integer s : sizes) {
+                if (s.intValue() == keyLength) {
+                    return keyLength;
+                }
+            }
+
+            throw new InvalidKeySpecException("Unknown " + getCipherName() + " key length: " + cipherType + " - supported: " + sizes);
+        } catch (NumberFormatException e) {
+            throw new InvalidKeySpecException("Bad " + getCipherName() + " key length (" + cipherType + "): " + e.getMessage(), e);
+        }
+    }
+
+    /**
+     * @return A {@link List} of {@link Integer}s holding the available key
+     * lengths values (in bits) for the JVM. <B>Note:</B> AES 256 requires
+     * special JCE policy extension installation (e.g., for Java 7 see
+     * <A HREF="http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html">this link</A>)
+     */
+    @SuppressWarnings("synthetic-access")
+    public static List<Integer> getAvailableKeyLengths() {
+        return LazyKeyLengthsHolder.KEY_LENGTHS;
+    }
+
+    private static final class LazyKeyLengthsHolder {
+        private static final List<Integer> KEY_LENGTHS = Collections.unmodifiableList(detectSupportedKeySizes());
+
+        private LazyKeyLengthsHolder() {
+            throw new UnsupportedOperationException("No instance allowed");
+        }
+
+        // AES 256 requires special JCE policy extension installation
+        private static List<Integer> detectSupportedKeySizes() {
+            List<Integer> sizes = new ArrayList<>();
+            for (int keyLength = 128; keyLength < Short.MAX_VALUE /* just so it doesn't go forever */; keyLength += 64) {
+                try {
+                    byte[] keyAsBytes = new byte[keyLength / Byte.SIZE];
+                    Key key = new SecretKeySpec(keyAsBytes, CIPHER_NAME);
+                    Cipher c = SecurityUtils.getCipher(CIPHER_NAME);
+                    c.init(Cipher.DECRYPT_MODE, key);
+                    sizes.add(Integer.valueOf(keyLength));
+                } catch (GeneralSecurityException e) {
+                    return sizes;
+                }
+            }
+
+            throw new IllegalStateException("No limit encountered: " + sizes);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/AbstractKeyPairResourceParser.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/AbstractKeyPairResourceParser.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/AbstractKeyPairResourceParser.java
new file mode 100644
index 0000000..a83bf68
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/AbstractKeyPairResourceParser.java
@@ -0,0 +1,181 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config.keys.loader;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StreamCorruptedException;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.buffer.BufferUtils;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractKeyPairResourceParser extends AbstractLoggingBean implements KeyPairResourceParser {
+    private final List<String> beginners;
+    private final List<String> enders;
+    private final List<List<String>> endingMarkers;
+
+    /**
+     * @param beginners The markers that indicate the beginning of a parsing block
+     * @param enders The <U>matching</U> (by position) markers that indicate the end of a parsing block
+     */
+    protected AbstractKeyPairResourceParser(List<String> beginners, List<String> enders) {
+        this.beginners = ValidateUtils.checkNotNullAndNotEmpty(beginners, "No begin markers");
+        this.enders = ValidateUtils.checkNotNullAndNotEmpty(enders, "No end markers");
+        ValidateUtils.checkTrue(
+                beginners.size() == enders.size(), "Mismatched begin(%d)/end(%d) markers sizes", beginners.size(), enders.size());
+        endingMarkers = new ArrayList<>(enders.size());
+        enders.forEach(m -> endingMarkers.add(Collections.singletonList(m)));
+    }
+
+    public List<String> getBeginners() {
+        return beginners;
+    }
+
+    public List<String> getEnders() {
+        return enders;
+    }
+
+    /**
+     * @return A {@link List} of same size as the ending markers, where
+     * each ending marker is encapsulated inside a singleton list and
+     * resides as the <U>same index</U> as the marker it encapsulates
+     */
+    public List<List<String>> getEndingMarkers() {
+        return endingMarkers;
+    }
+
+    @Override
+    public boolean canExtractKeyPairs(String resourceKey, List<String> lines) throws IOException, GeneralSecurityException {
+        return KeyPairResourceParser.containsMarkerLine(lines, getBeginners());
+    }
+
+    @Override
+    public Collection<KeyPair> loadKeyPairs(String resourceKey, FilePasswordProvider passwordProvider, List<String> lines)
+            throws IOException, GeneralSecurityException {
+        Collection<KeyPair> keyPairs = Collections.emptyList();
+        List<String> beginMarkers = getBeginners();
+        List<List<String>> endMarkers = getEndingMarkers();
+        for (Map.Entry<Integer, Integer> markerPos = KeyPairResourceParser.findMarkerLine(lines, beginMarkers); markerPos != null;) {
+            int startIndex = markerPos.getKey();
+            String startLine = lines.get(startIndex);
+            startIndex++;
+
+            int markerIndex = markerPos.getValue();
+            List<String> ender = endMarkers.get(markerIndex);
+            markerPos = KeyPairResourceParser.findMarkerLine(lines, startIndex, ender);
+            if (markerPos == null) {
+                throw new StreamCorruptedException("Missing end marker (" + ender + ") after line #" + startIndex);
+            }
+
+            int endIndex = markerPos.getKey();
+            String endLine = lines.get(endIndex);
+            Collection<KeyPair> kps =
+                extractKeyPairs(resourceKey, startLine, endLine, passwordProvider, lines.subList(startIndex, endIndex));
+            if (GenericUtils.isNotEmpty(kps)) {
+                if (GenericUtils.isEmpty(keyPairs)) {
+                    keyPairs = new LinkedList<>(kps);
+                } else {
+                    keyPairs.addAll(kps);
+                }
+            }
+
+            // see if there are more
+            markerPos = KeyPairResourceParser.findMarkerLine(lines, endIndex + 1, beginMarkers);
+        }
+
+        return keyPairs;
+    }
+
+    /**
+     * Extracts the key pairs within a <U>single</U> delimited by markers block of lines. By
+     * default cleans up the empty lines, joins them and converts them from BASE64
+     *
+     * @param resourceKey A hint as to the origin of the text lines
+     * @param beginMarker The line containing the begin marker
+     * @param endMarker The line containing the end marker
+     * @param passwordProvider The {@link FilePasswordProvider} to use
+     * in case the data is encrypted - may be {@code null} if no encrypted
+     * @param lines The block of lines between the markers
+     * @return The extracted {@link KeyPair}s - may be {@code null}/empty if none.
+     * @throws IOException If failed to parse the data
+     * @throws GeneralSecurityException If failed to generate the keys
+     * @see #extractKeyPairs(String, String, String, FilePasswordProvider, byte[])
+     */
+    public Collection<KeyPair> extractKeyPairs(
+            String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, List<String> lines)
+                    throws IOException, GeneralSecurityException {
+        return extractKeyPairs(resourceKey, beginMarker, endMarker, passwordProvider, KeyPairResourceParser.extractDataBytes(lines));
+    }
+
+    /**
+     * @param resourceKey A hint as to the origin of the text lines
+     * @param beginMarker The line containing the begin marker
+     * @param endMarker The line containing the end marker
+     * @param passwordProvider The {@link FilePasswordProvider} to use
+     * in case the data is encrypted - may be {@code null} if no encrypted
+     * @param bytes The decoded bytes from the lines containing the data
+     * @return The extracted {@link KeyPair}s - may be {@code null}/empty if none.
+     * @throws IOException If failed to parse the data
+     * @throws GeneralSecurityException If failed to generate the keys
+     * @see #extractKeyPairs(String, String, String, FilePasswordProvider, InputStream)
+     */
+    public Collection<KeyPair> extractKeyPairs(
+            String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, byte[] bytes)
+                    throws IOException, GeneralSecurityException {
+        if (log.isTraceEnabled()) {
+            BufferUtils.dumpHex(getSimplifiedLogger(), Level.FINER, beginMarker, ':', 16, bytes);
+        }
+
+        try (InputStream bais = new ByteArrayInputStream(bytes)) {
+            return extractKeyPairs(resourceKey, beginMarker, endMarker, passwordProvider, bais);
+        }
+    }
+
+    /**
+     * @param resourceKey A hint as to the origin of the text lines
+     * @param beginMarker The line containing the begin marker
+     * @param endMarker The line containing the end marker
+     * @param passwordProvider The {@link FilePasswordProvider} to use
+     * in case the data is encrypted - may be {@code null} if no encrypted
+     * @param stream The decoded data {@link InputStream}
+     * @return The extracted {@link KeyPair}s - may be {@code null}/empty if none.
+     * @throws IOException If failed to parse the data
+     * @throws GeneralSecurityException If failed to generate the keys
+     */
+    public abstract Collection<KeyPair> extractKeyPairs(
+            String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, InputStream stream)
+                    throws IOException, GeneralSecurityException;
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/AbstractPrivateKeyObfuscator.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/AbstractPrivateKeyObfuscator.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/AbstractPrivateKeyObfuscator.java
new file mode 100644
index 0000000..ac93ab4
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/AbstractPrivateKeyObfuscator.java
@@ -0,0 +1,191 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.config.keys.loader;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
+import java.security.MessageDigest;
+import java.security.SecureRandom;
+import java.security.spec.InvalidKeySpecException;
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.Random;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.apache.sshd.common.digest.BuiltinDigests;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.buffer.BufferUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractPrivateKeyObfuscator implements PrivateKeyObfuscator {
+    private final String algName;
+
+    protected AbstractPrivateKeyObfuscator(String name) {
+        algName = ValidateUtils.checkNotNullAndNotEmpty(name, "No name specified");
+    }
+
+    @Override
+    public final String getCipherName() {
+        return algName;
+    }
+
+    @Override
+    public byte[] generateInitializationVector(PrivateKeyEncryptionContext encContext) throws GeneralSecurityException {
+        return generateInitializationVector(resolveKeyLength(encContext));
+    }
+
+    @Override
+    public <A extends Appendable> A appendPrivateKeyEncryptionContext(A sb, PrivateKeyEncryptionContext encContext) throws IOException {
+        if (encContext == null) {
+            return sb;
+        }
+
+        sb.append("DEK-Info: ").append(encContext.getCipherName())
+          .append('-').append(encContext.getCipherType())
+          .append('-').append(encContext.getCipherMode());
+
+        byte[] initVector = encContext.getInitVector();
+        Objects.requireNonNull(initVector, "No encryption init vector");
+        ValidateUtils.checkTrue(initVector.length > 0, "Empty encryption init vector");
+        BufferUtils.appendHex(sb.append(','), BufferUtils.EMPTY_HEX_SEPARATOR, initVector);
+        sb.append(System.lineSeparator());
+        return sb;
+    }
+
+    protected byte[] generateInitializationVector(int keyLength) {
+        int keySize = keyLength / Byte.SIZE;
+        if ((keyLength % Byte.SIZE) != 0) { // e.g., if 36-bits then we need 5 bytes to hold
+            keySize++;
+        }
+
+        byte[] initVector = new byte[keySize];
+        Random randomizer = new SecureRandom();  // TODO consider using some pre-created singleton instance
+        randomizer.nextBytes(initVector);
+        return initVector;
+    }
+
+    protected abstract int resolveKeyLength(PrivateKeyEncryptionContext encContext) throws GeneralSecurityException;
+
+    // see http://martin.kleppmann.com/2013/05/24/improving-security-of-ssh-private-keys.html
+    // see http://www.ict.griffith.edu.au/anthony/info/crypto/openssl.hints (Password to Encryption Key section)
+    // see http://openssl.6102.n7.nabble.com/DES-EDE3-CBC-technical-details-td24883.html
+    protected byte[] deriveEncryptionKey(PrivateKeyEncryptionContext encContext, int outputKeyLength) throws GeneralSecurityException {
+        Objects.requireNonNull(encContext, "No encryption context");
+        ValidateUtils.checkNotNullAndNotEmpty(encContext.getCipherName(), "No cipher name");
+        ValidateUtils.checkNotNullAndNotEmpty(encContext.getCipherType(), "No cipher type");
+        ValidateUtils.checkNotNullAndNotEmpty(encContext.getCipherMode(), "No cipher mode");
+
+        byte[] initVector = Objects.requireNonNull(encContext.getInitVector(), "No encryption init vector");
+        ValidateUtils.checkTrue(initVector.length > 0, "Empty encryption init vector");
+
+        String password = ValidateUtils.checkNotNullAndNotEmpty(encContext.getPassword(), "No encryption password");
+        byte[] passBytes = password.getBytes(StandardCharsets.UTF_8);
+        byte[] keyValue = new byte[outputKeyLength];
+        MessageDigest hash = SecurityUtils.getMessageDigest(BuiltinDigests.Constants.MD5);
+        byte[]  prevHash = GenericUtils.EMPTY_BYTE_ARRAY;
+        for (int index = 0, remLen = keyValue.length; index < keyValue.length;) {
+            hash.reset();    // just making sure
+
+            hash.update(prevHash, 0, prevHash.length);
+            hash.update(passBytes, 0, passBytes.length);
+            hash.update(initVector, 0, Math.min(initVector.length, 8));
+
+            prevHash = hash.digest();
+
+            System.arraycopy(prevHash, 0, keyValue, index, Math.min(remLen, prevHash.length));
+            index += prevHash.length;
+            remLen -= prevHash.length;
+        }
+
+        return keyValue;
+    }
+
+    protected byte[] applyPrivateKeyCipher(byte[] bytes, PrivateKeyEncryptionContext encContext, int numBits, byte[] keyValue, boolean encryptIt)
+            throws GeneralSecurityException {
+        Objects.requireNonNull(encContext, "No encryption context");
+        String cipherName = ValidateUtils.checkNotNullAndNotEmpty(encContext.getCipherName(), "No cipher name");
+        ValidateUtils.checkNotNullAndNotEmpty(encContext.getCipherType(), "No cipher type");
+        String cipherMode = ValidateUtils.checkNotNullAndNotEmpty(encContext.getCipherMode(), "No cipher mode");
+
+        Objects.requireNonNull(bytes, "No source data");
+        Objects.requireNonNull(keyValue, "No encryption key");
+        ValidateUtils.checkTrue(keyValue.length > 0, "Empty encryption key");
+
+        byte[] initVector = Objects.requireNonNull(encContext.getInitVector(), "No encryption init vector");
+        ValidateUtils.checkTrue(initVector.length > 0, "Empty encryption init vector");
+
+        String xform = cipherName + "/" + cipherMode + "/NoPadding";
+        int maxAllowedBits = Cipher.getMaxAllowedKeyLength(xform);
+        // see http://www.javamex.com/tutorials/cryptography/unrestricted_policy_files.shtml
+        if (numBits > maxAllowedBits) {
+            throw new InvalidKeySpecException("applyPrivateKeyCipher(" + xform + ")[encrypt=" + encryptIt + "]"
+                                            + " required key length (" + numBits + ")"
+                                            + " exceeds max. available: " + maxAllowedBits);
+        }
+
+        SecretKeySpec skeySpec = new SecretKeySpec(keyValue, cipherName);
+        IvParameterSpec ivspec = new IvParameterSpec(initVector);
+        Cipher cipher = SecurityUtils.getCipher(xform);
+        int blockSize = cipher.getBlockSize();
+        int dataSize = bytes.length;
+        cipher.init(encryptIt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE, skeySpec, ivspec);
+        if (blockSize <= 0) {
+            return cipher.doFinal(bytes);
+        }
+
+        int remLen = dataSize % blockSize;
+        if (remLen <= 0) {
+            return cipher.doFinal(bytes);
+        }
+
+        int updateSize = dataSize - remLen;
+        byte[] lastBlock = new byte[blockSize];
+        Arrays.fill(lastBlock, (byte) 10);
+        System.arraycopy(bytes, updateSize, lastBlock, 0, remLen);
+
+        // TODO for some reason, calling cipher.update followed by cipher.doFinal does not work
+        ByteArrayOutputStream baos = new ByteArrayOutputStream(dataSize);
+        try {
+            try {
+                byte[] buf = cipher.update(bytes, 0, updateSize);
+                baos.write(buf);
+
+                buf = cipher.doFinal(lastBlock);
+                baos.write(buf);
+            } finally {
+                baos.close();
+            }
+        } catch (IOException e) {
+            throw new GeneralSecurityException("applyPrivateKeyCipher(" + xform + ")[encrypt=" + encryptIt + "]"
+                                             + " failed (" + e.getClass().getSimpleName() + ")"
+                                             + " to split-write: " + e.getMessage(), e);
+        }
+
+        return baos.toByteArray();
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/DESPrivateKeyObfuscator.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/DESPrivateKeyObfuscator.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/DESPrivateKeyObfuscator.java
new file mode 100644
index 0000000..2043f06
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/DESPrivateKeyObfuscator.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.config.keys.loader;
+
+import java.security.GeneralSecurityException;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class DESPrivateKeyObfuscator extends AbstractPrivateKeyObfuscator {
+    public static final int DEFAULT_KEY_LENGTH = 24 /* hardwired size for 3DES */;
+    public static final List<Integer> AVAILABLE_KEY_LENGTHS =
+            Collections.unmodifiableList(Collections.singletonList(Integer.valueOf(DEFAULT_KEY_LENGTH)));
+    public static final DESPrivateKeyObfuscator INSTANCE = new DESPrivateKeyObfuscator();
+
+    public DESPrivateKeyObfuscator() {
+        super("DES");
+    }
+
+    @Override
+    public byte[] applyPrivateKeyCipher(byte[] bytes, PrivateKeyEncryptionContext encContext, boolean encryptIt) throws GeneralSecurityException {
+        PrivateKeyEncryptionContext effContext = resolveEffectiveContext(encContext);
+        byte[] keyValue = deriveEncryptionKey(effContext, DEFAULT_KEY_LENGTH);
+        return applyPrivateKeyCipher(bytes, effContext, keyValue.length * Byte.SIZE, keyValue, encryptIt);
+    }
+
+    @Override
+    public List<Integer> getSupportedKeySizes() {
+        return AVAILABLE_KEY_LENGTHS;
+    }
+
+    @Override
+    protected int resolveKeyLength(PrivateKeyEncryptionContext encContext) throws GeneralSecurityException {
+        return DEFAULT_KEY_LENGTH;
+    }
+
+    @Override
+    protected byte[] generateInitializationVector(int keyLength) {
+        return super.generateInitializationVector(8 * Byte.SIZE);
+    }
+
+    public static final PrivateKeyEncryptionContext resolveEffectiveContext(PrivateKeyEncryptionContext encContext) {
+        if (encContext == null) {
+            return null;
+        }
+
+        String cipherName = encContext.getCipherName();
+        String cipherType = encContext.getCipherType();
+        PrivateKeyEncryptionContext effContext = encContext;
+        if ("EDE3".equalsIgnoreCase(cipherType)) {
+            cipherName += "ede";
+            effContext = encContext.clone();
+            effContext.setCipherName(cipherName);
+        }
+
+        return effContext;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/KeyPairResourceLoader.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/KeyPairResourceLoader.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/KeyPairResourceLoader.java
new file mode 100644
index 0000000..fa6930a
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/KeyPairResourceLoader.java
@@ -0,0 +1,129 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config.keys.loader;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StringReader;
+import java.net.URL;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.OpenOption;
+import java.nio.file.Path;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.util.io.IoUtils;
+
+/**
+ * Loads {@link KeyPair}s from text resources
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FunctionalInterface
+public interface KeyPairResourceLoader {
+    /**
+     * An empty loader that never fails but always returns an empty list
+     */
+    KeyPairResourceLoader EMPTY = (resourceKey, passwordProvider, lines) -> Collections.emptyList();
+
+    default Collection<KeyPair> loadKeyPairs(Path path, FilePasswordProvider passwordProvider, OpenOption... options)
+            throws IOException, GeneralSecurityException {
+        return loadKeyPairs(path, passwordProvider, StandardCharsets.UTF_8, options);
+    }
+
+    default Collection<KeyPair> loadKeyPairs(Path path, FilePasswordProvider passwordProvider, Charset cs, OpenOption... options)
+            throws IOException, GeneralSecurityException {
+        try (InputStream stream = Files.newInputStream(path, options)) {
+            return loadKeyPairs(path.toString(), passwordProvider, stream, cs);
+        }
+    }
+
+    default Collection<KeyPair> loadKeyPairs(URL url, FilePasswordProvider passwordProvider)
+            throws IOException, GeneralSecurityException {
+        return loadKeyPairs(url, passwordProvider, StandardCharsets.UTF_8);
+    }
+
+    default Collection<KeyPair> loadKeyPairs(URL url, FilePasswordProvider passwordProvider, Charset cs)
+            throws IOException, GeneralSecurityException {
+        try (InputStream stream = Objects.requireNonNull(url, "No URL").openStream()) {
+            return loadKeyPairs(url.toExternalForm(), passwordProvider, stream, cs);
+        }
+    }
+
+    default Collection<KeyPair> loadKeyPairs(String resourceKey, FilePasswordProvider passwordProvider, String data)
+            throws IOException, GeneralSecurityException {
+        try (Reader reader = new StringReader((data == null) ? "" : data)) {
+            return loadKeyPairs(resourceKey, passwordProvider, reader);
+        }
+    }
+
+    default Collection<KeyPair> loadKeyPairs(String resourceKey, FilePasswordProvider passwordProvider, InputStream stream)
+            throws IOException, GeneralSecurityException {
+        return loadKeyPairs(resourceKey, passwordProvider, stream, StandardCharsets.UTF_8);
+    }
+
+    default Collection<KeyPair> loadKeyPairs(String resourceKey, FilePasswordProvider passwordProvider, InputStream stream, Charset cs)
+            throws IOException, GeneralSecurityException {
+        try (Reader reader = new InputStreamReader(
+                Objects.requireNonNull(stream, "No stream instance"), Objects.requireNonNull(cs, "No charset"))) {
+            return loadKeyPairs(resourceKey, passwordProvider, reader);
+        }
+    }
+
+    default Collection<KeyPair> loadKeyPairs(String resourceKey, FilePasswordProvider passwordProvider, Reader r)
+            throws IOException, GeneralSecurityException {
+        try (BufferedReader br = new BufferedReader(Objects.requireNonNull(r, "No reader instance"), IoUtils.DEFAULT_COPY_SIZE)) {
+            return loadKeyPairs(resourceKey, passwordProvider, br);
+        }
+    }
+
+    default Collection<KeyPair> loadKeyPairs(String resourceKey, FilePasswordProvider passwordProvider, BufferedReader r)
+            throws IOException, GeneralSecurityException {
+        return loadKeyPairs(resourceKey, passwordProvider, IoUtils.readAllLines(r));
+    }
+
+    /**
+     * Loads key pairs from the given resource text lines
+     *
+     * @param resourceKey A hint as to the origin of the text lines
+     * @param passwordProvider The {@link FilePasswordProvider} to use
+     * in case the data is encrypted - may be {@code null} if no encrypted
+     * data is expected
+     * @param lines The {@link List} of lines as read from the resource
+     * @return The extracted {@link KeyPair}s - may be {@code null}/empty if none.
+     * <B>Note:</B> the resource loader may decide to skip unknown lines if
+     * more than one key pair type is encoded in it
+     * @throws IOException If failed to process the lines
+     * @throws GeneralSecurityException If failed to generate the keys from the
+     * parsed data
+     */
+    Collection<KeyPair> loadKeyPairs(String resourceKey, FilePasswordProvider passwordProvider, List<String> lines)
+            throws IOException, GeneralSecurityException;
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/KeyPairResourceParser.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/KeyPairResourceParser.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/KeyPairResourceParser.java
new file mode 100644
index 0000000..80fc2c5
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/KeyPairResourceParser.java
@@ -0,0 +1,192 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config.keys.loader;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.Arrays;
+import java.util.Base64;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface KeyPairResourceParser extends KeyPairResourceLoader {
+    /**
+     * An empty parser that never fails, but always report that it cannot
+     * extract key pairs and returns empty list if asked to load
+     */
+    KeyPairResourceParser EMPTY = new KeyPairResourceParser() {
+        @Override
+        public Collection<KeyPair> loadKeyPairs(String resourceKey, FilePasswordProvider passwordProvider, List<String> lines)
+                throws IOException, GeneralSecurityException {
+            return Collections.emptyList();
+        }
+
+        @Override
+        public boolean canExtractKeyPairs(String resourceKey, List<String> lines) throws IOException, GeneralSecurityException {
+            return false;
+        }
+
+        @Override
+        public String toString() {
+            return "EMPTY";
+        }
+    };
+
+    /**
+     * @param resourceKey A hint as to the origin of the text lines
+     * @param lines The resource lines
+     * @return {@code true} if the parser can extract some key pairs from the lines
+     * @throws IOException If failed to process the lines
+     * @throws GeneralSecurityException If failed to extract information regarding
+     * the possibility to extract the key pairs
+     */
+    boolean canExtractKeyPairs(String resourceKey, List<String> lines)
+            throws IOException, GeneralSecurityException;
+
+    /**
+     * Converts the lines assumed to contain BASE-64 encoded data into
+     * the actual content bytes.
+     *
+     * @param lines The data lines - empty lines and spaces are automatically
+     * deleted <U>before</U> BASE-64 decoding takes place.
+     * @return The decoded data bytes
+     * @see #joinDataLines(Collection)
+     */
+    static byte[] extractDataBytes(Collection<String> lines) {
+        String data = joinDataLines(lines);
+        Base64.Decoder decoder = Base64.getDecoder();
+        return decoder.decode(data);
+    }
+
+    static String joinDataLines(Collection<String> lines) {
+        String data = GenericUtils.join(lines, ' ');
+        data = data.replaceAll("\\s", "");
+        data = data.trim();
+        return data;
+    }
+
+    static boolean containsMarkerLine(List<String> lines, String marker) {
+        return containsMarkerLine(lines, Collections.singletonList(ValidateUtils.checkNotNullAndNotEmpty(marker, "No marker")));
+    }
+
+    static boolean containsMarkerLine(List<String> lines, List<String> markers) {
+        return findMarkerLine(lines, markers) != null;
+    }
+
+    /**
+     * Attempts to locate a line that contains one of the markers
+     *
+     * @param lines The list of lines to scan - ignored if {@code null}/empty
+     * @param markers The markers to match - ignored if {@code null}/empty
+     * @return A {@link SimpleImmutableEntry} whose key is the <U>first</U> line index
+     * that matched and value the matched marker index - {@code null} if no match found
+     * @see #findMarkerLine(List, int, List)
+     */
+    static SimpleImmutableEntry<Integer, Integer> findMarkerLine(List<String> lines, List<String> markers) {
+        return findMarkerLine(lines, 0, markers);
+    }
+
+    /**
+     * Attempts to locate a line that contains one of the markers
+     *
+     * @param lines The list of lines to scan - ignored if {@code null}/empty
+     * @param startLine The scan start line index
+     * @param markers The markers to match - ignored if {@code null}/empty
+     * @return A {@link SimpleImmutableEntry} whose key is the <U>first</U> line index
+     * that matched and value the matched marker index - {@code null} if no match found
+     */
+    static SimpleImmutableEntry<Integer, Integer> findMarkerLine(List<String> lines, int startLine, List<String> markers) {
+        if (GenericUtils.isEmpty(lines) || GenericUtils.isEmpty(markers)) {
+            return null;
+        }
+
+        for (int lineIndex = startLine; lineIndex < lines.size(); lineIndex++) {
+            String l = lines.get(lineIndex);
+            for (int markerIndex = 0; markerIndex < markers.size(); markerIndex++) {
+                String m = markers.get(markerIndex);
+                if (l.contains(m)) {
+                    return new SimpleImmutableEntry<>(lineIndex, markerIndex);
+                }
+            }
+        }
+
+        return null;
+    }
+
+    static KeyPairResourceParser aggregate(KeyPairResourceParser... parsers) {
+        return aggregate(Arrays.asList(ValidateUtils.checkNotNullAndNotEmpty(parsers, "No parsers to aggregate")));
+    }
+
+    static KeyPairResourceParser aggregate(Collection<? extends KeyPairResourceParser> parsers) {
+        ValidateUtils.checkNotNullAndNotEmpty(parsers, "No parsers to aggregate");
+        return new KeyPairResourceParser() {
+            @Override
+            public Collection<KeyPair> loadKeyPairs(String resourceKey, FilePasswordProvider passwordProvider, List<String> lines)
+                    throws IOException, GeneralSecurityException {
+                Collection<KeyPair> keyPairs = Collections.emptyList();
+                for (KeyPairResourceParser p : parsers) {
+                    if (!p.canExtractKeyPairs(resourceKey, lines)) {
+                        continue;
+                    }
+
+                    Collection<KeyPair> kps = p.loadKeyPairs(resourceKey, passwordProvider, lines);
+                    if (GenericUtils.isEmpty(kps)) {
+                        continue;
+                    }
+
+                    if (GenericUtils.isEmpty(keyPairs)) {
+                        keyPairs = new LinkedList<>(kps);
+                    } else {
+                        keyPairs.addAll(kps);
+                    }
+                }
+
+                return keyPairs;
+            }
+
+            @Override
+            public boolean canExtractKeyPairs(String resourceKey, List<String> lines) throws IOException, GeneralSecurityException {
+                for (KeyPairResourceParser p : parsers) {
+                    if (p.canExtractKeyPairs(resourceKey, lines)) {
+                        return true;
+                    }
+                }
+
+                return false;
+            }
+
+            @Override
+            public String toString() {
+                return KeyPairResourceParser.class.getSimpleName() + "[aggregate]";
+            }
+        };
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/PrivateKeyEncryptionContext.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/PrivateKeyEncryptionContext.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/PrivateKeyEncryptionContext.java
new file mode 100644
index 0000000..5e03cdb
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/PrivateKeyEncryptionContext.java
@@ -0,0 +1,270 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.config.keys.loader;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.NavigableSet;
+import java.util.Objects;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class PrivateKeyEncryptionContext implements Cloneable {
+    public static final String  DEFAULT_CIPHER_MODE = "CBC";
+
+    private static final Map<String, PrivateKeyObfuscator> OBFUSCATORS =
+            Stream.of(AESPrivateKeyObfuscator.INSTANCE, DESPrivateKeyObfuscator.INSTANCE)
+                .collect(Collectors.toMap(AbstractPrivateKeyObfuscator::getCipherName, Function.identity()));
+
+    private String  cipherName;
+    private String cipherType;
+    private String cipherMode = DEFAULT_CIPHER_MODE;
+    private String password;
+    private byte[]  initVector;
+    private transient PrivateKeyObfuscator  obfuscator;
+
+    public PrivateKeyEncryptionContext() {
+        super();
+    }
+
+    public PrivateKeyEncryptionContext(String algInfo) {
+        parseAlgorithmInfo(algInfo);
+    }
+
+    public String getCipherName() {
+        return cipherName;
+    }
+
+    public void setCipherName(String value) {
+        cipherName = value;
+    }
+
+    public String getCipherType() {
+        return cipherType;
+    }
+
+    public void setCipherType(String value) {
+        cipherType = value;
+    }
+
+    public String getCipherMode() {
+        return cipherMode;
+    }
+
+    public void setCipherMode(String value) {
+        cipherMode = value;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void setPassword(String value) {
+        password = value;
+    }
+
+    public byte[] getInitVector() {
+        return initVector;
+    }
+
+    public void setInitVector(byte... values) {
+        initVector = values;
+    }
+
+    public PrivateKeyObfuscator getPrivateKeyObfuscator() {
+        return obfuscator;
+    }
+
+    public void setPrivateKeyObfuscator(PrivateKeyObfuscator value) {
+        obfuscator = value;
+    }
+
+    public PrivateKeyObfuscator resolvePrivateKeyObfuscator() {
+        PrivateKeyObfuscator value = getPrivateKeyObfuscator();
+        if (value != null) {
+            return value;
+        }
+
+        return getRegisteredPrivateKeyObfuscator(getCipherName());
+    }
+
+    public static PrivateKeyObfuscator registerPrivateKeyObfuscator(PrivateKeyObfuscator o) {
+        return registerPrivateKeyObfuscator(Objects.requireNonNull(o, "No instance provided").getCipherName(), o);
+    }
+
+    public static PrivateKeyObfuscator registerPrivateKeyObfuscator(String cipherName, PrivateKeyObfuscator o) {
+        ValidateUtils.checkNotNullAndNotEmpty(cipherName, "No cipher name");
+        Objects.requireNonNull(o, "No instance provided");
+
+        synchronized (OBFUSCATORS) {
+            return OBFUSCATORS.put(cipherName, o);
+        }
+    }
+
+    public static boolean unregisterPrivateKeyObfuscator(PrivateKeyObfuscator o) {
+        Objects.requireNonNull(o, "No instance provided");
+        String  cipherName = o.getCipherName();
+        ValidateUtils.checkNotNullAndNotEmpty(cipherName, "No cipher name");
+
+        synchronized (OBFUSCATORS) {
+            PrivateKeyObfuscator prev = OBFUSCATORS.get(cipherName);
+            if (prev != o) {
+                return false;
+            }
+
+            OBFUSCATORS.remove(cipherName);
+        }
+
+        return true;
+    }
+
+    public static PrivateKeyObfuscator unregisterPrivateKeyObfuscator(String cipherName) {
+        ValidateUtils.checkNotNullAndNotEmpty(cipherName, "No cipher name");
+
+        synchronized (OBFUSCATORS) {
+            return OBFUSCATORS.remove(cipherName);
+        }
+    }
+
+    public static final PrivateKeyObfuscator getRegisteredPrivateKeyObfuscator(String cipherName) {
+        if (GenericUtils.isEmpty(cipherName)) {
+            return null;
+        }
+
+        synchronized (OBFUSCATORS) {
+            return OBFUSCATORS.get(cipherName);
+        }
+    }
+
+    public static final NavigableSet<String> getRegisteredPrivateKeyObfuscatorCiphers() {
+        synchronized (OBFUSCATORS) {
+            Collection<String> names = OBFUSCATORS.keySet();
+            return GenericUtils.asSortedSet(String.CASE_INSENSITIVE_ORDER, names);
+        }
+    }
+
+    public static final List<PrivateKeyObfuscator> getRegisteredPrivateKeyObfuscators() {
+        synchronized (OBFUSCATORS) {
+            Collection<? extends PrivateKeyObfuscator> l = OBFUSCATORS.values();
+            if (GenericUtils.isEmpty(l)) {
+                return Collections.emptyList();
+            } else {
+                return new ArrayList<>(l);
+            }
+        }
+    }
+
+    /**
+     * @param algInfo The algorithm info - format: <I>{@code name-type-mode}</I>
+     * @return The updated context instance
+     * @see #parseAlgorithmInfo(PrivateKeyEncryptionContext, String)
+     */
+    public PrivateKeyEncryptionContext parseAlgorithmInfo(String algInfo) {
+        return parseAlgorithmInfo(this, algInfo);
+    }
+
+    @Override
+    public PrivateKeyEncryptionContext clone() {
+        try {
+            PrivateKeyEncryptionContext copy = getClass().cast(super.clone());
+            byte[] v = copy.getInitVector();
+            if (v != null) {
+                v = v.clone();
+                copy.setInitVector(v);
+            }
+            return copy;
+        } catch (CloneNotSupportedException e) { // unexpected
+            throw new RuntimeException("Failed to clone: " + toString());
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return GenericUtils.hashCode(getCipherName(), Boolean.TRUE)
+             + GenericUtils.hashCode(getCipherType(), Boolean.TRUE)
+             + GenericUtils.hashCode(getCipherMode(), Boolean.TRUE)
+             + Objects.hashCode(getPassword())
+             + Arrays.hashCode(getInitVector());
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (this == obj) {
+            return true;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+
+        PrivateKeyEncryptionContext other = (PrivateKeyEncryptionContext) obj;
+        return (GenericUtils.safeCompare(getCipherName(), other.getCipherName(), false) == 0)
+            && (GenericUtils.safeCompare(getCipherType(), other.getCipherType(), false) == 0)
+            && (GenericUtils.safeCompare(getCipherMode(), other.getCipherMode(), false) == 0)
+            && (GenericUtils.safeCompare(getPassword(), other.getPassword(), true) == 0)
+            && Arrays.equals(getInitVector(), other.getInitVector());
+    }
+
+    @Override
+    public String toString() {
+        return GenericUtils.join(new String[]{getCipherName(), getCipherType(), getCipherMode()}, '-');
+    }
+
+    /**
+     * @param <C> Generic context type
+     * @param context The {@link PrivateKeyEncryptionContext} to update
+     * @param algInfo The algorithm info - format: {@code <I>name</I>-<I>type</I>-<I>mode</I>}
+     * @return The updated context
+     */
+    public static final <C extends PrivateKeyEncryptionContext> C parseAlgorithmInfo(C context, String algInfo) {
+        ValidateUtils.checkNotNullAndNotEmpty(algInfo, "No encryption algorithm data");
+
+        String[] cipherData = GenericUtils.split(algInfo, '-');
+        ValidateUtils.checkTrue(cipherData.length == 3, "Bad encryption algorithm data: %s", algInfo);
+
+        context.setCipherName(cipherData[0]);
+        context.setCipherType(cipherData[1]);
+        context.setCipherMode(cipherData[2]);
+        return context;
+    }
+
+    public static final PrivateKeyEncryptionContext newPrivateKeyEncryptionContext(PrivateKeyObfuscator o, String password) {
+        return initializeObfuscator(new PrivateKeyEncryptionContext(), o, password);
+    }
+
+    public static final <C extends PrivateKeyEncryptionContext> C initializeObfuscator(C context, PrivateKeyObfuscator o, String password) {
+        context.setCipherName(o.getCipherName());
+        context.setPrivateKeyObfuscator(o);
+        context.setPassword(password);
+        return context;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/PrivateKeyObfuscator.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/PrivateKeyObfuscator.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/PrivateKeyObfuscator.java
new file mode 100644
index 0000000..d8d2db5
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/PrivateKeyObfuscator.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.config.keys.loader;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface PrivateKeyObfuscator {
+    /**
+     * @return Basic cipher used to obfuscate
+     */
+    String getCipherName();
+
+    /**
+     * @return A {@link List} of the supported key sizes - <B>Note:</B> every
+     * call returns a and <U>un-modifiable</U> instance.
+     */
+    List<Integer> getSupportedKeySizes();
+
+    /**
+     * @param <A> Appendable generic type
+     * @param sb The {@link Appendable} instance to update
+     * @param encContext
+     * @return Same appendable instance
+     * @throws IOException
+     */
+    <A extends Appendable> A appendPrivateKeyEncryptionContext(A sb, PrivateKeyEncryptionContext encContext) throws IOException;
+
+    /**
+     * @param encContext The encryption context
+     * @return An initialization vector suitable to the specified context
+     * @throws GeneralSecurityException
+     */
+    byte[] generateInitializationVector(PrivateKeyEncryptionContext encContext) throws GeneralSecurityException;
+
+    /**
+     * @param bytes Original bytes
+     * @param encContext The encryption context
+     * @param encryptIt If {@code true} then encrypt the original bytes, otherwise decrypt them
+     * @return The result of applying the cipher to the original bytes
+     * @throws GeneralSecurityException If cannot encrypt/decrypt
+     */
+    byte[] applyPrivateKeyCipher(byte[] bytes, PrivateKeyEncryptionContext encContext, boolean encryptIt) throws GeneralSecurityException;
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHDSSPrivateKeyEntryDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHDSSPrivateKeyEntryDecoder.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHDSSPrivateKeyEntryDecoder.java
new file mode 100644
index 0000000..6188a04
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHDSSPrivateKeyEntryDecoder.java
@@ -0,0 +1,139 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config.keys.loader.openssh;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.KeyPairGenerator;
+import java.security.interfaces.DSAParams;
+import java.security.interfaces.DSAPrivateKey;
+import java.security.interfaces.DSAPublicKey;
+import java.security.spec.DSAPrivateKeySpec;
+import java.security.spec.DSAPublicKeySpec;
+import java.security.spec.InvalidKeySpecException;
+import java.util.Collections;
+import java.util.Objects;
+
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.KeyEntryResolver;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.config.keys.impl.AbstractPrivateKeyEntryDecoder;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class OpenSSHDSSPrivateKeyEntryDecoder extends AbstractPrivateKeyEntryDecoder<DSAPublicKey, DSAPrivateKey> {
+    public static final OpenSSHDSSPrivateKeyEntryDecoder INSTANCE = new OpenSSHDSSPrivateKeyEntryDecoder();
+
+    public OpenSSHDSSPrivateKeyEntryDecoder() {
+        super(DSAPublicKey.class, DSAPrivateKey.class, Collections.unmodifiableList(Collections.singletonList(KeyPairProvider.SSH_DSS)));
+    }
+
+    @Override
+    public DSAPrivateKey decodePrivateKey(String keyType, FilePasswordProvider passwordProvider, InputStream keyData)
+            throws IOException, GeneralSecurityException {
+        if (!KeyPairProvider.SSH_DSS.equals(keyType)) { // just in case we were invoked directly
+            throw new InvalidKeySpecException("Unexpected key type: " + keyType);
+        }
+
+        BigInteger p = KeyEntryResolver.decodeBigInt(keyData);
+        BigInteger q = KeyEntryResolver.decodeBigInt(keyData);
+        BigInteger g = KeyEntryResolver.decodeBigInt(keyData);
+        BigInteger y = KeyEntryResolver.decodeBigInt(keyData);
+        Objects.requireNonNull(y, "No public key data");   // TODO run some validation on it
+        BigInteger x = KeyEntryResolver.decodeBigInt(keyData);
+
+        return generatePrivateKey(new DSAPrivateKeySpec(x, p, q, g));
+    }
+
+    @Override
+    public String encodePrivateKey(OutputStream s, DSAPrivateKey key) throws IOException {
+        Objects.requireNonNull(key, "No private key provided");
+
+        DSAParams keyParams = Objects.requireNonNull(key.getParams(), "No DSA params available");
+        BigInteger p = keyParams.getP();
+        KeyEntryResolver.encodeBigInt(s, p);
+        KeyEntryResolver.encodeBigInt(s, keyParams.getQ());
+
+        BigInteger g = keyParams.getG();
+        KeyEntryResolver.encodeBigInt(s, g);
+
+        BigInteger x = key.getX();
+        BigInteger y = g.modPow(x, p);
+        KeyEntryResolver.encodeBigInt(s, y);
+        KeyEntryResolver.encodeBigInt(s, x);
+        return KeyPairProvider.SSH_DSS;
+    }
+
+    @Override
+    public boolean isPublicKeyRecoverySupported() {
+        return true;
+    }
+
+    @Override
+    public DSAPublicKey recoverPublicKey(DSAPrivateKey privateKey) throws GeneralSecurityException {
+        return KeyUtils.recoverDSAPublicKey(privateKey);
+    }
+
+    @Override
+    public DSAPublicKey clonePublicKey(DSAPublicKey key) throws GeneralSecurityException {
+        if (key == null) {
+            return null;
+        }
+
+        DSAParams params = key.getParams();
+        if (params == null) {
+            throw new InvalidKeyException("Missing parameters in key");
+        }
+
+        return generatePublicKey(new DSAPublicKeySpec(key.getY(), params.getP(), params.getQ(), params.getG()));
+    }
+
+    @Override
+    public DSAPrivateKey clonePrivateKey(DSAPrivateKey key) throws GeneralSecurityException {
+        if (key == null) {
+            return null;
+        }
+
+        DSAParams params = key.getParams();
+        if (params == null) {
+            throw new InvalidKeyException("Missing parameters in key");
+        }
+
+        return generatePrivateKey(new DSAPrivateKeySpec(key.getX(), params.getP(), params.getQ(), params.getG()));
+    }
+
+    @Override
+    public KeyPairGenerator getKeyPairGenerator() throws GeneralSecurityException {
+        return SecurityUtils.getKeyPairGenerator(KeyUtils.DSS_ALGORITHM);
+    }
+
+    @Override
+    public KeyFactory getKeyFactoryInstance() throws GeneralSecurityException {
+        return SecurityUtils.getKeyFactory(KeyUtils.DSS_ALGORITHM);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHECDSAPrivateKeyEntryDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHECDSAPrivateKeyEntryDecoder.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHECDSAPrivateKeyEntryDecoder.java
new file mode 100644
index 0000000..bceca59
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHECDSAPrivateKeyEntryDecoder.java
@@ -0,0 +1,164 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config.keys.loader.openssh;
+
+import java.io.IOException;
+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.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchProviderException;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPrivateKeySpec;
+import java.security.spec.ECPublicKeySpec;
+import java.security.spec.InvalidKeySpecException;
+import java.util.Objects;
+
+import org.apache.sshd.common.cipher.ECCurves;
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.KeyEntryResolver;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.config.keys.impl.AbstractPrivateKeyEntryDecoder;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class OpenSSHECDSAPrivateKeyEntryDecoder extends AbstractPrivateKeyEntryDecoder<ECPublicKey, ECPrivateKey> {
+    public static final OpenSSHECDSAPrivateKeyEntryDecoder INSTANCE = new OpenSSHECDSAPrivateKeyEntryDecoder();
+
+    public OpenSSHECDSAPrivateKeyEntryDecoder() {
+        super(ECPublicKey.class, ECPrivateKey.class, ECCurves.KEY_TYPES);
+    }
+
+    @Override
+    public ECPrivateKey decodePrivateKey(String keyType, FilePasswordProvider passwordProvider, InputStream keyData)
+            throws IOException, GeneralSecurityException {
+        ECCurves curve = ECCurves.fromKeyType(keyType);
+        if (curve == null) {
+            throw new InvalidKeySpecException("Not an EC curve name: " + keyType);
+        }
+
+        if (!SecurityUtils.isECCSupported()) {
+            throw new NoSuchProviderException("ECC not supported");
+        }
+
+        String keyCurveName = curve.getName();
+        // see rfc5656 section 3.1
+        String encCurveName = KeyEntryResolver.decodeString(keyData);
+        if (!keyCurveName.equals(encCurveName)) {
+            throw new InvalidKeySpecException("Mismatched key curve name (" + keyCurveName + ") vs. encoded one (" + encCurveName + ")");
+        }
+
+        byte[] pubKey = KeyEntryResolver.readRLEBytes(keyData);
+        Objects.requireNonNull(pubKey, "No public point");  // TODO validate it is a valid ECPoint
+        BigInteger s = KeyEntryResolver.decodeBigInt(keyData);
+        ECParameterSpec params = curve.getParameters();
+        return generatePrivateKey(new ECPrivateKeySpec(s, params));
+    }
+
+    @Override
+    public String encodePrivateKey(OutputStream s, ECPrivateKey key) throws IOException {
+        Objects.requireNonNull(key, "No private key provided");
+        return null;
+    }
+
+    @Override
+    public ECPublicKey recoverPublicKey(ECPrivateKey prvKey) throws GeneralSecurityException {
+        ECCurves curve = ECCurves.fromECKey(prvKey);
+        if (curve == null) {
+            throw new InvalidKeyException("Unknown curve");
+        }
+        // TODO see how we can figure out the public value
+        return super.recoverPublicKey(prvKey);
+    }
+
+    @Override
+    public ECPublicKey clonePublicKey(ECPublicKey key) throws GeneralSecurityException {
+        if (!SecurityUtils.isECCSupported()) {
+            throw new NoSuchProviderException("ECC not supported");
+        }
+
+        if (key == null) {
+            return null;
+        }
+
+        ECParameterSpec params = key.getParams();
+        if (params == null) {
+            throw new InvalidKeyException("Missing parameters in key");
+        }
+
+        return generatePublicKey(new ECPublicKeySpec(key.getW(), params));
+    }
+
+    @Override
+    public ECPrivateKey clonePrivateKey(ECPrivateKey key) throws GeneralSecurityException {
+        if (!SecurityUtils.isECCSupported()) {
+            throw new NoSuchProviderException("ECC not supported");
+        }
+
+        if (key == null) {
+            return null;
+        }
+
+        ECParameterSpec params = key.getParams();
+        if (params == null) {
+            throw new InvalidKeyException("Missing parameters in key");
+        }
+
+        return generatePrivateKey(new ECPrivateKeySpec(key.getS(), params));
+    }
+
+    @Override
+    public KeyFactory getKeyFactoryInstance() throws GeneralSecurityException {
+        if (SecurityUtils.isECCSupported()) {
+            return SecurityUtils.getKeyFactory(KeyUtils.EC_ALGORITHM);
+        } else {
+            throw new NoSuchProviderException("ECC not supported");
+        }
+    }
+
+    @Override
+    public KeyPair generateKeyPair(int keySize) throws GeneralSecurityException {
+        ECCurves curve = ECCurves.fromCurveSize(keySize);
+        if (curve == null) {
+            throw new InvalidKeySpecException("Unknown curve for key size=" + keySize);
+        }
+
+        KeyPairGenerator gen = getKeyPairGenerator();
+        gen.initialize(curve.getParameters());
+        return gen.generateKeyPair();
+    }
+
+    @Override
+    public KeyPairGenerator getKeyPairGenerator() throws GeneralSecurityException {
+        if (SecurityUtils.isECCSupported()) {
+            return SecurityUtils.getKeyPairGenerator(KeyUtils.EC_ALGORITHM);
+        } else {
+            throw new NoSuchProviderException("ECC not supported");
+        }
+    }
+}


[37/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common utilities code from sshd-core into sshd-common (new artifact)

Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/io/DirectoryScanner.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/DirectoryScanner.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/DirectoryScanner.java
new file mode 100644
index 0000000..8b4e9ba
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/DirectoryScanner.java
@@ -0,0 +1,380 @@
+/*
+ * 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.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.SelectorUtils;
+
+/**
+ * <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() {
+        super();
+    }
+
+    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 (GenericUtils.isEmpty(newfiles)) {
+            newfiles = GenericUtils.EMPTY_STRING_ARRAY;
+        }
+
+        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()];
+        return filesIncluded.toArray(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
+     */
+    @SuppressWarnings("PMD.AssignmentInOperand")
+    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;
+        for (int end = text.indexOf(repl, start); end != -1; end = text.indexOf(repl, start)) {
+            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/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/io/EmptyInputStream.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/EmptyInputStream.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/EmptyInputStream.java
new file mode 100644
index 0000000..8a1a68d
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/EmptyInputStream.java
@@ -0,0 +1,66 @@
+/*
+ * 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
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/io/FileInfoExtractor.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/FileInfoExtractor.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/FileInfoExtractor.java
new file mode 100644
index 0000000..feafd18
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/FileInfoExtractor.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.io;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.Path;
+import java.nio.file.attribute.FileTime;
+import java.nio.file.attribute.PosixFilePermission;
+import java.util.Set;
+
+/**
+ * @param <T> Type of information being extracted
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FunctionalInterface
+public interface FileInfoExtractor<T> {
+
+    FileInfoExtractor<Boolean> EXISTS = Files::exists;
+
+    FileInfoExtractor<Boolean> ISDIR = Files::isDirectory;
+
+    FileInfoExtractor<Boolean> ISREG = Files::isRegularFile;
+
+    FileInfoExtractor<Boolean> ISSYMLINK = (file, options) -> Files.isSymbolicLink(file);
+
+    FileInfoExtractor<Long> SIZE = (file, options) -> Files.size(file);
+
+    FileInfoExtractor<Set<PosixFilePermission>> PERMISSIONS = IoUtils::getPermissions;
+
+    FileInfoExtractor<FileTime> LASTMODIFIED = Files::getLastModifiedTime;
+
+    T infoOf(Path file, LinkOption... options) throws IOException;
+
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/io/InputStreamWithChannel.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/InputStreamWithChannel.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/InputStreamWithChannel.java
new file mode 100644
index 0000000..d847079
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/InputStreamWithChannel.java
@@ -0,0 +1,32 @@
+/*
+ * 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.InputStream;
+import java.nio.channels.Channel;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class InputStreamWithChannel extends InputStream implements Channel {
+    protected InputStreamWithChannel() {
+        super();
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/io/IoUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/IoUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/IoUtils.java
new file mode 100644
index 0000000..10aa59a
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/IoUtils.java
@@ -0,0 +1,556 @@
+/*
+ * 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.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.Closeable;
+import java.io.EOFException;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.CopyOption;
+import java.nio.file.FileSystem;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.OpenOption;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import java.nio.file.attribute.FileAttribute;
+import java.nio.file.attribute.PosixFilePermission;
+import java.nio.file.attribute.UserPrincipal;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.OsUtils;
+
+/**
+ * TODO Add javadoc
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class IoUtils {
+
+    public static final OpenOption[] EMPTY_OPEN_OPTIONS = new OpenOption[0];
+    public static final CopyOption[] EMPTY_COPY_OPTIONS = new CopyOption[0];
+    public static final LinkOption[] EMPTY_LINK_OPTIONS = new LinkOption[0];
+    public static final FileAttribute<?>[] EMPTY_FILE_ATTRIBUTES = new FileAttribute<?>[0];
+
+    public static final List<String> WINDOWS_EXECUTABLE_EXTENSIONS = Collections.unmodifiableList(Arrays.asList(".bat", ".exe", ".cmd"));
+
+    /**
+     * Size of preferred work buffer when reading / writing data to / from streams
+     */
+    public static final int DEFAULT_COPY_SIZE = 8192;
+
+    /**
+     * The local O/S line separator
+     */
+    public static final String EOL = System.lineSeparator();
+
+    /**
+     * A {@link Set} of {@link StandardOpenOption}-s that indicate an intent
+     * to create/modify a file
+     */
+    public static final Set<StandardOpenOption> WRITEABLE_OPEN_OPTIONS =
+        Collections.unmodifiableSet(
+            EnumSet.of(
+                StandardOpenOption.APPEND, StandardOpenOption.CREATE,
+                StandardOpenOption.CREATE_NEW, StandardOpenOption.DELETE_ON_CLOSE,
+                StandardOpenOption.DSYNC, StandardOpenOption.SYNC,
+                StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE));
+
+    private static final byte[] EOL_BYTES = EOL.getBytes(StandardCharsets.UTF_8);
+
+    private static final LinkOption[] NO_FOLLOW_OPTIONS = new LinkOption[]{LinkOption.NOFOLLOW_LINKS};
+
+    /**
+     * Private Constructor
+     */
+    private IoUtils() {
+        throw new UnsupportedOperationException("No instance allowed");
+    }
+
+    /**
+     * @return The local platform line separator bytes as UTF-8. <B>Note:</B>
+     * each call returns a <U>new</U> instance in order to avoid inadvertent
+     * changes in shared objects
+     * @see #EOL
+     */
+    public static byte[] getEOLBytes() {
+        return EOL_BYTES.clone();
+    }
+
+    public static LinkOption[] getLinkOptions(boolean followLinks) {
+        if (followLinks) {
+            return EMPTY_LINK_OPTIONS;
+        } else {    // return a clone that modifications to the array will not affect others
+            return NO_FOLLOW_OPTIONS.clone();
+        }
+    }
+
+    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];
+        for (int n = source.read(buf); n > 0; n = source.read(buf)) {
+            sink.write(buf, 0, n);
+            nread += n;
+        }
+
+        return nread;
+    }
+
+    /**
+     * Closes a bunch of resources suppressing any {@link IOException}s their
+     * {@link Closeable#close()} method may have thrown
+     *
+     * @param closeables The {@link Closeable}s to close
+     * @return The <U>first</U> {@link IOException} that occurred during closing
+     * of a resource - if more than one exception occurred, they are added as
+     * suppressed exceptions to the first one
+     * @see Throwable#getSuppressed()
+     */
+    @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
+    public static IOException closeQuietly(Closeable... closeables) {
+        IOException err = null;
+        for (Closeable c : closeables) {
+            try {
+                if (c != null) {
+                    c.close();
+                }
+            } catch (IOException e) {
+                err = GenericUtils.accumulateException(err, e);
+            }
+        }
+
+        return err;
+    }
+
+    /**
+     * @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}
+     * @param options The {@link LinkOption}s to use when querying the permissions
+     * @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, LinkOption... options) throws IOException {
+        FileSystem fs = path.getFileSystem();
+        Collection<String> views = fs.supportedFileAttributeViews();
+        if (views.contains("posix")) {
+            return Files.getPosixFilePermissions(path, options);
+        } 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 (isExecutable(f)) {
+            perms.add(PosixFilePermission.OWNER_EXECUTE);
+            perms.add(PosixFilePermission.GROUP_EXECUTE);
+            perms.add(PosixFilePermission.OTHERS_EXECUTE);
+        }
+
+        return perms;
+    }
+
+    public static boolean isExecutable(File f) {
+        if (f == null) {
+            return false;
+        }
+
+        if (OsUtils.isWin32()) {
+            return isWindowsExecutable(f.getName());
+        } else {
+            return f.canExecute();
+        }
+    }
+
+    /**
+     * 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>Get file owner.</P>
+     *
+     * @param path  The {@link Path}
+     * @param options The {@link LinkOption}s to use when querying the owner
+     * @return Owner of the file or null if unsupported. <B>Note:</B> for
+     * <I>Windows</I> it strips any prepended domain or group name
+     * @throws IOException If failed to access the file system
+     * @see Files#getOwner(Path, LinkOption...)
+     */
+    public static String getFileOwner(Path path, LinkOption... options) throws IOException {
+        try {
+            UserPrincipal principal = Files.getOwner(path, options);
+            String owner = (principal == null) ? null : principal.getName();
+            return OsUtils.getCanonicalUser(owner);
+        } catch (UnsupportedOperationException e) {
+            return null;
+        }
+    }
+
+    /**
+     * <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>
+     *
+     * <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 &ge; 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;
+    }
+
+    /**
+     * @param perms    The current {@link PosixFilePermission}s - ignored if {@code null}/empty
+     * @param excluded The permissions <U>not</U> allowed to exist - ignored if {@code null}/empty
+     * @return The violating {@link PosixFilePermission} - {@code null}
+     * if no violating permission found
+     */
+    public static PosixFilePermission validateExcludedPermissions(Collection<PosixFilePermission> perms, Collection<PosixFilePermission> excluded) {
+        if (GenericUtils.isEmpty(perms) || GenericUtils.isEmpty(excluded)) {
+            return null;
+        }
+
+        for (PosixFilePermission p : excluded) {
+            if (perms.contains(p)) {
+                return p;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * @param path    The {@link Path} to check
+     * @param options The {@link LinkOption}s to use when checking if path is a directory
+     * @return The same input path if it is a directory
+     * @throws UnsupportedOperationException if input path not a directory
+     */
+    public static Path ensureDirectory(Path path, LinkOption... options) {
+        if (!Files.isDirectory(path, options)) {
+            throw new UnsupportedOperationException("Not a directory: " + path);
+        }
+        return path;
+    }
+
+    /**
+     * @param options The {@link LinkOption}s - OK if {@code null}/empty
+     * @return {@code true} if the link options are {@code null}/empty or do
+     * not contain {@link LinkOption#NOFOLLOW_LINKS}, {@code false} otherwise
+     * (i.e., the array is not empty and contains the special value)
+     */
+    public static boolean followLinks(LinkOption... options) {
+        if (GenericUtils.isEmpty(options)) {
+            return true;
+        }
+
+        for (LinkOption localLinkOption : options) {
+            if (localLinkOption == LinkOption.NOFOLLOW_LINKS) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public static String appendPathComponent(String prefix, String component) {
+        if (GenericUtils.isEmpty(prefix)) {
+            return component;
+        }
+
+        if (GenericUtils.isEmpty(component)) {
+            return prefix;
+        }
+
+        StringBuilder sb = new StringBuilder(prefix.length() + component.length() + File.separator.length()).append(prefix);
+
+        if (sb.charAt(prefix.length() - 1) == File.separatorChar) {
+            if (component.charAt(0) == File.separatorChar) {
+                sb.append(component.substring(1));
+            } else {
+                sb.append(component);
+            }
+        } else {
+            if (component.charAt(0) != File.separatorChar) {
+                sb.append(File.separatorChar);
+            }
+            sb.append(component);
+        }
+
+        return sb.toString();
+    }
+
+    public static byte[] toByteArray(InputStream inStream) throws IOException {
+        try (ByteArrayOutputStream baos = new ByteArrayOutputStream(DEFAULT_COPY_SIZE)) {
+            copy(inStream, baos);
+            return baos.toByteArray();
+        }
+    }
+
+    /**
+     * Reads all lines until no more available
+     *
+     * @param url The {@link URL} to read from
+     * @return The {@link List} of lines in the same <U>order</U> as it was read
+     * @throws IOException If failed to read the lines
+     * @see #readAllLines(InputStream)
+     */
+    public static List<String> readAllLines(URL url) throws IOException {
+        try (InputStream stream = Objects.requireNonNull(url, "No URL").openStream()) {
+            return readAllLines(stream);
+        }
+    }
+
+    /**
+     * Reads all lines until no more available
+     *
+     * @param stream The {@link InputStream} - <B>Note:</B> assumed to
+     * contain {@code UTF-8} encoded data
+     * @return The {@link List} of lines in the same <U>order</U> as it was read
+     * @throws IOException If failed to read the lines
+     * @see #readAllLines(Reader)
+     */
+    public static List<String> readAllLines(InputStream stream) throws IOException {
+        try (Reader reader = new InputStreamReader(Objects.requireNonNull(stream, "No stream instance"), StandardCharsets.UTF_8)) {
+            return readAllLines(reader);
+        }
+    }
+
+    public static List<String> readAllLines(Reader reader) throws IOException {
+        try (BufferedReader br = new BufferedReader(Objects.requireNonNull(reader, "No reader instance"), DEFAULT_COPY_SIZE)) {
+            return readAllLines(br);
+        }
+    }
+
+    /**
+     * Reads all lines until no more available
+     *
+     * @param reader The {@link BufferedReader} to read all lines
+     * @return The {@link List} of lines in the same <U>order</U> as it was read
+     * @throws IOException If failed to read the lines
+     * @see #readAllLines(BufferedReader, int)
+     */
+    public static List<String> readAllLines(BufferedReader reader) throws IOException {
+        return readAllLines(reader, -1);
+    }
+
+    /**
+     * Reads all lines until no more available
+     *
+     * @param reader The {@link BufferedReader} to read all lines
+     * @param lineCountHint A hint as to the expected number of lines - non-positive
+     * means unknown - in which case some initial default value will be used to
+     * initialize the list used to accumulate the lines.
+     * @return The {@link List} of lines in the same <U>order</U> as it was read
+     * @throws IOException If failed to read the lines
+     */
+    public static List<String> readAllLines(BufferedReader reader, int lineCountHint) throws IOException {
+        List<String> result = new ArrayList<>(Math.max(lineCountHint, Short.SIZE));
+        for (String line = reader.readLine(); line != null; line = reader.readLine()) {
+            result.add(line);
+        }
+        return result;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/io/LimitInputStream.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/LimitInputStream.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/LimitInputStream.java
new file mode 100644
index 0000000..bbf956a
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/LimitInputStream.java
@@ -0,0 +1,113 @@
+/*
+ * 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.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.channels.Channel;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Reads from another {@link InputStream} up to specified max. length
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class LimitInputStream extends FilterInputStream implements Channel {
+    private final AtomicBoolean open = new AtomicBoolean(true);
+    private long remaining;
+
+    public LimitInputStream(InputStream in, long length) {
+        super(in);
+        remaining = length;
+    }
+
+    @Override
+    public boolean isOpen() {
+        return open.get();
+    }
+
+    @Override
+    public int read() throws IOException {
+        if (!isOpen()) {
+            throw new IOException("read() - stream is closed (remaining=" + remaining + ")");
+        }
+
+        if (remaining > 0) {
+            remaining--;
+            return super.read();
+        } else {
+            return -1;
+        }
+    }
+
+    @Override
+    public int read(byte[] b, int off, int len) throws IOException {
+        if (!isOpen()) {
+            throw new IOException("read(len=" + len + ") stream is closed (remaining=" + remaining + ")");
+        }
+
+        int nb = len;
+        if (nb > remaining) {
+            nb = (int) remaining;
+        }
+        if (nb > 0) {
+            int read = super.read(b, off, nb);
+            remaining -= read;
+            return read;
+        } else {
+            return -1;
+        }
+    }
+
+    @Override
+    public long skip(long n) throws IOException {
+        if (!isOpen()) {
+            throw new IOException("skip(" + n + ") stream is closed (remaining=" + remaining + ")");
+        }
+
+        long skipped = super.skip(n);
+        remaining -= skipped;
+        return skipped;
+    }
+
+    @Override
+    public int available() throws IOException {
+        if (!isOpen()) {
+            throw new IOException("available() stream is closed (remaining=" + remaining + ")");
+        }
+
+        int av = super.available();
+        if (av > remaining) {
+            return (int) remaining;
+        } else {
+            return av;
+        }
+    }
+
+    @Override
+    public void close() throws IOException {
+        // do not close the original input stream since it serves for ACK(s)
+        if (open.getAndSet(false)) {
+            //noinspection UnnecessaryReturnStatement
+            return; // debug breakpoint
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/io/LoggingFilterOutputStream.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/LoggingFilterOutputStream.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/LoggingFilterOutputStream.java
new file mode 100644
index 0000000..ff4dbb6
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/LoggingFilterOutputStream.java
@@ -0,0 +1,67 @@
+/*
+ * 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.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.sshd.common.PropertyResolver;
+import org.apache.sshd.common.util.buffer.BufferUtils;
+import org.apache.sshd.common.util.logging.LoggingUtils;
+import org.apache.sshd.common.util.logging.SimplifiedLog;
+import org.slf4j.Logger;
+
+/**
+ * Dumps everything that is written to the stream to the logger
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class LoggingFilterOutputStream extends FilterOutputStream {
+
+    private final String msg;
+    private final SimplifiedLog log;
+    private final int chunkSize;
+    private final AtomicInteger writeCount = new AtomicInteger(0);
+
+    public LoggingFilterOutputStream(OutputStream out, String msg, Logger log, PropertyResolver resolver) {
+        this(out, msg, log, resolver.getIntProperty(BufferUtils.HEXDUMP_CHUNK_SIZE, BufferUtils.DEFAULT_HEXDUMP_CHUNK_SIZE));
+    }
+
+    public LoggingFilterOutputStream(OutputStream out, String msg, Logger log, int chunkSize) {
+        super(out);
+        this.msg = msg;
+        this.log = LoggingUtils.wrap(log);
+        this.chunkSize = chunkSize;
+    }
+
+    @Override
+    public void write(int b) throws IOException {
+        byte[] d = new byte[1];
+        d[0] = (byte) b;
+        write(d, 0, 1);
+    }
+
+    @Override
+    public void write(byte[] b, int off, int len) throws IOException {
+        int count = writeCount.incrementAndGet();
+        BufferUtils.dumpHex(log, BufferUtils.DEFAULT_HEXDUMP_LEVEL, msg + "[" + count + "]", BufferUtils.DEFAULT_HEX_SEPARATOR, chunkSize, b, off, len);
+        out.write(b, off, len);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/io/ModifiableFileWatcher.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/ModifiableFileWatcher.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/ModifiableFileWatcher.java
new file mode 100644
index 0000000..032260b
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/ModifiableFileWatcher.java
@@ -0,0 +1,258 @@
+/*
+ * 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.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.Path;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.nio.file.attribute.FileTime;
+import java.nio.file.attribute.PosixFilePermission;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.OsUtils;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
+
+/**
+ * Watches over changes for a file and re-loads them if file has changed - including
+ * if file is deleted or (re-)created
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class ModifiableFileWatcher extends AbstractLoggingBean {
+
+    /**
+     * The {@link Set} of {@link PosixFilePermission} <U>not</U> allowed if strict
+     * permissions are enforced on key files
+     */
+    public static final Set<PosixFilePermission> STRICTLY_PROHIBITED_FILE_PERMISSION =
+            Collections.unmodifiableSet(
+                    EnumSet.of(PosixFilePermission.GROUP_WRITE, PosixFilePermission.OTHERS_WRITE));
+
+    protected final LinkOption[] options;
+
+    private final Path file;
+    private final AtomicBoolean lastExisted = new AtomicBoolean(false);
+    private final AtomicLong lastSize = new AtomicLong(Long.MIN_VALUE);
+    private final AtomicLong lastModified = new AtomicLong(-1L);
+
+    public ModifiableFileWatcher(File file) {
+        this(Objects.requireNonNull(file, "No file to watch").toPath());
+    }
+
+    public ModifiableFileWatcher(Path file) {
+        this(file, IoUtils.getLinkOptions(true));
+    }
+
+    public ModifiableFileWatcher(Path file, LinkOption... options) {
+        this.file = Objects.requireNonNull(file, "No path to watch");
+        // use a clone to avoid being sensitive to changes in the passed array
+        this.options = (options == null) ? IoUtils.EMPTY_LINK_OPTIONS : options.clone();
+    }
+
+    /**
+     * @return The watched {@link Path}
+     */
+    public final Path getPath() {
+        return file;
+    }
+
+    public final boolean exists() throws IOException {
+        return Files.exists(getPath(), options);
+    }
+
+    public final long size() throws IOException {
+        if (exists()) {
+            return Files.size(getPath());
+        } else {
+            return -1L;
+        }
+    }
+
+    public final FileTime lastModified() throws IOException {
+        if (exists()) {
+            BasicFileAttributes attrs = Files.readAttributes(getPath(), BasicFileAttributes.class, options);
+            return attrs.lastModifiedTime();
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * @return {@code true} if the watched file has probably been changed
+     * @throws IOException If failed to query file data
+     */
+    public boolean checkReloadRequired() throws IOException {
+        boolean exists = exists();
+        // if existence state changed from last time
+        if (exists != lastExisted.getAndSet(exists)) {
+            return true;
+        }
+
+        if (!exists) {
+            // file did not exist and still does not exist
+            resetReloadAttributes();
+            return false;
+        }
+
+        long size = size();
+        if (size < 0L) {
+            // means file no longer exists
+            resetReloadAttributes();
+            return true;
+        }
+
+        // if size changed then obviously need reload
+        if (size != lastSize.getAndSet(size)) {
+            return true;
+        }
+
+        FileTime modifiedTime = lastModified();
+        if (modifiedTime == null) {
+            // means file no longer exists
+            resetReloadAttributes();
+            return true;
+        }
+
+        long timestamp = modifiedTime.toMillis();
+        return timestamp != lastModified.getAndSet(timestamp);
+
+    }
+
+    /**
+     * Resets the state attributes used to detect changes to the initial
+     * construction values - i.e., file assumed not to exist and no known
+     * size of modify time
+     */
+    public void resetReloadAttributes() {
+        lastExisted.set(false);
+        lastSize.set(Long.MIN_VALUE);
+        lastModified.set(-1L);
+    }
+
+    /**
+     * May be called to refresh the state attributes used to detect changes
+     * e.g., file existence, size and last-modified time once re-loading is
+     * successfully completed. If the file does not exist then the attributes
+     * are reset to an &quot;unknown&quot; state.
+     *
+     * @throws IOException If failed to access the file (if exists)
+     * @see #resetReloadAttributes()
+     */
+    public void updateReloadAttributes() throws IOException {
+        if (exists()) {
+            long size = size();
+            FileTime modifiedTime = lastModified();
+
+            if ((size >= 0L) && (modifiedTime != null)) {
+                lastExisted.set(true);
+                lastSize.set(size);
+                lastModified.set(modifiedTime.toMillis());
+                return;
+            }
+        }
+
+        resetReloadAttributes();
+    }
+
+    @Override
+    public String toString() {
+        return Objects.toString(getPath());
+    }
+
+    /**
+     * <P>Checks if a path has strict permissions</P>
+     * <UL>
+     *
+     * <LI><P>
+     * (For {@code Unix}) The path may not have group or others write permissions
+     * </P></LI>
+     *
+     * <LI><P>
+     * The path must be owned by current user.
+     * </P></LI>
+     *
+     * <LI><P>
+     * (For {@code Unix}) The path may be owned by root.
+     * </P></LI>
+     *
+     * </UL>
+     *
+     * @param path    The {@link Path} to be checked - ignored if {@code null}
+     *                or does not exist
+     * @param options The {@link LinkOption}s to use to query the file's permissions
+     * @return The violated permission as {@link SimpleImmutableEntry} where key
+     * is a loggable message and value is the offending object
+     * - e.g., {@link PosixFilePermission} or {@link String} for owner. Return
+     * value is {@code null} if no violations detected
+     * @throws IOException If failed to retrieve the permissions
+     * @see #STRICTLY_PROHIBITED_FILE_PERMISSION
+     */
+    public static SimpleImmutableEntry<String, Object> validateStrictConfigFilePermissions(Path path, LinkOption... options) throws IOException {
+        if ((path == null) || (!Files.exists(path, options))) {
+            return null;
+        }
+
+        Collection<PosixFilePermission> perms = IoUtils.getPermissions(path, options);
+        if (GenericUtils.isEmpty(perms)) {
+            return null;
+        }
+
+        if (OsUtils.isUNIX()) {
+            PosixFilePermission p = IoUtils.validateExcludedPermissions(perms, STRICTLY_PROHIBITED_FILE_PERMISSION);
+            if (p != null) {
+                return new SimpleImmutableEntry<>(String.format("Permissions violation (%s)", p), p);
+            }
+        }
+
+        String owner = IoUtils.getFileOwner(path, options);
+        if (GenericUtils.isEmpty(owner)) {
+            // we cannot get owner
+            // general issue: jvm does not support permissions
+            // security issue: specific filesystem does not support permissions
+            return null;
+        }
+
+        String current = OsUtils.getCurrentUser();
+        Set<String> expected = new HashSet<>();
+        expected.add(current);
+        if (OsUtils.isUNIX()) {
+            // Windows "Administrator" was considered however in Windows most likely a group is used.
+            expected.add(OsUtils.ROOT_USER);
+        }
+
+        if (!expected.contains(owner)) {
+            return new SimpleImmutableEntry<>(String.format("Owner violation (%s)", owner), owner);
+        }
+
+        return null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseInputStream.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseInputStream.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseInputStream.java
new file mode 100644
index 0000000..b9aedee
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseInputStream.java
@@ -0,0 +1,47 @@
+/*
+ * 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.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * TODO Add javadoc
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class NoCloseInputStream extends FilterInputStream {
+    public NoCloseInputStream(InputStream in) {
+        super(in);
+    }
+
+    @Override
+    public void close() throws IOException {
+        // ignored
+    }
+
+    public static InputStream resolveInputStream(InputStream input, boolean okToClose) {
+        if ((input == null) || okToClose) {
+            return input;
+        } else {
+            return new NoCloseInputStream(input);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseOutputStream.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseOutputStream.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseOutputStream.java
new file mode 100644
index 0000000..4ba16d3
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseOutputStream.java
@@ -0,0 +1,47 @@
+/*
+ * 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.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * TODO Add javadoc
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class NoCloseOutputStream extends FilterOutputStream {
+    public NoCloseOutputStream(OutputStream out) {
+        super(out);
+    }
+
+    @Override
+    public void close() throws IOException {
+        // ignored
+    }
+
+    public static OutputStream resolveOutputStream(OutputStream output, boolean okToClose) {
+        if ((output == null) || okToClose) {
+            return output;
+        } else {
+            return new NoCloseOutputStream(output);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseReader.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseReader.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseReader.java
new file mode 100644
index 0000000..9c8b218
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseReader.java
@@ -0,0 +1,46 @@
+/*
+ * 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.FilterReader;
+import java.io.IOException;
+import java.io.Reader;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class NoCloseReader extends FilterReader {
+    public NoCloseReader(Reader in) {
+        super(in);
+    }
+
+    @Override
+    public void close() throws IOException {
+        // ignored
+    }
+
+    public static Reader resolveReader(Reader r, boolean okToClose) {
+        if ((r == null) || okToClose) {
+            return r;
+        } else {
+            return new NoCloseReader(r);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseWriter.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseWriter.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseWriter.java
new file mode 100644
index 0000000..0f92697
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseWriter.java
@@ -0,0 +1,46 @@
+/*
+ * 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.FilterWriter;
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class NoCloseWriter extends FilterWriter {
+    public NoCloseWriter(Writer out) {
+        super(out);
+    }
+
+    @Override
+    public void close() throws IOException {
+        // ignored
+    }
+
+    public static Writer resolveWriter(Writer r, boolean okToClose) {
+        if ((r == null) || okToClose) {
+            return r;
+        } else {
+            return new NoCloseWriter(r);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/io/NullInputStream.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/NullInputStream.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/NullInputStream.java
new file mode 100644
index 0000000..eb21383
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/NullInputStream.java
@@ -0,0 +1,90 @@
+/*
+ * 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.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.channels.Channel;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * A {@code /dev/null} input stream
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class NullInputStream extends InputStream implements Channel {
+    private final AtomicBoolean open = new AtomicBoolean(true);
+
+    public NullInputStream() {
+        super();
+    }
+
+    @Override
+    public boolean isOpen() {
+        return open.get();
+    }
+
+    @Override
+    public int read() throws IOException {
+        if (!isOpen()) {
+            throw new EOFException("Stream is closed for reading one value");
+        }
+        return -1;
+    }
+
+    @Override
+    public int read(byte[] b, int off, int len) throws IOException {
+        if (!isOpen()) {
+            throw new EOFException("Stream is closed for reading " + len + " bytes");
+        }
+        return -1;
+    }
+
+    @Override
+    public long skip(long n) throws IOException {
+        if (!isOpen()) {
+            throw new EOFException("Stream is closed for skipping " + n + " bytes");
+        }
+        return 0L;
+    }
+
+    @Override
+    public int available() throws IOException {
+        if (!isOpen()) {
+            throw new EOFException("Stream is closed for availability query");
+        }
+        return 0;
+    }
+
+    @Override
+    public synchronized void reset() throws IOException {
+        if (!isOpen()) {
+            throw new EOFException("Stream is closed for reset");
+        }
+    }
+
+    @Override
+    public void close() throws IOException {
+        if (open.getAndSet(false)) {
+            //noinspection UnnecessaryReturnStatement
+            return; // debug breakpoint
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/io/NullOutputStream.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/NullOutputStream.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/NullOutputStream.java
new file mode 100644
index 0000000..67fa2d0
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/NullOutputStream.java
@@ -0,0 +1,72 @@
+/*
+ * 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.EOFException;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.channels.Channel;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * A {code /dev/null} output stream
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class NullOutputStream extends OutputStream implements Channel {
+    private final AtomicBoolean open = new AtomicBoolean(true);
+
+    public NullOutputStream() {
+        super();
+    }
+
+    @Override
+    public boolean isOpen() {
+        return open.get();
+    }
+
+    @Override
+    public void write(int b) throws IOException {
+        if (!isOpen()) {
+            throw new EOFException("Stream is closed for writing one byte");
+        }
+    }
+
+    @Override
+    public void write(byte[] b, int off, int len) throws IOException {
+        if (!isOpen()) {
+            throw new EOFException("Stream is closed for writing " + len + " bytes");
+        }
+    }
+
+    @Override
+    public void flush() throws IOException {
+        if (!isOpen()) {
+            throw new EOFException("Stream is closed for flushing");
+        }
+    }
+
+    @Override
+    public void close() throws IOException {
+        if (open.getAndSet(false)) {
+            //noinspection UnnecessaryReturnStatement
+            return; // debug breakpoint
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/io/OutputStreamWithChannel.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/OutputStreamWithChannel.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/OutputStreamWithChannel.java
new file mode 100644
index 0000000..6f81872
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/OutputStreamWithChannel.java
@@ -0,0 +1,32 @@
+/*
+ * 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.OutputStream;
+import java.nio.channels.Channel;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class OutputStreamWithChannel extends OutputStream implements Channel {
+    protected OutputStreamWithChannel() {
+        super();
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/io/der/ASN1Class.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/der/ASN1Class.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/der/ASN1Class.java
new file mode 100644
index 0000000..b8351f1
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/der/ASN1Class.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util.io.der;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public enum ASN1Class {
+    // NOTE: order is crucial, so DON'T change it
+    UNIVERSAL((byte) 0x00),
+    APPLICATION((byte) 0x01),
+    CONTEXT((byte) 0x02),
+    PRIVATE((byte) 0x03);
+
+    public static final List<ASN1Class>  VALUES =
+            Collections.unmodifiableList(Arrays.asList(values()));
+
+    private final byte  byteValue;
+
+    ASN1Class(byte classValue) {
+        byteValue = classValue;
+    }
+
+    public byte getClassValue() {
+        return byteValue;
+    }
+
+    public static ASN1Class fromName(String s) {
+        if (GenericUtils.isEmpty(s)) {
+            return null;
+        }
+
+        for (ASN1Class c : VALUES) {
+            if (s.equalsIgnoreCase(c.name())) {
+                return c;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * <P>The first byte in DER encoding is made of following fields</P>
+     * <pre>
+     *-------------------------------------------------
+     *|Bit 8|Bit 7|Bit 6|Bit 5|Bit 4|Bit 3|Bit 2|Bit 1|
+     *-------------------------------------------------
+     *|  Class    | CF  |        Type                 |
+     *-------------------------------------------------
+     * </pre>
+     * @param value The original DER encoded byte
+     * @return The {@link ASN1Class} value - {@code null} if no match found
+     * @see #fromTypeValue(int)
+     */
+    public static ASN1Class fromDERValue(int value) {
+        return fromTypeValue((value >> 6) & 0x03);
+    }
+
+    /**
+     * @param value The &quot;pure&quot; value - unshifted and with no extras
+     * @return The {@link ASN1Class} value - {@code null} if no match found
+     */
+    public static ASN1Class fromTypeValue(int value) {
+        // all 4 values are defined
+        if ((value < 0) || (value >= VALUES.size())) {
+            return null;
+        }
+
+        return VALUES.get(value);
+    }
+}


[46/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common utilities code from sshd-core into sshd-common (new artifact)

Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/VersionProperties.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/VersionProperties.java b/sshd-common/src/main/java/org/apache/sshd/common/config/VersionProperties.java
new file mode 100644
index 0000000..0e351a8
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/VersionProperties.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config;
+
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.util.Collections;
+import java.util.NavigableMap;
+import java.util.Properties;
+import java.util.TreeMap;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.threads.ThreadUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class VersionProperties {
+    private static final class LazyVersionPropertiesHolder {
+        private static final NavigableMap<String, String> PROPERTIES =
+            Collections.unmodifiableNavigableMap(loadVersionProperties(LazyVersionPropertiesHolder.class));
+
+        private LazyVersionPropertiesHolder() {
+            throw new UnsupportedOperationException("No instance allowed");
+        }
+
+        private static NavigableMap<String, String> loadVersionProperties(Class<?> anchor) {
+            return loadVersionProperties(anchor, ThreadUtils.resolveDefaultClassLoader(anchor));
+        }
+
+        private static NavigableMap<String, String> loadVersionProperties(Class<?> anchor, ClassLoader loader) {
+            NavigableMap<String, String> result = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+            try {
+                InputStream input = loader.getResourceAsStream("org/apache/sshd/sshd-version.properties");
+                if (input == null) {
+                    throw new FileNotFoundException("Version resource does not exist");
+                }
+
+                Properties props = new Properties();
+                try {
+                    props.load(input);
+                } finally {
+                    input.close();
+                }
+
+                for (String key : props.stringPropertyNames()) {
+                    String propValue = props.getProperty(key);
+                    String value = GenericUtils.trimToEmpty(propValue);
+                    if (GenericUtils.isEmpty(value)) {
+                        continue;   // we have no need for empty values
+                    }
+
+                    String prev = result.put(key, value);
+                    if (prev != null) {
+                        Logger log = LoggerFactory.getLogger(anchor);
+                        log.warn("Multiple values for key=" + key + ": current=" + value + ", previous=" + prev);
+                    }
+                }
+            } catch (Exception e) {
+                Logger log = LoggerFactory.getLogger(anchor);
+                log.warn("Failed (" + e.getClass().getSimpleName() + ") to load version properties: " + e.getMessage());
+            }
+
+            return result;
+        }
+    }
+
+    private VersionProperties() {
+        throw new UnsupportedOperationException("No instance");
+    }
+
+    /**
+     * @return A case <u>insensitive</u> un-modifiable {@link NavigableMap} of the {@code sshd-version.properties} data
+     */
+    @SuppressWarnings("synthetic-access")
+    public static NavigableMap<String, String> getVersionProperties() {
+        return LazyVersionPropertiesHolder.PROPERTIES;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntry.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntry.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntry.java
new file mode 100644
index 0000000..c03616c
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntry.java
@@ -0,0 +1,480 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config.keys;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StreamCorruptedException;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.OpenOption;
+import java.nio.file.Path;
+import java.security.GeneralSecurityException;
+import java.security.PublicKey;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.NavigableMap;
+import java.util.TreeMap;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.io.NoCloseInputStream;
+import org.apache.sshd.common.util.io.NoCloseReader;
+
+/**
+ * Represents an entry in the user's {@code authorized_keys} file according
+ * to the <A HREF="http://en.wikibooks.org/wiki/OpenSSH/Client_Configuration_Files#.7E.2F.ssh.2Fauthorized_keys">OpenSSH format</A>.
+ * <B>Note:</B> {@code equals/hashCode} check only the key type and data - the
+ * comment and/or login options are not considered part of equality
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ * @see <A HREF="http://man.openbsd.org/sshd.8#AUTHORIZED_KEYS_FILE_FORMAT">sshd(8) - AUTHORIZED_KEYS_FILE_FORMAT</A>
+ */
+public class AuthorizedKeyEntry extends PublicKeyEntry {
+    public static final char BOOLEAN_OPTION_NEGATION_INDICATOR = '!';
+
+    private static final long serialVersionUID = -9007505285002809156L;
+
+    private String comment;
+    // for options that have no value, "true" is used
+    private Map<String, String> loginOptions = Collections.emptyMap();
+
+    public AuthorizedKeyEntry() {
+        super();
+    }
+
+    public String getComment() {
+        return comment;
+    }
+
+    public void setComment(String value) {
+        this.comment = value;
+    }
+
+    public Map<String, String> getLoginOptions() {
+        return loginOptions;
+    }
+
+    public void setLoginOptions(Map<String, String> value) {
+        if (value == null) {
+            this.loginOptions = Collections.emptyMap();
+        } else {
+            this.loginOptions = value;
+        }
+    }
+
+    @Override
+    public PublicKey appendPublicKey(Appendable sb, PublicKeyEntryResolver fallbackResolver) throws IOException, GeneralSecurityException {
+        Map<String, String> options = getLoginOptions();
+        if (!GenericUtils.isEmpty(options)) {
+            int index = 0;
+            // Cannot use forEach because the index value is not effectively final
+            for (Map.Entry<String, String> oe : options.entrySet()) {
+                String key = oe.getKey();
+                String value = oe.getValue();
+                if (index > 0) {
+                    sb.append(',');
+                }
+                sb.append(key);
+                // TODO figure out a way to remember which options where quoted
+                // TODO figure out a way to remember which options had no value
+                if (!Boolean.TRUE.toString().equals(value)) {
+                    sb.append('=').append(value);
+                }
+                index++;
+            }
+
+            if (index > 0) {
+                sb.append(' ');
+            }
+        }
+
+        PublicKey key = super.appendPublicKey(sb, fallbackResolver);
+        String kc = getComment();
+        if (!GenericUtils.isEmpty(kc)) {
+            sb.append(' ').append(kc);
+        }
+
+        return key;
+    }
+
+    @Override   // to avoid Findbugs[EQ_DOESNT_OVERRIDE_EQUALS]
+    public int hashCode() {
+        return super.hashCode();
+    }
+
+    @Override   // to avoid Findbugs[EQ_DOESNT_OVERRIDE_EQUALS]
+    public boolean equals(Object obj) {
+        return super.equals(obj);
+    }
+
+    @Override
+    public String toString() {
+        String entry = super.toString();
+        String kc = getComment();
+        Map<?, ?> ko = getLoginOptions();
+        return (GenericUtils.isEmpty(ko) ? "" : ko.toString() + " ")
+                + entry
+                + (GenericUtils.isEmpty(kc) ? "" : " " + kc);
+    }
+
+    public static List<PublicKey> resolveAuthorizedKeys(
+            PublicKeyEntryResolver fallbackResolver, Collection<? extends AuthorizedKeyEntry> entries)
+                    throws IOException, GeneralSecurityException {
+        if (GenericUtils.isEmpty(entries)) {
+            return Collections.emptyList();
+        }
+
+        List<PublicKey> keys = new ArrayList<>(entries.size());
+        for (AuthorizedKeyEntry e : entries) {
+            PublicKey k = e.resolvePublicKey(fallbackResolver);
+            if (k != null) {
+                keys.add(k);
+            }
+        }
+
+        return keys;
+    }
+
+    /**
+     * Reads read the contents of an <code>authorized_keys</code> file
+     *
+     * @param url The {@link URL} to read from
+     * @return A {@link List} of all the {@link AuthorizedKeyEntry}-ies found there
+     * @throws IOException If failed to read or parse the entries
+     * @see #readAuthorizedKeys(InputStream, boolean)
+     */
+    public static List<AuthorizedKeyEntry> readAuthorizedKeys(URL url) throws IOException {
+        try (InputStream in = url.openStream()) {
+            return readAuthorizedKeys(in, true);
+        }
+    }
+
+    /**
+     * Reads read the contents of an <code>authorized_keys</code> file
+     *
+     * @param file The {@link File} to read from
+     * @return A {@link List} of all the {@link AuthorizedKeyEntry}-ies found there
+     * @throws IOException If failed to read or parse the entries
+     * @see #readAuthorizedKeys(InputStream, boolean)
+     */
+    public static List<AuthorizedKeyEntry> readAuthorizedKeys(File file) throws IOException {
+        try (InputStream in = new FileInputStream(file)) {
+            return readAuthorizedKeys(in, true);
+        }
+    }
+
+    /**
+     * Reads read the contents of an <code>authorized_keys</code> file
+     *
+     * @param path    {@link Path} to read from
+     * @param options The {@link OpenOption}s to use - if unspecified then appropriate
+     *                defaults assumed
+     * @return A {@link List} of all the {@link AuthorizedKeyEntry}-ies found there
+     * @throws IOException If failed to read or parse the entries
+     * @see #readAuthorizedKeys(InputStream, boolean)
+     * @see Files#newInputStream(Path, OpenOption...)
+     */
+    public static List<AuthorizedKeyEntry> readAuthorizedKeys(Path path, OpenOption... options) throws IOException {
+        try (InputStream in = Files.newInputStream(path, options)) {
+            return readAuthorizedKeys(in, true);
+        }
+    }
+
+    /**
+     * Reads read the contents of an <code>authorized_keys</code> file
+     *
+     * @param filePath The file path to read from
+     * @return A {@link List} of all the {@link AuthorizedKeyEntry}-ies found there
+     * @throws IOException If failed to read or parse the entries
+     * @see #readAuthorizedKeys(InputStream, boolean)
+     */
+    public static List<AuthorizedKeyEntry> readAuthorizedKeys(String filePath) throws IOException {
+        try (InputStream in = new FileInputStream(filePath)) {
+            return readAuthorizedKeys(in, true);
+        }
+    }
+
+    /**
+     * Reads read the contents of an <code>authorized_keys</code> file
+     *
+     * @param in        The {@link InputStream}
+     * @param okToClose <code>true</code> if method may close the input stream
+     *                  regardless of whether successful or failed
+     * @return A {@link List} of all the {@link AuthorizedKeyEntry}-ies found there
+     * @throws IOException If failed to read or parse the entries
+     * @see #readAuthorizedKeys(Reader, boolean)
+     */
+    public static List<AuthorizedKeyEntry> readAuthorizedKeys(InputStream in, boolean okToClose) throws IOException {
+        try (Reader rdr = new InputStreamReader(NoCloseInputStream.resolveInputStream(in, okToClose), StandardCharsets.UTF_8)) {
+            return readAuthorizedKeys(rdr, true);
+        }
+    }
+
+    /**
+     * Reads read the contents of an <code>authorized_keys</code> file
+     *
+     * @param rdr       The {@link Reader}
+     * @param okToClose <code>true</code> if method may close the input stream
+     *                  regardless of whether successful or failed
+     * @return A {@link List} of all the {@link AuthorizedKeyEntry}-ies found there
+     * @throws IOException If failed to read or parse the entries
+     * @see #readAuthorizedKeys(BufferedReader)
+     */
+    public static List<AuthorizedKeyEntry> readAuthorizedKeys(Reader rdr, boolean okToClose) throws IOException {
+        try (BufferedReader buf = new BufferedReader(NoCloseReader.resolveReader(rdr, okToClose))) {
+            return readAuthorizedKeys(buf);
+        }
+    }
+
+    /**
+     * @param rdr The {@link BufferedReader} to use to read the contents of
+     *            an <code>authorized_keys</code> file
+     * @return A {@link List} of all the {@link AuthorizedKeyEntry}-ies found there
+     * @throws IOException If failed to read or parse the entries
+     * @see #parseAuthorizedKeyEntry(String)
+     */
+    public static List<AuthorizedKeyEntry> readAuthorizedKeys(BufferedReader rdr) throws IOException {
+        List<AuthorizedKeyEntry> entries = null;
+
+        for (String line = rdr.readLine(); line != null; line = rdr.readLine()) {
+            AuthorizedKeyEntry entry;
+            try {
+                entry = parseAuthorizedKeyEntry(line);
+                if (entry == null) {    // null, empty or comment line
+                    continue;
+                }
+            } catch (RuntimeException | Error e) {
+                throw new StreamCorruptedException("Failed (" + e.getClass().getSimpleName() + ")"
+                        + " to parse key entry=" + line + ": " + e.getMessage());
+            }
+
+            if (entries == null) {
+                entries = new ArrayList<>();
+            }
+
+            entries.add(entry);
+        }
+
+        if (entries == null) {
+            return Collections.emptyList();
+        } else {
+            return entries;
+        }
+    }
+
+    /**
+     * @param value Original line from an <code>authorized_keys</code> file
+     * @return {@link AuthorizedKeyEntry} or {@code null} if the line is
+     * {@code null}/empty or a comment line
+     * @throws IllegalArgumentException If failed to parse/decode the line
+     * @see #COMMENT_CHAR
+     */
+    public static AuthorizedKeyEntry parseAuthorizedKeyEntry(String value) throws IllegalArgumentException {
+        String line = GenericUtils.replaceWhitespaceAndTrim(value);
+        if (GenericUtils.isEmpty(line) || (line.charAt(0) == COMMENT_CHAR) /* comment ? */) {
+            return null;
+        }
+
+        int startPos = line.indexOf(' ');
+        if (startPos <= 0) {
+            throw new IllegalArgumentException("Bad format (no key data delimiter): " + line);
+        }
+
+        int endPos = line.indexOf(' ', startPos + 1);
+        if (endPos <= startPos) {
+            endPos = line.length();
+        }
+
+        String keyType = line.substring(0, startPos);
+        PublicKeyEntryDecoder<?, ?> decoder = KeyUtils.getPublicKeyEntryDecoder(keyType);
+        AuthorizedKeyEntry entry;
+        // assume this is due to the fact that it starts with login options
+        if (decoder == null) {
+            Map.Entry<String, String> comps = resolveEntryComponents(line);
+            entry = parseAuthorizedKeyEntry(comps.getValue());
+            ValidateUtils.checkTrue(entry != null, "Bad format (no key data after login options): %s", line);
+            entry.setLoginOptions(parseLoginOptions(comps.getKey()));
+        } else {
+            String encData = (endPos < (line.length() - 1)) ? line.substring(0, endPos).trim() : line;
+            String comment = (endPos < (line.length() - 1)) ? line.substring(endPos + 1).trim() : null;
+            entry = parsePublicKeyEntry(new AuthorizedKeyEntry(), encData);
+            entry.setComment(comment);
+        }
+
+        return entry;
+    }
+
+    /**
+     * Parses a single line from an <code>authorized_keys</code> file that is <U>known</U>
+     * to contain login options and separates it to the options and the rest of the line.
+     *
+     * @param entryLine The line to be parsed
+     * @return A {@link SimpleImmutableEntry} representing the parsed data where key=login options part
+     * and value=rest of the data - {@code null} if no data in line or line starts with comment character
+     * @see <A HREF="http://man.openbsd.org/sshd.8#AUTHORIZED_KEYS_FILE_FORMAT">sshd(8) - AUTHORIZED_KEYS_FILE_FORMAT</A>
+     */
+    public static SimpleImmutableEntry<String, String> resolveEntryComponents(String entryLine) {
+        String line = GenericUtils.replaceWhitespaceAndTrim(entryLine);
+        if (GenericUtils.isEmpty(line) || (line.charAt(0) == COMMENT_CHAR) /* comment ? */) {
+            return null;
+        }
+
+        for (int lastPos = 0; lastPos < line.length();) {
+            int startPos = line.indexOf(' ', lastPos);
+            if (startPos < lastPos) {
+                throw new IllegalArgumentException("Bad format (no key data delimiter): " + line);
+            }
+
+            int quotePos = line.indexOf('"', startPos + 1);
+            // If found quotes after the space then assume part of a login option
+            if (quotePos > startPos) {
+                lastPos = quotePos + 1;
+                continue;
+            }
+
+            String loginOptions = line.substring(0, startPos).trim();
+            String remainder = line.substring(startPos + 1).trim();
+            return new SimpleImmutableEntry<>(loginOptions, remainder);
+        }
+
+        throw new IllegalArgumentException("Bad format (no key data contents): " + line);
+    }
+
+    /**
+     * <P>
+     * Parses login options line according to
+     * <A HREF="http://man.openbsd.org/sshd.8#AUTHORIZED_KEYS_FILE_FORMAT">sshd(8) - AUTHORIZED_KEYS_FILE_FORMAT</A>
+     * guidelines. <B>Note:</B>
+     * </P>
+     *
+     * <UL>
+     *      <P><LI>
+     *      Options that have a value are automatically stripped of any surrounding double quotes./
+     *      </LI></P>
+     *
+     *      <P><LI>
+     *      Options that have no value are marked as {@code true/false} - according
+     *      to the {@link #BOOLEAN_OPTION_NEGATION_INDICATOR}.
+     *      </LI></P>
+     *
+     *      <P><LI>
+     *      Options that appear multiple times are simply concatenated using comma as separator.
+     *      </LI></P>
+     * </UL>
+     *
+     * @param options The options line to parse - ignored if {@code null}/empty/blank
+     * @return A {@link NavigableMap} where key=case <U>insensitive</U> option name and value=the parsed value.
+     * @see #addLoginOption(Map, String) addLoginOption
+     */
+    public static NavigableMap<String, String> parseLoginOptions(String options) {
+        String line = GenericUtils.replaceWhitespaceAndTrim(options);
+        int len = GenericUtils.length(line);
+        if (len <= 0) {
+            return Collections.emptyNavigableMap();
+        }
+
+        NavigableMap<String, String> optsMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+        int lastPos = 0;
+        for (int curPos = 0; curPos < len; curPos++) {
+            int nextPos = line.indexOf(',', curPos);
+            if (nextPos < curPos) {
+                break;
+            }
+
+            // check if "true" comma or one inside quotes
+            int quotePos = line.indexOf('"', curPos);
+            if ((quotePos >= lastPos) && (quotePos < nextPos)) {
+                nextPos = line.indexOf('"', quotePos + 1);
+                if (nextPos <= quotePos) {
+                    throw new IllegalArgumentException("Bad format (imbalanced quoted command): " + line);
+                }
+
+                // Make sure either comma or no more options follow the 2nd quote
+                for (nextPos++; nextPos < len; nextPos++) {
+                    char ch = line.charAt(nextPos);
+                    if (ch == ',') {
+                        break;
+                    }
+
+                    if (ch != ' ') {
+                        throw new IllegalArgumentException("Bad format (incorrect list format): " + line);
+                    }
+                }
+            }
+
+            addLoginOption(optsMap, line.substring(lastPos, nextPos));
+            lastPos = nextPos + 1;
+            curPos = lastPos;
+        }
+
+        // Any leftovers at end of line ?
+        if (lastPos < len) {
+            addLoginOption(optsMap, line.substring(lastPos));
+        }
+
+        return optsMap;
+    }
+
+    /**
+     * Parses and adds a new option to the options map. If a valued option is re-specified then
+     * its value(s) are concatenated using comma as separator.
+     *
+     * @param optsMap Options map to add to
+     * @param option The option data to parse - ignored if {@code null}/empty/blank
+     * @return The updated entry - {@code null} if no option updated in the map
+     * @throws IllegalStateException If a boolean option is re-specified
+     */
+    public static SimpleImmutableEntry<String, String> addLoginOption(Map<String, String> optsMap, String option) {
+        String p = GenericUtils.trimToEmpty(option);
+        if (GenericUtils.isEmpty(p)) {
+            return null;
+        }
+
+        int pos = p.indexOf('=');
+        String name = (pos < 0) ? p : GenericUtils.trimToEmpty(p.substring(0, pos));
+        CharSequence value = (pos < 0) ? null : GenericUtils.trimToEmpty(p.substring(pos + 1));
+        value = GenericUtils.stripQuotes(value);
+        if (value == null) {
+            value = Boolean.toString(name.charAt(0) != BOOLEAN_OPTION_NEGATION_INDICATOR);
+        }
+
+        SimpleImmutableEntry<String, String> entry = new SimpleImmutableEntry<>(name, value.toString());
+        String prev = optsMap.put(entry.getKey(), entry.getValue());
+        if (prev != null) {
+            if (pos < 0) {
+                throw new IllegalStateException("Bad format (boolean option (" + name + ") re-specified): " + p);
+            }
+            optsMap.put(entry.getKey(), prev + "," + entry.getValue());
+        }
+
+        return entry;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/BuiltinIdentities.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/BuiltinIdentities.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/BuiltinIdentities.java
new file mode 100644
index 0000000..70e5c8b
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/BuiltinIdentities.java
@@ -0,0 +1,212 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config.keys;
+
+import java.security.Key;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.interfaces.DSAPrivateKey;
+import java.security.interfaces.DSAPublicKey;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.Objects;
+import java.util.Set;
+
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public enum BuiltinIdentities implements Identity {
+    RSA(Constants.RSA, RSAPublicKey.class, RSAPrivateKey.class),
+    DSA(Constants.DSA, DSAPublicKey.class, DSAPrivateKey.class),
+    ECDSA(Constants.ECDSA, KeyUtils.EC_ALGORITHM, ECPublicKey.class, ECPrivateKey.class) {
+        @Override
+        public boolean isSupported() {
+            return SecurityUtils.isECCSupported();
+        }
+    },
+    ED25119(Constants.ED25519, SecurityUtils.EDDSA, SecurityUtils.getEDDSAPublicKeyType(), SecurityUtils.getEDDSAPrivateKeyType()) {
+        @Override
+        public boolean isSupported() {
+            return SecurityUtils.isEDDSACurveSupported();
+        }
+    };
+
+    public static final Set<BuiltinIdentities> VALUES =
+        Collections.unmodifiableSet(EnumSet.allOf(BuiltinIdentities.class));
+
+    public static final Set<String> NAMES =
+        Collections.unmodifiableSet(
+            GenericUtils.asSortedSet(
+                String.CASE_INSENSITIVE_ORDER, NamedResource.getNameList(VALUES)));
+
+    private final String name;
+    private final String algorithm;
+    private final Class<? extends PublicKey> pubType;
+    private final Class<? extends PrivateKey> prvType;
+
+    BuiltinIdentities(String type, Class<? extends PublicKey> pubType, Class<? extends PrivateKey> prvType) {
+        this(type, type, pubType, prvType);
+    }
+
+    BuiltinIdentities(String name, String algorithm, Class<? extends PublicKey> pubType, Class<? extends PrivateKey> prvType) {
+        this.name = name.toLowerCase();
+        this.algorithm = algorithm.toUpperCase();
+        this.pubType = pubType;
+        this.prvType = prvType;
+    }
+
+    @Override
+    public final String getName() {
+        return name;
+    }
+
+    @Override
+    public boolean isSupported() {
+        return true;
+    }
+
+    @Override
+    public String getAlgorithm() {
+        return algorithm;
+    }
+
+    @Override
+    public final Class<? extends PublicKey> getPublicKeyType() {
+        return pubType;
+    }
+
+    @Override
+    public final Class<? extends PrivateKey> getPrivateKeyType() {
+        return prvType;
+    }
+
+    /**
+     * @param name The identity name - ignored if {@code null}/empty
+     * @return The matching {@link BuiltinIdentities} whose {@link #getName()}
+     * value matches case <U>insensitive</U> or {@code null} if no match found
+     */
+    public static BuiltinIdentities fromName(String name) {
+        return NamedResource.findByName(name, String.CASE_INSENSITIVE_ORDER, VALUES);
+    }
+
+    /**
+     * @param algorithm The algorithm  - ignored if {@code null}/empty
+     * @return The matching {@link BuiltinIdentities} whose {@link #getAlgorithm()}
+     * value matches case <U>insensitive</U> or {@code null} if no match found
+     */
+    public static BuiltinIdentities fromAlgorithm(String algorithm) {
+        if (GenericUtils.isEmpty(algorithm)) {
+            return null;
+        }
+
+        for (BuiltinIdentities id : VALUES) {
+            if (algorithm.equalsIgnoreCase(id.getAlgorithm())) {
+                return id;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * @param kp The {@link KeyPair} - ignored if {@code null}
+     * @return The matching {@link BuiltinIdentities} provided <U>both</U>
+     * public and public keys are of the same type - {@code null} if no
+     * match could be found
+     * @see #fromKey(Key)
+     */
+    public static BuiltinIdentities fromKeyPair(KeyPair kp) {
+        if (kp == null) {
+            return null;
+        }
+
+        BuiltinIdentities i1 = fromKey(kp.getPublic());
+        BuiltinIdentities i2 = fromKey(kp.getPrivate());
+        if (Objects.equals(i1, i2)) {
+            return i1;
+        } else {
+            return null;    // some kind of mixed keys...
+        }
+    }
+
+    /**
+     * @param key The {@link Key} instance - ignored if {@code null}
+     * @return The matching {@link BuiltinIdentities} whose either public or
+     * private key type matches the requested one or {@code null} if no match found
+     * @see #fromKeyType(Class)
+     */
+    public static BuiltinIdentities fromKey(Key key) {
+        return fromKeyType((key == null) ? null : key.getClass());
+    }
+
+    /**
+     * @param clazz The key type - ignored if {@code null} or not
+     *              a {@link Key} class
+     * @return The matching {@link BuiltinIdentities} whose either public or
+     * private key type matches the requested one or {@code null} if no match found
+     * @see #getPublicKeyType()
+     * @see #getPrivateKeyType()
+     */
+    public static BuiltinIdentities fromKeyType(Class<?> clazz) {
+        if ((clazz == null) || (!Key.class.isAssignableFrom(clazz))) {
+            return null;
+        }
+
+        for (BuiltinIdentities id : VALUES) {
+            Class<?> pubType = id.getPublicKeyType();
+            Class<?> prvType = id.getPrivateKeyType();
+            // Ignore placeholder classes (e.g., if ed25519 is not supported)
+            if ((prvType == null) || (pubType == null)) {
+                continue;
+            }
+            if ((prvType == PrivateKey.class) || (pubType == PublicKey.class)) {
+                continue;
+            }
+            if (pubType.isAssignableFrom(clazz) || prvType.isAssignableFrom(clazz)) {
+                return id;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Contains the names of the identities
+     */
+    public static final class Constants {
+        public static final String RSA = KeyUtils.RSA_ALGORITHM;
+        public static final String DSA = KeyUtils.DSS_ALGORITHM;
+        public static final String ECDSA = "ECDSA";
+        public static final String ED25519 = "ED25519";
+
+        private Constants() {
+            throw new UnsupportedOperationException("No instance allowed");
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/FilePasswordProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/FilePasswordProvider.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/FilePasswordProvider.java
new file mode 100644
index 0000000..064f75c
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/FilePasswordProvider.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config.keys;
+
+import java.io.IOException;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FunctionalInterface
+public interface FilePasswordProvider {
+    /**
+     * An &quot;empty&quot; provider that returns {@code null} - i.e., unprotected key file
+     */
+    FilePasswordProvider EMPTY = resourceKey -> null;
+
+    /**
+     * @param resourceKey The resource key representing the <U>private</U>
+     *                    file
+     * @return The password - if {@code null}/empty then no password is required
+     * @throws IOException if cannot resolve password
+     */
+    String getPassword(String resourceKey) throws IOException;
+
+    static FilePasswordProvider of(String password) {
+        return r -> password;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/Identity.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/Identity.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/Identity.java
new file mode 100644
index 0000000..eaec413
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/Identity.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config.keys;
+
+import java.security.PrivateKey;
+import java.security.PublicKey;
+
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.OptionalFeature;
+
+/**
+ * Represents an SSH key type
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface Identity extends NamedResource, OptionalFeature {
+    /**
+     * @return The key algorithm - e.g., RSA, DSA, EC
+     */
+    String getAlgorithm();
+
+    Class<? extends PublicKey> getPublicKeyType();
+
+    Class<? extends PrivateKey> getPrivateKeyType();
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/IdentityResourceLoader.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/IdentityResourceLoader.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/IdentityResourceLoader.java
new file mode 100644
index 0000000..d826821
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/IdentityResourceLoader.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.config.keys;
+
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.util.Collection;
+
+/**
+ * @param <PUB> Type of {@link PublicKey}
+ * @param <PRV> Type of {@link PrivateKey}
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface IdentityResourceLoader<PUB extends PublicKey, PRV extends PrivateKey> {
+    /**
+     * @return The {@link Class} of the {@link PublicKey} that is the result
+     * of decoding
+     */
+    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
+     * are supported by this decoder - e.g., ECDSA keys have several curve names.
+     * <B>Caveat:</B> this collection may be un-modifiable...
+     */
+    Collection<String> getSupportedTypeNames();
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/IdentityUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/IdentityUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/IdentityUtils.java
new file mode 100644
index 0000000..fbc3ce7
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/IdentityUtils.java
@@ -0,0 +1,159 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config.keys;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.OpenOption;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.util.Collections;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.keyprovider.MappedKeyPairProvider;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class IdentityUtils {
+    private IdentityUtils() {
+        throw new UnsupportedOperationException("No instance");
+    }
+
+    private static final class LazyDefaultUserHomeFolderHolder {
+        private static final Path PATH =
+            Paths.get(ValidateUtils.checkNotNullAndNotEmpty(System.getProperty("user.home"), "No user home"))
+                .toAbsolutePath()
+                .normalize();
+
+        private LazyDefaultUserHomeFolderHolder() {
+            throw new UnsupportedOperationException("No instance allowed");
+        }
+    }
+
+    /**
+     * @return The {@link Path} to the currently running user home
+     */
+    @SuppressWarnings("synthetic-access")
+    public static Path getUserHomeFolder() {
+        return LazyDefaultUserHomeFolderHolder.PATH;
+    }
+
+    /**
+     * @param prefix The file name prefix - ignored if {@code null}/empty
+     * @param type   The identity type - ignored if {@code null}/empty
+     * @param suffix The file name suffix - ignored if {@code null}/empty
+     * @return The identity file name or {@code null} if no name
+     */
+    public static String getIdentityFileName(String prefix, String type, String suffix) {
+        if (GenericUtils.isEmpty(type)) {
+            return null;
+        } else {
+            return GenericUtils.trimToEmpty(prefix)
+                    + type.toLowerCase() + GenericUtils.trimToEmpty(suffix);
+        }
+    }
+
+    /**
+     * @param ids           A {@link Map} of the loaded identities where key=the identity type,
+     *                      value=the matching {@link KeyPair} - ignored if {@code null}/empty
+     * @param supportedOnly If {@code true} then ignore identities that are not
+     *                      supported internally
+     * @return A {@link KeyPair} for the identities - {@code null} if no identities
+     * available (e.g., after filtering unsupported ones)
+     * @see BuiltinIdentities
+     */
+    public static KeyPairProvider createKeyPairProvider(Map<String, KeyPair> ids, boolean supportedOnly) {
+        if (GenericUtils.isEmpty(ids)) {
+            return null;
+        }
+
+        Map<String, KeyPair> pairsMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+        ids.forEach((type, kp) -> {
+            BuiltinIdentities id = BuiltinIdentities.fromName(type);
+            if (id == null) {
+                id = BuiltinIdentities.fromKeyPair(kp);
+            }
+
+            if (supportedOnly && ((id == null) || (!id.isSupported()))) {
+                return;
+            }
+
+            String keyType = KeyUtils.getKeyType(kp);
+            if (GenericUtils.isEmpty(keyType)) {
+                return;
+            }
+
+            KeyPair prev = pairsMap.put(keyType, kp);
+            if (prev != null) {
+                return;   // less of an offense if 2 pairs mapped to same key type
+            }
+        });
+
+        if (GenericUtils.isEmpty(pairsMap)) {
+            return null;
+        } else {
+            return new MappedKeyPairProvider(pairsMap);
+        }
+    }
+
+    /**
+     * @param paths    A {@link Map} of the identities where key=identity type (case
+     *                 <U>insensitive</U>), value=the {@link Path} of file with the identity key
+     * @param provider A {@link FilePasswordProvider} - may be {@code null}
+     *                 if the loaded keys are <U>guaranteed</U> not to be encrypted. The argument
+     *                 to {@link FilePasswordProvider#getPassword(String)} is the path of the
+     *                 file whose key is to be loaded
+     * @param options  The {@link OpenOption}s to use when reading the key data
+     * @return A {@link Map} of the identities where key=identity type (case
+     * <U>insensitive</U>), value=the {@link KeyPair} of the identity
+     * @throws IOException              If failed to access the file system
+     * @throws GeneralSecurityException If failed to load the keys
+     * @see SecurityUtils#loadKeyPairIdentity(String, InputStream, FilePasswordProvider)
+     */
+    public static Map<String, KeyPair> loadIdentities(Map<String, ? extends Path> paths, FilePasswordProvider provider, OpenOption... options)
+            throws IOException, GeneralSecurityException {
+        if (GenericUtils.isEmpty(paths)) {
+            return Collections.emptyMap();
+        }
+
+        Map<String, KeyPair> ids = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+        // Cannot use forEach because the potential for IOExceptions being thrown
+        for (Map.Entry<String, ? extends Path> pe : paths.entrySet()) {
+            String type = pe.getKey();
+            Path path = pe.getValue();
+            try (InputStream inputStream = Files.newInputStream(path, options)) {
+                KeyPair kp = SecurityUtils.loadKeyPairIdentity(path.toString(), inputStream, provider);
+                KeyPair prev = ids.put(type, kp);
+                ValidateUtils.checkTrue(prev == null, "Multiple keys for type=%s", type);
+            }
+        }
+
+        return ids;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/KeyEntryResolver.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/KeyEntryResolver.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/KeyEntryResolver.java
new file mode 100644
index 0000000..4bfbea0
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/KeyEntryResolver.java
@@ -0,0 +1,190 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config.keys;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.math.BigInteger;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+
+import org.apache.sshd.common.util.io.IoUtils;
+
+/**
+ * @param <PUB> Type of {@link PublicKey}
+ * @param <PRV> Type of {@link PrivateKey}
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface KeyEntryResolver<PUB extends PublicKey, PRV extends PrivateKey> extends IdentityResourceLoader<PUB, PRV> {
+    /**
+     * @param keySize Key size in bits
+     * @return A {@link KeyPair} with the specified key size
+     * @throws GeneralSecurityException if unable to generate the pair
+     */
+    default KeyPair generateKeyPair(int keySize) throws GeneralSecurityException {
+        KeyPairGenerator gen = getKeyPairGenerator();
+        gen.initialize(keySize);
+        return gen.generateKeyPair();
+    }
+
+    /**
+     * @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()
+     */
+    default KeyPair cloneKeyPair(KeyPair kp) throws GeneralSecurityException {
+        if (kp == null) {
+            return null;
+        }
+
+        PUB pubCloned = null;
+        PublicKey pubOriginal = kp.getPublic();
+        Class<PUB> pubExpected = getPublicKeyType();
+        if (pubOriginal != null) {
+            Class<?> orgType = pubOriginal.getClass();
+            if (!pubExpected.isAssignableFrom(orgType)) {
+                throw new InvalidKeyException("Mismatched public key types: expected=" + pubExpected.getSimpleName() + ", actual=" + orgType.getSimpleName());
+            }
+
+            pubCloned = clonePublicKey(pubExpected.cast(pubOriginal));
+        }
+
+        PRV prvCloned = null;
+        PrivateKey prvOriginal = kp.getPrivate();
+        Class<PRV> prvExpected = getPrivateKeyType();
+        if (prvOriginal != null) {
+            Class<?> orgType = prvOriginal.getClass();
+            if (!prvExpected.isAssignableFrom(orgType)) {
+                throw new InvalidKeyException("Mismatched private key types: expected=" + prvExpected.getSimpleName() + ", actual=" + orgType.getSimpleName());
+            }
+
+            prvCloned = clonePrivateKey(prvExpected.cast(prvOriginal));
+        }
+
+        return new KeyPair(pubCloned, prvCloned);
+    }
+
+    /**
+     * @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;
+
+    /**
+     * @return A {@link KeyFactory} suitable for the specific decoder type
+     * @throws GeneralSecurityException If failed to create one
+     */
+    KeyFactory getKeyFactoryInstance() throws GeneralSecurityException;
+
+    static int encodeString(OutputStream s, String v) throws IOException {
+        return encodeString(s, v, StandardCharsets.UTF_8);
+    }
+
+    static int encodeString(OutputStream s, String v, String charset) throws IOException {
+        return encodeString(s, v, Charset.forName(charset));
+    }
+
+    static int encodeString(OutputStream s, String v, Charset cs) throws IOException {
+        return writeRLEBytes(s, v.getBytes(cs));
+    }
+
+    static int encodeBigInt(OutputStream s, BigInteger v) throws IOException {
+        return writeRLEBytes(s, v.toByteArray());
+    }
+
+    static int writeRLEBytes(OutputStream s, byte... bytes) throws IOException {
+        return writeRLEBytes(s, bytes, 0, bytes.length);
+    }
+
+    static int writeRLEBytes(OutputStream s, byte[] bytes, int off, int len) throws IOException {
+        byte[] lenBytes = encodeInt(s, len);
+        s.write(bytes, off, len);
+        return lenBytes.length + len;
+    }
+
+    static byte[] encodeInt(OutputStream s, int v) throws IOException {
+        byte[] bytes = {
+            (byte) ((v >> 24) & 0xFF),
+            (byte) ((v >> 16) & 0xFF),
+            (byte) ((v >> 8) & 0xFF),
+            (byte) (v & 0xFF)
+        };
+        s.write(bytes);
+        return bytes;
+    }
+
+    static String decodeString(InputStream s) throws IOException {
+        return decodeString(s, StandardCharsets.UTF_8);
+    }
+
+    static String decodeString(InputStream s, String charset) throws IOException {
+        return decodeString(s, Charset.forName(charset));
+    }
+
+    static String decodeString(InputStream s, Charset cs) throws IOException {
+        byte[] bytes = readRLEBytes(s);
+        return new String(bytes, cs);
+    }
+
+    static BigInteger decodeBigInt(InputStream s) throws IOException {
+        return new BigInteger(readRLEBytes(s));
+    }
+
+    static byte[] readRLEBytes(InputStream s) throws IOException {
+        int len = decodeInt(s);
+        byte[] bytes = new byte[len];
+        IoUtils.readFully(s, bytes);
+        return bytes;
+    }
+
+    static int decodeInt(InputStream s) throws IOException {
+        byte[] bytes = {0, 0, 0, 0};
+        IoUtils.readFully(s, bytes);
+        return ((bytes[0] & 0xFF) << 24)
+                | ((bytes[1] & 0xFF) << 16)
+                | ((bytes[2] & 0xFF) << 8)
+                | (bytes[3] & 0xFF);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/KeyRandomArt.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/KeyRandomArt.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/KeyRandomArt.java
new file mode 100644
index 0000000..b59dbd0
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/KeyRandomArt.java
@@ -0,0 +1,310 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config.keys;
+
+import java.io.IOException;
+import java.io.StreamCorruptedException;
+import java.security.KeyPair;
+import java.security.PublicKey;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Objects;
+
+import org.apache.sshd.common.Factory;
+import org.apache.sshd.common.digest.Digest;
+import org.apache.sshd.common.keyprovider.KeyIdentityProvider;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * Draw an ASCII-Art representing the fingerprint so human brain can
+ * profit from its built-in pattern recognition ability.
+ * This technique is called "random art" and can be found in some
+ * scientific publications like this original paper:
+ *
+ * &quot;Hash Visualization: a New Technique to improve Real-World Security&quot;,
+ * Perrig A. and Song D., 1999, International Workshop on Cryptographic
+ * Techniques and E-Commerce (CrypTEC '99)
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ * @see <a href="http://sparrow.ece.cmu.edu/~adrian/projects/validation/validation.pdf">Original article</a>
+ * @see <a href="http://opensource.apple.com/source/OpenSSH/OpenSSH-175/openssh/key.c">C implementation</a>
+ */
+public class KeyRandomArt {
+    public static final int FLDBASE = 8;
+    public static final int FLDSIZE_Y = FLDBASE + 1;
+    public static final int FLDSIZE_X = FLDBASE * 2 + 1;
+    public static final String AUGMENTATION_STRING = " .o+=*BOX@%&#/^SE";
+
+    private final String algorithm;
+    private final int keySize;
+    private final char[][] field = new char[FLDSIZE_X][FLDSIZE_Y];
+
+    public KeyRandomArt(PublicKey key) throws Exception {
+        this(key, KeyUtils.getDefaultFingerPrintFactory());
+    }
+
+    public KeyRandomArt(PublicKey key, Factory<? extends Digest> f) throws Exception {
+        this(key, Objects.requireNonNull(f, "No digest factory").create());
+    }
+
+    public KeyRandomArt(PublicKey key, Digest d) throws Exception {
+        this(Objects.requireNonNull(key, "No key provided").getAlgorithm(),
+             KeyUtils.getKeySize(key),
+             KeyUtils.getRawFingerprint(Objects.requireNonNull(d, "No key digest"), key));
+    }
+
+    /**
+     * @param algorithm The key algorithm
+     * @param keySize The key size in bits
+     * @param digest The key digest
+     */
+    public KeyRandomArt(String algorithm, int keySize, byte[] digest) {
+        this.algorithm = ValidateUtils.checkNotNullAndNotEmpty(algorithm, "No algorithm provided");
+        ValidateUtils.checkTrue(keySize > 0, "Invalid key size: %d", keySize);
+        this.keySize = keySize;
+        Objects.requireNonNull(digest, "No key digest provided");
+
+        int x = FLDSIZE_X / 2;
+        int y = FLDSIZE_Y / 2;
+        int len = AUGMENTATION_STRING.length() - 1;
+        for (int i = 0; i < digest.length; i++) {
+            /* each byte conveys four 2-bit move commands */
+            int input = digest[i] & 0xFF;
+            for (int b = 0; b < 4; b++) {
+                /* evaluate 2 bit, rest is shifted later */
+                x += ((input & 0x1) != 0) ? 1 : -1;
+                y += ((input & 0x2) != 0) ? 1 : -1;
+
+                /* assure we are still in bounds */
+                x = Math.max(x, 0);
+                y = Math.max(y, 0);
+                x = Math.min(x, FLDSIZE_X - 1);
+                y = Math.min(y, FLDSIZE_Y - 1);
+
+                /* augment the field */
+                if (field[x][y] < (len - 2)) {
+                    field[x][y]++;
+                }
+                input = input >> 2;
+            }
+        }
+
+        /* mark starting point and end point*/
+        field[FLDSIZE_X / 2][FLDSIZE_Y / 2] = (char) (len - 1);
+        field[x][y] = (char) len;
+    }
+
+    public String getAlgorithm() {
+        return algorithm;
+    }
+
+    public int getKeySize() {
+        return keySize;
+    }
+
+    /**
+     * Outputs the generated random art
+     *
+     * @param <A> The {@link Appendable} output writer
+     * @param sb The writer
+     * @return The updated writer instance
+     * @throws IOException If failed to write the combined result
+     */
+    public <A extends Appendable> A append(A sb) throws IOException {
+        // Upper border
+        String s = String.format("+--[%4s %4d]", getAlgorithm(), getKeySize());
+        sb.append(s);
+        for (int index = s.length(); index <= FLDSIZE_X; index++) {
+            sb.append('-');
+        }
+        sb.append('+');
+        sb.append('\n');
+
+        // contents
+        int len = AUGMENTATION_STRING.length() - 1;
+        for (int y = 0; y < FLDSIZE_Y; y++) {
+            sb.append('|');
+            for (int x = 0; x < FLDSIZE_X; x++) {
+                char ch = field[x][y];
+                sb.append(AUGMENTATION_STRING.charAt(Math.min(ch, len)));
+            }
+            sb.append('|');
+            sb.append('\n');
+        }
+
+        // lower border
+        sb.append('+');
+        for (int index = 0; index < FLDSIZE_X; index++) {
+            sb.append('-');
+        }
+
+        sb.append('+');
+        sb.append('\n');
+        return sb;
+    }
+
+    @Override
+    public String toString() {
+        try {
+            return append(new StringBuilder((FLDSIZE_X + 4) * (FLDSIZE_Y + 3))).toString();
+        } catch (IOException e) {
+            return e.getClass().getSimpleName();    // unexpected
+        }
+    }
+
+    /**
+     * Combines the arts in a user-friendly way so they are aligned with each other
+     *
+     * @param separator The separator to use between the arts - if empty char
+     * ('\0') then no separation is done
+     * @param arts The {@link KeyRandomArt}s to combine - ignored if {@code null}/empty
+     * @return The combined result
+     */
+    public static String combine(char separator, Collection<? extends KeyRandomArt> arts) {
+        if (GenericUtils.isEmpty(arts)) {
+            return "";
+        }
+
+        try {
+            return combine(new StringBuilder(arts.size() * (FLDSIZE_X + 4) * (FLDSIZE_Y + 3)), separator, arts).toString();
+        } catch (IOException e) {
+            return e.getClass().getSimpleName();    // unexpected
+        }
+    }
+
+    /**
+     * Creates the combined representation of the random art entries for the provided keys
+     *
+     * @param separator The separator to use between the arts - if empty char
+     * ('\0') then no separation is done
+     * @param provider The {@link KeyIdentityProvider} - ignored if {@code null}
+     * or has no keys to provide
+     * @return The combined representation
+     * @throws Exception If failed to extract or combine the entries
+     * @see #combine(Appendable, char, KeyIdentityProvider)
+     */
+    public static String combine(char separator, KeyIdentityProvider provider) throws Exception {
+        return combine(new StringBuilder(4 * (FLDSIZE_X + 4) * (FLDSIZE_Y + 3)), separator, provider).toString();
+    }
+
+    /**
+     * Appends the combined random art entries for the provided keys
+     *
+     * @param <A> The {@link Appendable} output writer
+     * @param sb The writer
+     * @param separator The separator to use between the arts - if empty char
+     * ('\0') then no separation is done
+     * @param provider The {@link KeyIdentityProvider} - ignored if {@code null}
+     * or has no keys to provide
+     * @return The updated writer instance
+     * @throws Exception If failed to extract or write the entries
+     * @see #generate(KeyIdentityProvider)
+     * @see #combine(Appendable, char, Collection)
+     */
+    public static <A extends Appendable> A combine(A sb, char separator, KeyIdentityProvider provider) throws Exception {
+        return combine(sb, separator, generate(provider));
+    }
+
+    /**
+     * Extracts and generates random art entries for all key in the provider
+     *
+     * @param provider The {@link KeyIdentityProvider} - ignored if {@code null}
+     * or has no keys to provide
+     * @return The extracted {@link KeyRandomArt}s
+     * @throws Exception If failed to extract the entries
+     * @see KeyIdentityProvider#loadKeys()
+     */
+    public static Collection<KeyRandomArt> generate(KeyIdentityProvider provider) throws Exception {
+        Iterable<KeyPair> keys = (provider == null) ? null : provider.loadKeys();
+        Iterator<KeyPair> iter = (keys == null) ? null : keys.iterator();
+        if ((iter == null) || (!iter.hasNext())) {
+            return Collections.emptyList();
+        }
+
+        Collection<KeyRandomArt> arts = new LinkedList<>();
+        do {
+            KeyPair kp = iter.next();
+            KeyRandomArt a = new KeyRandomArt(kp.getPublic());
+            arts.add(a);
+        } while (iter.hasNext());
+
+        return arts;
+    }
+
+    /**
+     * Combines the arts in a user-friendly way so they are aligned with each other
+     *
+     * @param <A> The {@link Appendable} output writer
+     * @param sb The writer
+     * @param separator The separator to use between the arts - if empty char
+     * ('\0') then no separation is done
+     * @param arts The {@link KeyRandomArt}s to combine - ignored if {@code null}/empty
+     * @return The updated writer instance
+     * @throws IOException If failed to write the combined result
+     */
+    public static <A extends Appendable> A combine(A sb, char separator, Collection<? extends KeyRandomArt> arts) throws IOException {
+        if (GenericUtils.isEmpty(arts)) {
+            return sb;
+        }
+
+        List<String[]> allLines = new ArrayList<>(arts.size());
+        int numLines = -1;
+        for (KeyRandomArt a : arts) {
+            String s = a.toString();
+            String[] lines = GenericUtils.split(s, '\n');
+            if (numLines <= 0) {
+                numLines = lines.length;
+            } else {
+                if (numLines != lines.length) {
+                    throw new StreamCorruptedException("Mismatched lines count: expected=" + numLines + ", actual=" + lines.length);
+                }
+            }
+
+            for (int index = 0; index < lines.length; index++) {
+                String l = lines[index];
+                if ((l.length() > 0) && (l.charAt(l.length() - 1) == '\r')) {
+                    l = l.substring(0, l.length() - 1);
+                    lines[index] = l;
+                }
+            }
+
+            allLines.add(lines);
+        }
+
+        for (int row = 0; row < numLines; row++) {
+            for (int index = 0; index < allLines.size(); index++) {
+                String[] lines = allLines.get(index);
+                String l = lines[row];
+                sb.append(l);
+                if ((index > 0) && (separator != '\0')) {
+                    sb.append(separator);
+                }
+            }
+            sb.append('\n');
+        }
+
+        return sb;
+    }
+}


[19/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common utilities code from sshd-core into sshd-common (new artifact)

Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/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
deleted file mode 100644
index f469583..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/digest/BuiltinDigests.java
+++ /dev/null
@@ -1,166 +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.digest;
-
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.Set;
-
-import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.NamedResource;
-import org.apache.sshd.common.util.GenericUtils;
-
-/**
- * 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 DigestFactory {
-    md5(Constants.MD5, "MD5", 16),
-    sha1(Constants.SHA1, "SHA-1", 20),
-    sha224(Constants.SHA224, "SHA-224", 28),
-    sha256(Constants.SHA256, "SHA-256", 32),
-    sha384(Constants.SHA384, "SHA-384", 48),
-    sha512(Constants.SHA512, "SHA-512", 64);
-
-    public static final Set<BuiltinDigests> VALUES =
-            Collections.unmodifiableSet(EnumSet.allOf(BuiltinDigests.class));
-
-    private final String algorithm;
-    private final int blockSize;
-    private final String factoryName;
-    private final boolean supported;
-
-    BuiltinDigests(String factoryName, String algorithm, int blockSize) {
-        this.factoryName = factoryName;
-        this.algorithm = algorithm;
-        this.blockSize = blockSize;
-        /*
-         * This can be done once since in order to change the support the JVM
-         * needs to be stopped, some unlimited-strength files need be installed
-         * and then the JVM re-started. Therefore, the answer is not going to
-         * change while the JVM is running
-         */
-        this.supported = DigestUtils.checkSupported(algorithm);
-    }
-
-    @Override
-    public final String getName() {
-        return factoryName;
-    }
-
-    @Override
-    public final String getAlgorithm() {
-        return algorithm;
-    }
-
-    @Override
-    public final int getBlockSize() {
-        return blockSize;
-    }
-
-    @Override
-    public final String toString() {
-        return getName();
-    }
-
-    @Override
-    public final Digest create() {
-        return new BaseDigest(getAlgorithm(), getBlockSize());
-    }
-
-    @Override
-    public final boolean isSupported() {
-        return supported;
-    }
-
-    /**
-     * @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
-     * (case <U>insensitive</U>) the provided argument - {@code null} if no match
-     */
-    public static BuiltinDigests fromString(String s) {
-        if (GenericUtils.isEmpty(s)) {
-            return null;
-        }
-
-        for (BuiltinDigests c : VALUES) {
-            if (s.equalsIgnoreCase(c.name())) {
-                return c;
-            }
-        }
-
-        return null;
-    }
-
-    /**
-     * @param factory The {@link org.apache.sshd.common.NamedFactory} for the cipher - ignored if {@code null}
-     * @return The matching {@link org.apache.sshd.common.digest.BuiltinDigests} whose factory name matches
-     * (case <U>insensitive</U>) the digest factory name
-     * @see #fromFactoryName(String)
-     */
-    public static BuiltinDigests fromFactory(NamedFactory<? extends Digest> factory) {
-        if (factory == null) {
-            return null;
-        } else {
-            return fromFactoryName(factory.getName());
-        }
-    }
-
-    /**
-     * @param name The factory name - ignored if {@code null}/empty
-     * @return The matching {@link org.apache.sshd.common.digest.BuiltinDigests} whose factory name matches
-     * (case <U>insensitive</U>) the provided name - {@code null} if no match
-     */
-    public static BuiltinDigests fromFactoryName(String name) {
-        return NamedResource.findByName(name, String.CASE_INSENSITIVE_ORDER, VALUES);
-    }
-
-    /**
-     * @param d The {@link Digest} instance - ignored if {@code null}
-     * @return The matching {@link org.apache.sshd.common.digest.BuiltinDigests} whose algorithm matches
-     * (case <U>insensitive</U>) the digets's algorithm - {@code null} if no match
-     */
-    public static BuiltinDigests fromDigest(Digest d) {
-        return fromAlgorithm((d == null) ? null : d.getAlgorithm());
-    }
-
-    /**
-     * @param algo The algorithm to find - ignored if {@code null}/empty
-     * @return The matching {@link org.apache.sshd.common.digest.BuiltinDigests} whose algorithm matches
-     * (case <U>insensitive</U>) the provided name - {@code null} if no match
-     */
-    public static BuiltinDigests fromAlgorithm(String algo) {
-        return DigestUtils.findFactoryByAlgorithm(algo, String.CASE_INSENSITIVE_ORDER, VALUES);
-    }
-
-    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";
-
-        private Constants() {
-            throw new UnsupportedOperationException("No instance allowed");
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/digest/Digest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/digest/Digest.java b/sshd-core/src/main/java/org/apache/sshd/common/digest/Digest.java
deleted file mode 100644
index 27204ff..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/digest/Digest.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sshd.common.digest;
-
-/**
- * Interface used to compute digests, based on algorithms such as MD5 or SHA1.
- * The digest implementation are compared first by the algorithm name (case
- * <U>insensitive</U> and second according to the block size
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface Digest extends DigestInformation, Comparable<Digest> {
-    void init() throws Exception;
-
-    void update(byte[] data) throws Exception;
-
-    void update(byte[] data, int start, int len) throws Exception;
-
-    byte[] digest() throws Exception;
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/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
deleted file mode 100644
index f00e7ba..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/digest/DigestFactory.java
+++ /dev/null
@@ -1,32 +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.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>
- */
-// 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/10de190e/sshd-core/src/main/java/org/apache/sshd/common/digest/DigestInformation.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/digest/DigestInformation.java b/sshd-core/src/main/java/org/apache/sshd/common/digest/DigestInformation.java
deleted file mode 100644
index ba181dc..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/digest/DigestInformation.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.common.digest;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface DigestInformation {
-    /**
-     * @return The digest algorithm name
-     */
-    String getAlgorithm();
-
-    /**
-     * @return The number of bytes in the digest's output
-     */
-    int getBlockSize();
-
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/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
deleted file mode 100644
index 30369ac..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/digest/DigestUtils.java
+++ /dev/null
@@ -1,228 +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.digest;
-
-import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
-import java.security.MessageDigest;
-import java.util.Base64;
-import java.util.Collection;
-import java.util.Comparator;
-import java.util.Objects;
-
-import org.apache.sshd.common.Factory;
-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;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public final class DigestUtils {
-    private DigestUtils() {
-        throw new UnsupportedOperationException("No instance");
-    }
-
-    /**
-     * @param algorithm The digest algorithm - never {@code null}/empty
-     * @return {@code true} if this digest algorithm is supported
-     * @see SecurityUtils#getMessageDigest(String)
-     */
-    public static boolean checkSupported(String algorithm) {
-        ValidateUtils.checkNotNullAndNotEmpty(algorithm, "No algorithm");
-        try {
-            MessageDigest digest = SecurityUtils.getMessageDigest(algorithm);
-            return digest != null;  // just in case
-        } catch (Exception e) {
-            return false;
-        }
-    }
-
-    /**
-     * @param <D> The generic type of digest factory
-     * @param algo The required algorithm name - ignored if {@code null}/empty
-     * @param comp The {@link Comparator} to use to compare algorithm names
-     * @param digests The factories to check - ignored if {@code null}/empty
-     * @return The first {@link DigestFactory} whose algorithm matches the required one
-     * according to the comparator - {@code null} if no match found
-     */
-    public static <D extends Digest> D findDigestByAlgorithm(String algo, Comparator<? super String> comp, Collection<? extends D> digests) {
-        if (GenericUtils.isEmpty(algo) || GenericUtils.isEmpty(digests)) {
-            return null;
-        }
-
-        for (D d : digests) {
-            if (comp.compare(algo, d.getAlgorithm()) == 0) {
-                return d;
-            }
-        }
-
-        return null;
-    }
-
-    /**
-     * @param <F> The generic type of digest factory
-     * @param algo The required algorithm name - ignored if {@code null}/empty
-     * @param comp The {@link Comparator} to use to compare algorithm names
-     * @param factories The factories to check - ignored if {@code null}/empty
-     * @return The first {@link DigestFactory} whose algorithm matches the required one
-     * according to the comparator - {@code null} if no match found
-     */
-    public static <F extends DigestFactory> F findFactoryByAlgorithm(String algo, Comparator<? super String> comp, Collection<? extends F> factories) {
-        if (GenericUtils.isEmpty(algo) || GenericUtils.isEmpty(factories)) {
-            return null;
-        }
-
-        for (F f : factories) {
-            if (comp.compare(algo, f.getAlgorithm()) == 0) {
-                return f;
-            }
-        }
-
-        return null;
-    }
-
-    /**
-     * @param f The {@link Factory} to create the {@link Digest} to use
-     * @param s The {@link String} to digest - ignored if {@code null}/empty,
-     *          otherwise its UTF-8 representation is used as input for the fingerprint
-     * @return The fingerprint - {@code null} if {@code null}/empty input
-     * @throws Exception If failed to calculate the digest
-     * @see #getFingerPrint(Digest, String, Charset)
-     */
-    public static String getFingerPrint(Factory<? extends Digest> f, String s) throws Exception {
-        return getFingerPrint(f, s, StandardCharsets.UTF_8);
-    }
-
-    /**
-     * @param f       The {@link Factory} to create the {@link Digest} to use
-     * @param s       The {@link String} to digest - ignored if {@code null}/empty
-     * @param charset The {@link Charset} to use in order to convert the
-     *                string to its byte representation to use as input for the fingerprint
-     * @return The fingerprint - {@code null} if {@code null}/empty input
-     * @throws Exception If failed to calculate the digest
-     */
-    public static String getFingerPrint(Factory<? extends Digest> f, String s, Charset charset) throws Exception {
-        return getFingerPrint(Objects.requireNonNull(f, "No factory").create(), s, charset);
-    }
-
-    /**
-     * @param d The {@link Digest} to use
-     * @param s The {@link String} to digest - ignored if {@code null}/empty,
-     *          otherwise its UTF-8 representation is used as input for the fingerprint
-     * @return The fingerprint - {@code null} if {@code null}/empty input
-     * @throws Exception If failed to calculate the digest
-     * @see #getFingerPrint(Digest, String, Charset)
-     */
-    public static String getFingerPrint(Digest d, String s) throws Exception {
-        return getFingerPrint(d, s, StandardCharsets.UTF_8);
-    }
-
-    /**
-     * @param d       The {@link Digest} to use
-     * @param s       The {@link String} to digest - ignored if {@code null}/empty
-     * @param charset The {@link Charset} to use in order to convert the
-     *                string to its byte representation to use as input for the fingerprint
-     * @return The fingerprint - {@code null} if {@code null}/empty input
-     * @throws Exception If failed to calculate the digest
-     */
-    public static String getFingerPrint(Digest d, String s, Charset charset) throws Exception {
-        if (GenericUtils.isEmpty(s)) {
-            return null;
-        } else {
-            return DigestUtils.getFingerPrint(d, s.getBytes(charset));
-        }
-    }
-
-    /**
-     * @param f   The {@link Factory} to create the {@link Digest} to use
-     * @param buf The data buffer to be fingerprint-ed
-     * @return The fingerprint - {@code null} if empty data buffer
-     * @throws Exception If failed to calculate the fingerprint
-     * @see #getFingerPrint(Factory, byte[], int, int)
-     */
-    public static String getFingerPrint(Factory<? extends Digest> f, byte... buf) throws Exception {
-        return getFingerPrint(f, buf, 0, NumberUtils.length(buf));
-    }
-
-    /**
-     * @param f      The {@link Factory} to create the {@link Digest} to use
-     * @param buf    The data buffer to be fingerprint-ed
-     * @param offset The offset of the data in the buffer
-     * @param len    The length of data - ignored if non-positive
-     * @return The fingerprint - {@code null} if non-positive length
-     * @throws Exception If failed to calculate the fingerprint
-     */
-    public static String getFingerPrint(Factory<? extends Digest> f, byte[] buf, int offset, int len) throws Exception {
-        return getFingerPrint(Objects.requireNonNull(f, "No factory").create(), buf, offset, len);
-    }
-
-    /**
-     * @param d   The {@link Digest} to use
-     * @param buf The data buffer to be fingerprint-ed
-     * @return The fingerprint - {@code null} if empty data buffer
-     * @throws Exception If failed to calculate the fingerprint
-     * @see #getFingerPrint(Digest, byte[], int, int)
-     */
-    public static String getFingerPrint(Digest d, byte... buf) throws Exception {
-        return getFingerPrint(d, buf, 0, NumberUtils.length(buf));
-    }
-
-    /**
-     * @param d      The {@link Digest} to use
-     * @param buf    The data buffer to be fingerprint-ed
-     * @param offset The offset of the data in the buffer
-     * @param len    The length of data - ignored if non-positive
-     * @return The fingerprint - {@code null} if non-positive length
-     * @throws Exception If failed to calculate the fingerprint
-     * @see #getRawFingerprint(Digest, byte[], int, int)
-     */
-    public static String getFingerPrint(Digest d, byte[] buf, int offset, int len) throws Exception {
-        if (len <= 0) {
-            return null;
-        }
-
-        byte[] data = getRawFingerprint(d, buf, offset, len);
-        String algo = d.getAlgorithm();
-        if (BuiltinDigests.md5.getAlgorithm().equals(algo)) {
-            return algo + ":" + BufferUtils.toHex(':', data).toLowerCase();
-        }
-
-        Base64.Encoder encoder = Base64.getEncoder();
-        return algo.replace("-", "").toUpperCase() + ":" + encoder.encodeToString(data).replaceAll("=", "");
-    }
-
-    public static byte[] getRawFingerprint(Digest d, byte... buf) throws Exception {
-        return getRawFingerprint(d, buf, 0, NumberUtils.length(buf));
-    }
-
-    public static byte[] getRawFingerprint(Digest d, byte[] buf, int offset, int len) throws Exception {
-        if (len <= 0) {
-            return null;
-        }
-
-        Objects.requireNonNull(d, "No digest").init();
-        d.update(buf, offset, len);
-
-        return d.digest();
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/digest/package.html
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/digest/package.html b/sshd-core/src/main/java/org/apache/sshd/common/digest/package.html
deleted file mode 100644
index 65940e0..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/digest/package.html
+++ /dev/null
@@ -1,25 +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.
--->
-<html>
-<head>
-</head>
-<body>
-
-<a href="{@docRoot}/org/apache/sshd/common/digest/Digest.html"><code>Digest</code></a> implementations.
-
-</body>
-</html>

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/future/AbstractSshFuture.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/future/AbstractSshFuture.java b/sshd-core/src/main/java/org/apache/sshd/common/future/AbstractSshFuture.java
deleted file mode 100644
index 1dca54c..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/future/AbstractSshFuture.java
+++ /dev/null
@@ -1,194 +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.future;
-
-import java.io.IOException;
-import java.io.InterruptedIOException;
-import java.io.StreamCorruptedException;
-import java.util.function.Function;
-
-import org.apache.sshd.common.SshException;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.logging.AbstractLoggingBean;
-
-/**
- * @param <T> Type of future
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class AbstractSshFuture<T extends SshFuture> extends AbstractLoggingBean implements SshFuture<T> {
-    /**
-     * A default value to indicate the future has been canceled
-     */
-    protected static final Object CANCELED = new Object();
-
-    protected final boolean debugEnabled;
-    protected final boolean traceEnabled;
-    private final Object id;
-
-    /**
-     * @param id Some identifier useful as {@link #toString()} value
-     */
-    protected AbstractSshFuture(Object id) {
-        this.id = id;
-        this.debugEnabled = log.isDebugEnabled();
-        this.traceEnabled = log.isTraceEnabled();
-    }
-
-    @Override
-    public Object getId() {
-        return id;
-    }
-
-    @Override
-    public boolean await(long timeoutMillis) throws IOException {
-        return await0(timeoutMillis, true) != null;
-    }
-
-    @Override
-    public boolean awaitUninterruptibly(long timeoutMillis) {
-        try {
-            return await0(timeoutMillis, false) != null;
-        } catch (InterruptedIOException e) {
-            throw formatExceptionMessage(msg -> new InternalError(msg, e),
-                    "Unexpected interrupted exception wile awaitUninterruptibly %d msec: %s",
-                    timeoutMillis, e.getMessage());
-        }
-    }
-
-    /**
-     * <P>Waits (interruptible) for the specified timeout (msec.) and then checks
-     * the result:</P>
-     * <UL>
-     *      <LI><P>
-     *      If result is {@code null} then timeout is assumed to have expired - throw
-     *      an appropriate {@link IOException}
-     *      </P></LI>
-     *
-     *      <LI><P>
-     *      If the result is of the expected type, then cast and return it
-     *      </P></LI>
-     *
-     *      <LI><P>
-     *      If the result is an {@link IOException} then re-throw it
-     *      </P></LI>
-     *
-     *      <LI><P>
-     *      If the result is a {@link Throwable} then throw an {@link IOException}
-     *      whose cause is the original exception
-     *      </P></LI>
-     *
-     *      <LI><P>
-     *      Otherwise (should never happen), throw a {@link StreamCorruptedException}
-     *      with the name of the result type
-     *      </P></LI>
-     * </UL>
-     *
-     * @param <R>          The generic result type
-     * @param expectedType The expected result type
-     * @param timeout      The timeout (millis) to wait for a result
-     * @return The (never {@code null}) result
-     * @throws IOException If failed to retrieve the expected result on time
-     */
-    protected <R> R verifyResult(Class<? extends R> expectedType, long timeout) throws IOException {
-        Object value = await0(timeout, true);
-        if (value == null) {
-            throw formatExceptionMessage(SshException::new, "Failed to get operation result within specified timeout: %s", timeout);
-        }
-
-        Class<?> actualType = value.getClass();
-        if (expectedType.isAssignableFrom(actualType)) {
-            return expectedType.cast(value);
-        }
-
-        if (Throwable.class.isAssignableFrom(actualType)) {
-            Throwable t = GenericUtils.peelException((Throwable) value);
-            if (t != value) {
-                value = t;
-                actualType = value.getClass();
-            }
-
-            if (IOException.class.isAssignableFrom(actualType)) {
-                throw (IOException) value;
-            }
-
-            Throwable cause = GenericUtils.resolveExceptionCause(t);
-            throw formatExceptionMessage(msg -> new SshException(msg, cause), "Failed (%s) to execute: %s", t.getClass().getSimpleName(), t.getMessage());
-        } else {    // what else can it be ????
-            throw formatExceptionMessage(StreamCorruptedException::new, "Unknown result type: %s", actualType.getName());
-        }
-    }
-
-    /**
-     * Wait for the Future to be ready. If the requested delay is 0 or
-     * negative, this method returns immediately.
-     *
-     * @param timeoutMillis The delay we will wait for the Future to be ready
-     * @param interruptable Tells if the wait can be interrupted or not.
-     * If {@code true} and the thread is interrupted then an {@link InterruptedIOException}
-     * is thrown.
-     * @return The non-{@code null} result object if the Future is ready,
-     * {@code null} if the timeout expired and no result was received
-     * @throws InterruptedIOException If the thread has been interrupted when it's not allowed.
-     */
-    protected abstract Object await0(long timeoutMillis, boolean interruptable) throws InterruptedIOException;
-
-    @SuppressWarnings("unchecked")
-    protected SshFutureListener<T> asListener(Object o) {
-        return (SshFutureListener<T>) o;
-    }
-
-    protected void notifyListener(SshFutureListener<T> l) {
-        try {
-            l.operationComplete(asT());
-        } catch (Throwable t) {
-            log.warn("notifyListener({}) failed ({}) to invoke {}: {}",
-                     this, t.getClass().getSimpleName(), l, t.getMessage());
-            if (debugEnabled) {
-                log.debug("notifyListener(" + this + ")[" + l + "] invocation failure details", t);
-            }
-        }
-    }
-
-    @SuppressWarnings("unchecked")
-    protected T asT() {
-        return (T) this;
-    }
-
-    /**
-     * Generates an exception whose message is prefixed by the future simple class name + {@link #getId() identifier}
-     * as a hint to the context of the failure.
-     *
-     * @param <E> Type of {@link Throwable} being generated
-     * @param exceptionCreator The exception creator from the formatted message
-     * @param format The extra payload format as per {@link String#format(String, Object...)}
-     * @param args The formatting arguments
-     * @return The generated exception
-     */
-    protected <E extends Throwable> E formatExceptionMessage(Function<? super String, ? extends E> exceptionCreator, String format, Object... args) {
-        String messagePayload = String.format(format, args);
-        String excMessage = getClass().getSimpleName() + "[" + getId() + "]: " + messagePayload;
-        return exceptionCreator.apply(excMessage);
-    }
-
-    @Override
-    public String toString() {
-        return getClass().getSimpleName() + "[id=" + getId() + "]";
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/future/CloseFuture.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/future/CloseFuture.java b/sshd-core/src/main/java/org/apache/sshd/common/future/CloseFuture.java
deleted file mode 100644
index 808716d..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/future/CloseFuture.java
+++ /dev/null
@@ -1,40 +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.future;
-
-/**
- * An {@link SshFuture} for asynchronous close requests.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface CloseFuture extends SshFuture<CloseFuture> {
-
-    /**
-     * @return <tt>true</tt> if the close request is finished and the target is closed.
-     */
-    boolean isClosed();
-
-    /**
-     * Marks this future as closed and notifies all threads waiting for this
-     * future.  This method is invoked by SSHD internally.  Please do not call
-     * this method directly.
-     */
-    void setClosed();
-
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/future/DefaultCloseFuture.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/future/DefaultCloseFuture.java b/sshd-core/src/main/java/org/apache/sshd/common/future/DefaultCloseFuture.java
deleted file mode 100644
index 4c34a06..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/future/DefaultCloseFuture.java
+++ /dev/null
@@ -1,52 +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.future;
-
-/**
- * A default implementation of {@link CloseFuture}.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class DefaultCloseFuture extends DefaultSshFuture<CloseFuture> implements CloseFuture {
-
-    /**
-     * Create a new instance
-     *
-     * @param id Some identifier useful as {@link #toString()} value
-     * @param lock A synchronization object for locking access - if {@code null}
-     * then synchronization occurs on {@code this} instance
-     */
-    public DefaultCloseFuture(Object id, Object lock) {
-        super(id, lock);
-    }
-
-    @Override
-    public boolean isClosed() {
-        if (isDone()) {
-            return (Boolean) getValue();
-        } else {
-            return false;
-        }
-    }
-
-    @Override
-    public void setClosed() {
-        setValue(Boolean.TRUE);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/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
deleted file mode 100644
index cd475ff..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/future/DefaultSshFuture.java
+++ /dev/null
@@ -1,236 +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.future;
-
-import java.io.InterruptedIOException;
-import java.lang.reflect.Array;
-import java.util.Objects;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-
-/**
- * A default implementation of {@link SshFuture}.
- *
- * @param <T> Type of future
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class DefaultSshFuture<T extends SshFuture> extends AbstractSshFuture<T> {
-    /**
-     * A lock used by the wait() method
-     */
-    private final Object lock;
-    private Object listeners;
-    private Object result;
-
-    /**
-     * Creates a new instance.
-     *
-     * @param id Some identifier useful as {@link #toString()} value
-     * @param lock A synchronization object for locking access - if {@code null}
-     * then synchronization occurs on {@code this} instance
-     */
-    public DefaultSshFuture(Object id, Object lock) {
-        super(id);
-
-        this.lock = (lock != null) ? lock : this;
-    }
-
-    @Override
-    protected Object await0(long timeoutMillis, boolean interruptable) throws InterruptedIOException {
-        ValidateUtils.checkTrue(timeoutMillis >= 0L, "Negative timeout N/A: %d", timeoutMillis);
-        long startTime = System.currentTimeMillis();
-        long curTime = startTime;
-        long endTime = ((Long.MAX_VALUE - timeoutMillis) < curTime) ? Long.MAX_VALUE : (curTime + timeoutMillis);
-
-        synchronized (lock) {
-            if ((result != null) || (timeoutMillis <= 0)) {
-                return result;
-            }
-
-            for (;;) {
-                try {
-                    lock.wait(endTime - curTime);
-                } catch (InterruptedException e) {
-                    if (interruptable) {
-                        curTime = System.currentTimeMillis();
-                        throw formatExceptionMessage(msg -> {
-                            InterruptedIOException exc = new InterruptedIOException(msg);
-                            exc.initCause(e);
-                            return exc;
-                        }, "Interrupted after %d msec.", curTime - startTime);
-                    }
-                }
-
-                curTime = System.currentTimeMillis();
-                if ((result != null) || (curTime >= endTime)) {
-                    return result;
-                }
-            }
-        }
-    }
-
-    @Override
-    public boolean isDone() {
-        synchronized (lock) {
-            return result != null;
-        }
-    }
-
-    /**
-     * Sets the result of the asynchronous operation, and mark it as finished.
-     *
-     * @param newValue The operation result
-     */
-    public void setValue(Object newValue) {
-        synchronized (lock) {
-            // Allow only once.
-            if (result != null) {
-                return;
-            }
-
-            result = (newValue != null) ? newValue : GenericUtils.NULL;
-            lock.notifyAll();
-        }
-
-        notifyListeners();
-    }
-
-    public int getNumRegisteredListeners() {
-        synchronized (lock) {
-            if (listeners == null) {
-                return 0;
-            } else if (listeners instanceof SshFutureListener) {
-                return 1;
-            } else {
-                int l = Array.getLength(listeners);
-                int count = 0;
-                for (int i = 0; i < l; i++) {
-                    if (Array.get(listeners, i) != null) {
-                        count++;
-                    }
-                }
-                return count;
-            }
-        }
-    }
-
-    /**
-     * @return The result of the asynchronous operation - or {@code null}
-     * if none set.
-     */
-    public Object getValue() {
-        synchronized (lock) {
-            return (result == GenericUtils.NULL) ? null : result;
-        }
-    }
-
-    @Override
-    public T addListener(SshFutureListener<T> listener) {
-        Objects.requireNonNull(listener, "Missing listener argument");
-        boolean notifyNow = false;
-        synchronized (lock) {
-            // if already have a result don't register the listener and invoke it directly
-            if (result != null) {
-                notifyNow = true;
-            } else if (listeners == null) {
-                listeners = listener;   // 1st listener ?
-            } else if (listeners instanceof SshFutureListener) {
-                listeners = new Object[]{listeners, listener};
-            } else {    // increase array of registered listeners
-                Object[] ol = (Object[]) listeners;
-                int l = ol.length;
-                Object[] nl = new Object[l + 1];
-                System.arraycopy(ol, 0, nl, 0, l);
-                nl[l] = listener;
-                listeners = nl;
-            }
-        }
-
-        if (notifyNow) {
-            notifyListener(listener);
-        }
-
-        return asT();
-    }
-
-    @Override
-    public T removeListener(SshFutureListener<T> listener) {
-        Objects.requireNonNull(listener, "No listener provided");
-
-        synchronized (lock) {
-            if (result != null) {
-                return asT();   // the train has already left the station...
-            }
-
-            if (listeners == null) {
-                return asT();   // no registered instances anyway
-            }
-
-            if (listeners == listener) {
-                listeners = null;   // the one and only
-            } else if (!(listeners instanceof SshFutureListener))  {
-                int l = Array.getLength(listeners);
-                for (int i = 0; i < l; i++) {
-                    if (Array.get(listeners, i) == listener) {
-                        Array.set(listeners, i, null);
-                        break;
-                    }
-                }
-            }
-        }
-
-        return asT();
-    }
-
-    protected void notifyListeners() {
-        /*
-         * There won't be any visibility problem or concurrent modification
-         * because result value is checked in both addListener and
-         * removeListener calls under lock. If the result is already set then
-         * both methods will not modify the internal listeners
-         */
-        if (listeners != null) {
-            if (listeners instanceof SshFutureListener) {
-                notifyListener(asListener(listeners));
-            } else {
-                int l = Array.getLength(listeners);
-                for (int i = 0; i < l; i++) {
-                    SshFutureListener<T> listener = asListener(Array.get(listeners, i));
-                    if (listener != null) {
-                        notifyListener(listener);
-                    }
-                }
-            }
-        }
-    }
-
-    public boolean isCanceled() {
-        return getValue() == CANCELED;
-    }
-
-    public void cancel() {
-        setValue(CANCELED);
-    }
-
-    @Override
-    public String toString() {
-        return super.toString() + "[value=" + result + "]";
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/future/DefaultVerifiableSshFuture.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/future/DefaultVerifiableSshFuture.java b/sshd-core/src/main/java/org/apache/sshd/common/future/DefaultVerifiableSshFuture.java
deleted file mode 100644
index 1b02fed..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/future/DefaultVerifiableSshFuture.java
+++ /dev/null
@@ -1,30 +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.future;
-
-/**
- * @param <T> Type of future
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class DefaultVerifiableSshFuture<T extends SshFuture> extends DefaultSshFuture<T> implements VerifiableFuture<T> {
-    protected DefaultVerifiableSshFuture(Object id, Object lock) {
-        super(id, lock);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/future/SshFuture.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/future/SshFuture.java b/sshd-core/src/main/java/org/apache/sshd/common/future/SshFuture.java
deleted file mode 100644
index b0d8e3c..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/future/SshFuture.java
+++ /dev/null
@@ -1,48 +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.future;
-
-/**
- * Represents the completion of an asynchronous SSH operation on a given object
- * (it may be an SSH session or an SSH channel).
- * Can be listened for completion using a {@link SshFutureListener}.
- *
- * @param <T> Type of future
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface SshFuture<T extends SshFuture> extends WaitableFuture {
-    /**
-     * Adds an event <tt>listener</tt> which is notified when
-     * this future is completed. If the listener is added
-     * after the completion, the listener is directly notified.
-     *
-     * @param listener The {@link SshFutureListener} instance to add
-     * @return The future instance
-     */
-    T addListener(SshFutureListener<T> listener);
-
-    /**
-     * Removes an existing event <tt>listener</tt> so it won't be notified when
-     * the future is completed.
-     *
-     * @param listener The {@link SshFutureListener} instance to remove
-     * @return The future instance
-     */
-    T removeListener(SshFutureListener<T> listener);
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/future/SshFutureListener.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/future/SshFutureListener.java b/sshd-core/src/main/java/org/apache/sshd/common/future/SshFutureListener.java
deleted file mode 100644
index a005c0f..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/future/SshFutureListener.java
+++ /dev/null
@@ -1,42 +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.future;
-
-import org.apache.sshd.common.util.SshdEventListener;
-
-/**
- * Something interested in being notified when the completion
- * of an asynchronous SSH operation : {@link SshFuture}.
- *
- * @param <T> type of future
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@SuppressWarnings("rawtypes")
-@FunctionalInterface
-public interface SshFutureListener<T extends SshFuture> extends SshdEventListener {
-
-    /**
-     * Invoked when the operation associated with the {@link SshFuture}
-     * has been completed even if you add the listener after the completion.
-     *
-     * @param future The source {@link SshFuture} which called this
-     *               callback.
-     */
-    void operationComplete(T future);
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/future/VerifiableFuture.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/future/VerifiableFuture.java b/sshd-core/src/main/java/org/apache/sshd/common/future/VerifiableFuture.java
deleted file mode 100644
index e318de7..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/future/VerifiableFuture.java
+++ /dev/null
@@ -1,68 +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.future;
-
-import java.io.IOException;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Represents an asynchronous operation whose successful result can be
- * verified somehow. The contract guarantees that if the {@code verifyXXX}
- * method returns without an exception then the operation was completed
- * <U>successfully</U>
- *
- * @param <T> Type of verification result
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FunctionalInterface
-public interface VerifiableFuture<T> {
-    /**
-     * Wait {@link Long#MAX_VALUE} msec. and verify that the operation was successful
-     *
-     * @return The (same) future instance
-     * @throws IOException If failed to verify successfully on time
-     * @see #verify(long)
-     */
-    default T verify() throws IOException {
-        return verify(Long.MAX_VALUE);
-    }
-
-    /**
-     * Wait and verify that the operation was successful
-     *
-     * @param timeout The number of time units to wait
-     * @param unit    The wait {@link TimeUnit}
-     * @return The (same) future instance
-     * @throws IOException If failed to verify successfully on time
-     * @see #verify(long)
-     */
-    default T verify(long timeout, TimeUnit unit) throws IOException {
-        return verify(unit.toMillis(timeout));
-    }
-
-    /**
-     * Wait and verify that the operation was successful
-     *
-     * @param timeoutMillis Wait timeout in milliseconds
-     * @return The (same) future instance
-     * @throws IOException If failed to verify successfully on time
-     */
-    T verify(long timeoutMillis) throws IOException;
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/future/WaitableFuture.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/future/WaitableFuture.java b/sshd-core/src/main/java/org/apache/sshd/common/future/WaitableFuture.java
deleted file mode 100644
index aff4adc..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/future/WaitableFuture.java
+++ /dev/null
@@ -1,118 +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.future;
-
-import java.io.IOException;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Represents an asynchronous operation which one can wait for its completion.
- * <B>Note:</B> the only thing guaranteed is that if {@code true} is returned
- * from one of the {@code awaitXXX} methods then the operation has completed.
- * However, the <B>caller</B> has to determine whether it was a successful or
- * failed completion.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface WaitableFuture {
-    /**
-     * @return Some identifier useful as {@link #toString()} value
-     */
-    Object getId();
-
-    /**
-     * Wait {@link Long#MAX_VALUE} msec. for the asynchronous operation to complete.
-     * The attached listeners will be notified when the operation is
-     * completed.
-     *
-     * @return {@code true} if the operation is completed.
-     * @throws IOException if failed - specifically {@link java.io.InterruptedIOException}
-     *                     if waiting was interrupted
-     * @see #await(long)
-     */
-    default boolean await() throws IOException {
-        return await(Long.MAX_VALUE);
-    }
-
-    /**
-     * Wait for the asynchronous operation to complete with the specified timeout.
-     *
-     * @param timeout   The number of time units to wait
-     * @param unit      The {@link TimeUnit} for waiting
-     * @return {@code true} if the operation is completed.
-     * @throws IOException if failed - specifically {@link java.io.InterruptedIOException}
-     *                     if waiting was interrupted
-     * @see #await(long)
-     */
-    default boolean await(long timeout, TimeUnit unit) throws IOException {
-        return await(unit.toMillis(timeout));
-    }
-
-    /**
-     * Wait for the asynchronous operation to complete with the specified timeout.
-     *
-     * @param timeoutMillis Wait time in milliseconds
-     * @return {@code true} if the operation is completed.
-     * @throws IOException if failed - specifically {@link java.io.InterruptedIOException}
-     *                     if waiting was interrupted
-     */
-    boolean await(long timeoutMillis) throws IOException;
-
-    /**
-     * Wait {@link Long#MAX_VALUE} msec. for the asynchronous operation to complete
-     * uninterruptibly. The attached listeners will be notified when the operation is
-     * completed.
-     *
-     * @return {@code true} if the operation is completed.
-     * @see #awaitUninterruptibly(long)
-     */
-    default boolean awaitUninterruptibly() {
-        return awaitUninterruptibly(Long.MAX_VALUE);
-    }
-
-    /**
-     * Wait for the asynchronous operation to complete with the specified timeout
-     * uninterruptibly.
-     *
-     * @param timeout   The number of time units to wait
-     * @param unit      The {@link TimeUnit} for waiting
-     * @return {@code true} if the operation is completed.
-     * @see #awaitUninterruptibly(long)
-     */
-    default boolean awaitUninterruptibly(long timeout, TimeUnit unit) {
-        return awaitUninterruptibly(unit.toMillis(timeout));
-    }
-
-    /**
-     * Wait for the asynchronous operation to complete with the specified timeout
-     * uninterruptibly.
-     *
-     * @param timeoutMillis Wait time in milliseconds
-     * @return {@code true} if the operation is finished.
-     */
-    boolean awaitUninterruptibly(long timeoutMillis);
-
-    /**
-     * @return {@code true} if the asynchronous operation is completed. <B>Note:</B>
-     * it is up to the <B>caller</B> to determine whether it was a successful or
-     * failed completion.
-     */
-    boolean isDone();
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/helpers/AbstractFactoryManager.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/helpers/AbstractFactoryManager.java b/sshd-core/src/main/java/org/apache/sshd/common/helpers/AbstractFactoryManager.java
index 7dd7f86..2342e45 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/helpers/AbstractFactoryManager.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/helpers/AbstractFactoryManager.java
@@ -29,7 +29,6 @@ import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.TimeUnit;
 
 import org.apache.sshd.agent.SshAgentFactory;
-import org.apache.sshd.common.AttributeStore;
 import org.apache.sshd.common.Factory;
 import org.apache.sshd.common.FactoryManager;
 import org.apache.sshd.common.NamedFactory;
@@ -165,11 +164,6 @@ public abstract class AbstractFactoryManager extends AbstractKexFactoryManager i
     }
 
     @Override
-    public <T> T resolveAttribute(AttributeKey<T> key) {
-        return AttributeStore.resolveAttribute(this, key);
-    }
-
-    @Override
     public PropertyResolver getParentPropertyResolver() {
         return parentResolver;
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/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
deleted file mode 100644
index 077d199..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/AbstractKeyPairProvider.java
+++ /dev/null
@@ -1,32 +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.keyprovider;
-
-import org.apache.sshd.common.util.logging.AbstractLoggingBean;
-
-/**
- * Provides a default implementation for some {@link KeyPairProvider} methods
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class AbstractKeyPairProvider extends AbstractLoggingBean implements KeyPairProvider {
-    protected AbstractKeyPairProvider() {
-        super();
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/AbstractResourceKeyPairProvider.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/AbstractResourceKeyPairProvider.java b/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/AbstractResourceKeyPairProvider.java
deleted file mode 100644
index e4e941d..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/AbstractResourceKeyPairProvider.java
+++ /dev/null
@@ -1,234 +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.keyprovider;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.security.PublicKey;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.NoSuchElementException;
-import java.util.Objects;
-import java.util.TreeMap;
-import java.util.TreeSet;
-
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * @param <R> Type of resource from which the {@link KeyPair} is generated
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class AbstractResourceKeyPairProvider<R> extends AbstractKeyPairProvider {
-    private FilePasswordProvider passwordFinder;
-    /*
-     * NOTE: the map is case insensitive even for Linux, as it is (very) bad
-     * practice to have 2 key files that differ from one another only in their
-     * case...
-     */
-    private final Map<String, KeyPair> cacheMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
-
-    protected AbstractResourceKeyPairProvider() {
-        super();
-    }
-
-    public FilePasswordProvider getPasswordFinder() {
-        return passwordFinder;
-    }
-
-    public void setPasswordFinder(FilePasswordProvider passwordFinder) {
-        this.passwordFinder = passwordFinder;
-    }
-
-    /**
-     * Checks which of the new resources we already loaded and can keep the
-     * associated key pair
-     *
-     * @param resources The collection of new resources - can be {@code null}/empty
-     * in which case the cache is cleared
-     */
-    protected void resetCacheMap(Collection<?> resources) {
-        // if have any cached pairs then see what we can keep from previous load
-        Collection<String> toDelete = Collections.emptySet();
-        synchronized (cacheMap) {
-            if (cacheMap.size() <= 0) {
-                return; // already empty - nothing to keep
-            }
-
-            if (GenericUtils.isEmpty(resources)) {
-                cacheMap.clear();
-                return;
-            }
-
-            for (Object r : resources) {
-                String resourceKey = ValidateUtils.checkNotNullAndNotEmpty(Objects.toString(r, null), "No resource key value");
-                if (cacheMap.containsKey(resourceKey)) {
-                    continue;
-                }
-
-                if (toDelete.isEmpty()) {
-                    toDelete = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
-                }
-
-                if (!toDelete.add(resourceKey)) {
-                    continue;   // debug breakpoint
-                }
-            }
-
-            if (GenericUtils.size(toDelete) > 0) {
-                toDelete.forEach(cacheMap::remove);
-            }
-        }
-
-        if (log.isDebugEnabled()) {
-            log.debug("resetCacheMap(" + resources + ") removed previous cached keys for " + toDelete);
-        }
-    }
-
-    protected Iterable<KeyPair> loadKeys(final Collection<? extends R> resources) {
-        if (GenericUtils.isEmpty(resources)) {
-            return Collections.emptyList();
-        } else {
-            return () -> new KeyPairIterator(resources);
-        }
-    }
-
-    protected KeyPair doLoadKey(R resource) throws IOException, GeneralSecurityException {
-        String resourceKey = ValidateUtils.checkNotNullAndNotEmpty(Objects.toString(resource, null), "No resource string value");
-        KeyPair kp;
-        synchronized (cacheMap) {
-            // check if lucky enough to have already loaded this file
-            kp = cacheMap.get(resourceKey);
-        }
-
-        if (kp != null) {
-            if (log.isTraceEnabled()) {
-                PublicKey key = kp.getPublic();
-                log.trace("doLoadKey({}) use cached key {}-{}",
-                          resourceKey, KeyUtils.getKeyType(key), KeyUtils.getFingerPrint(key));
-            }
-            return kp;
-        }
-
-        kp = doLoadKey(resourceKey, resource, getPasswordFinder());
-        if (kp != null) {
-            boolean reusedKey;
-            synchronized (cacheMap) {
-                // if somebody else beat us to it, use the cached key - just in case file contents changed
-                reusedKey = cacheMap.containsKey(resourceKey);
-                if (reusedKey) {
-                    kp = cacheMap.get(resourceKey);
-                } else {
-                    cacheMap.put(resourceKey, kp);
-                }
-            }
-
-            if (log.isDebugEnabled()) {
-                PublicKey key = kp.getPublic();
-                log.debug("doLoadKey({}) {} {}-{}",
-                          resourceKey, reusedKey ? "re-loaded" : "loaded",
-                          KeyUtils.getKeyType(key), KeyUtils.getFingerPrint(key));
-            }
-        } else {
-            if (log.isDebugEnabled()) {
-                log.debug("doLoadKey({}) no key loaded", resourceKey);
-            }
-        }
-
-        return kp;
-    }
-
-    protected KeyPair doLoadKey(String resourceKey, R resource, FilePasswordProvider provider) throws IOException, GeneralSecurityException {
-        try (InputStream inputStream = openKeyPairResource(resourceKey, resource)) {
-            return doLoadKey(resourceKey, inputStream, provider);
-        }
-    }
-
-    protected abstract InputStream openKeyPairResource(String resourceKey, R resource) throws IOException;
-
-    protected KeyPair doLoadKey(String resourceKey, InputStream inputStream, FilePasswordProvider provider)
-            throws IOException, GeneralSecurityException {
-        return SecurityUtils.loadKeyPairIdentity(resourceKey, inputStream, provider);
-    }
-
-    protected class KeyPairIterator implements Iterator<KeyPair> {
-        private final Iterator<? extends R> iterator;
-        private KeyPair nextKeyPair;
-        private boolean nextKeyPairSet;
-
-        protected KeyPairIterator(Collection<? extends R> resources) {
-            iterator = resources.iterator();
-        }
-
-        @Override
-        public boolean hasNext() {
-            return nextKeyPairSet || setNextObject();
-        }
-
-        @Override
-        public KeyPair next() {
-            if (!nextKeyPairSet) {
-                if (!setNextObject()) {
-                    throw new NoSuchElementException("Out of files to try");
-                }
-            }
-            nextKeyPairSet = false;
-            return nextKeyPair;
-        }
-
-        @Override
-        public void remove() {
-            throw new UnsupportedOperationException("loadKeys(files) Iterator#remove() N/A");
-        }
-
-        @SuppressWarnings("synthetic-access")
-        private boolean setNextObject() {
-            boolean debugEnabled = log.isDebugEnabled();
-            while (iterator.hasNext()) {
-                R r = iterator.next();
-                try {
-                    nextKeyPair = doLoadKey(r);
-                } catch (Throwable e) {
-                    log.warn("Failed (" + e.getClass().getSimpleName() + ")"
-                           + " to load key resource=" + r + ": " + e.getMessage());
-                    if (debugEnabled) {
-                        log.debug("Key resource=" + r + " load failure details", e);
-                    }
-                    nextKeyPair = null;
-                    continue;
-                }
-
-                if (nextKeyPair != null) {
-                    nextKeyPairSet = true;
-                    return true;
-                }
-            }
-
-            return false;
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/ClassLoadableResourceKeyPairProvider.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/ClassLoadableResourceKeyPairProvider.java b/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/ClassLoadableResourceKeyPairProvider.java
deleted file mode 100644
index 6e8da54..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/ClassLoadableResourceKeyPairProvider.java
+++ /dev/null
@@ -1,113 +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.keyprovider;
-
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.StreamCorruptedException;
-import java.security.KeyPair;
-import java.util.Collection;
-import java.util.Collections;
-
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.threads.ThreadUtils;
-
-/**
- * This provider loads private keys from the specified resources that
- * are accessible via {@link ClassLoader#getResourceAsStream(String)}.
- * If no loader configured via {@link #setResourceLoader(ClassLoader)}, then
- * {@link ThreadUtils#resolveDefaultClassLoader(Class)} is used
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class ClassLoadableResourceKeyPairProvider extends AbstractResourceKeyPairProvider<String> {
-    private ClassLoader classLoader;
-    private Collection<String> resources;
-
-    public ClassLoadableResourceKeyPairProvider() {
-        this(Collections.emptyList());
-    }
-
-    public ClassLoadableResourceKeyPairProvider(ClassLoader cl) {
-        this(cl, Collections.emptyList());
-    }
-
-    public ClassLoadableResourceKeyPairProvider(String res) {
-        this(Collections.singletonList(ValidateUtils.checkNotNullAndNotEmpty(res, "No resource specified")));
-    }
-
-    public ClassLoadableResourceKeyPairProvider(ClassLoader cl, String res) {
-        this(cl, Collections.singletonList(ValidateUtils.checkNotNullAndNotEmpty(res, "No resource specified")));
-    }
-
-    public ClassLoadableResourceKeyPairProvider(Collection<String> resources) {
-        this.classLoader = ThreadUtils.resolveDefaultClassLoader(getClass());
-        this.resources = (resources == null) ? Collections.emptyList() : resources;
-    }
-
-    public ClassLoadableResourceKeyPairProvider(ClassLoader cl, Collection<String> resources) {
-        this.classLoader = cl;
-        this.resources = (resources == null) ? Collections.emptyList() : resources;
-    }
-
-    public Collection<String> getResources() {
-        return resources;
-    }
-
-    public void setResources(Collection<String> resources) {
-        this.resources = (resources == null) ? Collections.emptyList() : resources;
-    }
-
-    public ClassLoader getResourceLoader() {
-        return classLoader;
-    }
-
-    public void setResourceLoader(ClassLoader classLoader) {
-        this.classLoader = classLoader;
-    }
-
-    @Override
-    public Iterable<KeyPair> loadKeys() {
-        return loadKeys(getResources());
-    }
-
-    @Override
-    protected InputStream openKeyPairResource(String resourceKey, String resource) throws IOException {
-        ClassLoader cl = resolveClassLoader();
-        if (cl == null) {
-            throw new StreamCorruptedException("No resource loader for " + resource);
-        }
-
-        InputStream input = cl.getResourceAsStream(resource);
-        if (input == null) {
-            throw new FileNotFoundException("Cannot find resource " + resource);
-        }
-
-        return input;
-    }
-
-    protected ClassLoader resolveClassLoader() {
-        ClassLoader cl = getResourceLoader();
-        if (cl == null) {
-            cl = ThreadUtils.resolveDefaultClassLoader(getClass());
-        }
-        return cl;
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/FileKeyPairProvider.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/FileKeyPairProvider.java b/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/FileKeyPairProvider.java
deleted file mode 100644
index c4aae97..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/FileKeyPairProvider.java
+++ /dev/null
@@ -1,92 +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.keyprovider;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Objects;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.io.IoUtils;
-
-/**
- * This host key provider loads private keys from the specified files. The
- * loading is <U>lazy</U> - i.e., a file is not loaded until it is actually
- * required. Once required though, its loaded {@link KeyPair} result is
- * <U>cached</U> and not re-loaded.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class FileKeyPairProvider extends AbstractResourceKeyPairProvider<Path> {
-    private Collection<? extends Path> files;
-
-    public FileKeyPairProvider() {
-        super();
-    }
-
-    public FileKeyPairProvider(Path path) {
-        this(Collections.singletonList(Objects.requireNonNull(path, "No path provided")));
-    }
-
-    public FileKeyPairProvider(Path... files) {
-        this(Arrays.asList(files));
-    }
-
-    public FileKeyPairProvider(Collection<? extends Path> files) {
-        this.files = files;
-    }
-
-    public Collection<? extends Path> getPaths() {
-        return files;
-    }
-
-    public void setFiles(Collection<File> files) {
-        setPaths(GenericUtils.map(files, File::toPath));
-    }
-
-    public void setPaths(Collection<? extends Path> paths) {
-        // use absolute path in order to have unique cache keys
-        Collection<Path> resolved = GenericUtils.map(paths, Path::toAbsolutePath);
-        resetCacheMap(resolved);
-        files = resolved;
-    }
-
-    @Override
-    public Iterable<KeyPair> loadKeys() {
-        return loadKeys(getPaths());
-    }
-
-    @Override
-    protected KeyPair doLoadKey(Path resource) throws IOException, GeneralSecurityException {
-        return super.doLoadKey((resource == null) ? null : resource.toAbsolutePath());
-    }
-
-    @Override
-    protected InputStream openKeyPairResource(String resourceKey, Path resource) throws IOException {
-        return Files.newInputStream(resource, IoUtils.EMPTY_OPEN_OPTIONS);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/KeyIdentityProvider.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/KeyIdentityProvider.java b/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/KeyIdentityProvider.java
deleted file mode 100644
index 2ef4fa2..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/KeyIdentityProvider.java
+++ /dev/null
@@ -1,204 +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.keyprovider;
-
-import java.security.KeyPair;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.function.Function;
-import java.util.function.Supplier;
-
-import org.apache.sshd.client.session.ClientSession;
-import org.apache.sshd.common.util.GenericUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FunctionalInterface
-public interface KeyIdentityProvider {
-    /**
-     * An &quot;empty&quot; implementation of {@link KeyIdentityProvider} that
-     * returns an empty group of key pairs
-     */
-    KeyIdentityProvider EMPTY_KEYS_PROVIDER = new KeyIdentityProvider() {
-        @Override
-        public Iterable<KeyPair> loadKeys() {
-            return Collections.emptyList();
-        }
-
-        @Override
-        public String toString() {
-            return "EMPTY";
-        }
-    };
-
-    /**
-     * Invokes {@link KeyIdentityProvider#loadKeys()} and returns the result - ignores
-     * {@code null} providers (i.e., returns an empty iterable instance)
-     */
-    Function<KeyIdentityProvider, Iterable<KeyPair>> LOADER = p ->
-            (p == null) ? Collections.<KeyPair>emptyList() : p.loadKeys();
-
-    /**
-     * Load available keys.
-     *
-     * @return an {@link Iterable} instance of available keys - ignored if {@code null}
-     */
-    Iterable<KeyPair> loadKeys();
-
-    /**
-     * Creates a &quot;unified&quot; {@link KeyIdentityProvider} of key pairs out of the registered
-     * {@link KeyPair} identities and the extra available ones as a single iterator
-     * of key pairs
-     *
-     *
-     * @param session The {@link ClientSession} - ignored if {@code null} (i.e., empty
-     * iterator returned)
-     * @return The wrapping KeyIdentityProvider
-     * @see ClientSession#getRegisteredIdentities()
-     * @see ClientSession#getKeyPairProvider()
-     */
-    static KeyIdentityProvider providerOf(ClientSession session) {
-        return session == null
-                ? EMPTY_KEYS_PROVIDER
-                : resolveKeyIdentityProvider(session.getRegisteredIdentities(), session.getKeyPairProvider());
-    }
-
-    /**
-     * Creates a &quot;unified&quot; {@link Iterator} of key pairs out of the registered
-     * {@link KeyPair} identities and the extra available ones as a single iterator
-     * of key pairs
-     *
-     * @param session The {@link ClientSession} - ignored if {@code null} (i.e., empty
-     * iterator returned)
-     * @return The wrapping iterator
-     * @see ClientSession#getRegisteredIdentities()
-     * @see ClientSession#getKeyPairProvider()
-     */
-    static Iterator<KeyPair> iteratorOf(ClientSession session) {
-        return iteratorOf(providerOf(session));
-    }
-
-    /**
-     * Creates a &quot;unified&quot; {@link Iterator} of {@link KeyPair}s out of 2 possible
-     * {@link KeyIdentityProvider}
-     *
-     * @param identities The registered keys identities
-     * @param keys Extra available key pairs
-     * @return The wrapping iterator
-     * @see #resolveKeyIdentityProvider(KeyIdentityProvider, KeyIdentityProvider)
-     */
-    static Iterator<KeyPair> iteratorOf(KeyIdentityProvider identities, KeyIdentityProvider keys) {
-        return iteratorOf(resolveKeyIdentityProvider(identities, keys));
-    }
-
-    /**
-     * Resolves a non-{@code null} iterator of the available keys
-     *
-     * @param provider The {@link KeyIdentityProvider} - ignored if {@code null}
-     * @return A non-{@code null} iterator - which may be empty if no provider or no keys
-     */
-    static Iterator<KeyPair> iteratorOf(KeyIdentityProvider provider) {
-        return GenericUtils.iteratorOf((provider == null) ? null : provider.loadKeys());
-    }
-
-    /**
-     * <P>Creates a &quot;unified&quot; {@link KeyIdentityProvider} out of 2 possible ones
-     * as follows:</P></BR>
-     * <UL>
-     *      <LI>If both are {@code null} then return {@code null}.</LI>
-     *      <LI>If either one is {@code null} then use the non-{@code null} one.</LI>
-     *      <LI>If both are the same instance then use it.</U>
-     *      <LI>Otherwise, returns a wrapper that groups both providers.</LI>
-     * </UL>
-     * @param identities The registered key pair identities
-     * @param keys The extra available key pairs
-     * @return The resolved provider
-     * @see #multiProvider(KeyIdentityProvider...)
-     */
-    static KeyIdentityProvider resolveKeyIdentityProvider(KeyIdentityProvider identities, KeyIdentityProvider keys) {
-        if ((keys == null) || (identities == keys)) {
-            return identities;
-        } else if (identities == null) {
-            return keys;
-        } else {
-            return multiProvider(identities, keys);
-        }
-    }
-
-    /**
-     * Wraps a group of {@link KeyIdentityProvider} into a single one
-     *
-     * @param providers The providers - ignored if {@code null}/empty (i.e., returns
-     * {@link #EMPTY_KEYS_PROVIDER})
-     * @return The wrapping provider
-     * @see #multiProvider(Collection)
-     */
-    static KeyIdentityProvider multiProvider(KeyIdentityProvider... providers) {
-        return multiProvider(GenericUtils.asList(providers));
-    }
-
-    /**
-     * Wraps a group of {@link KeyIdentityProvider} into a single one
-     *
-     * @param providers The providers - ignored if {@code null}/empty (i.e., returns
-     * {@link #EMPTY_KEYS_PROVIDER})
-     * @return The wrapping provider
-     */
-    static KeyIdentityProvider multiProvider(Collection<? extends KeyIdentityProvider> providers) {
-        return GenericUtils.isEmpty(providers) ? EMPTY_KEYS_PROVIDER : wrapKeyPairs(iterableOf(providers));
-    }
-
-    /**
-     * Wraps a group of {@link KeyIdentityProvider} into an {@link Iterable} of {@link KeyPair}s
-     *
-     * @param providers The group of providers - ignored if {@code null}/empty (i.e., returns an
-     * empty iterable instance)
-     * @return The wrapping iterable
-     */
-    static Iterable<KeyPair> iterableOf(Collection<? extends KeyIdentityProvider> providers) {
-        Iterable<Supplier<Iterable<KeyPair>>> keysSuppliers =
-                GenericUtils.<KeyIdentityProvider, Supplier<Iterable<KeyPair>>>wrapIterable(providers, p -> p::loadKeys);
-        return GenericUtils.multiIterableSuppliers(keysSuppliers);
-    }
-
-    /**
-     * Wraps a group of {@link KeyPair}s into a {@link KeyIdentityProvider}
-     *
-     * @param pairs The key pairs - ignored if {@code null}/empty (i.e., returns
-     * {@link #EMPTY_KEYS_PROVIDER}).
-     * @return The provider wrapper
-     */
-    static KeyIdentityProvider wrapKeyPairs(KeyPair... pairs) {
-        return wrapKeyPairs(GenericUtils.asList(pairs));
-    }
-
-    /**
-     * Wraps a group of {@link KeyPair}s into a {@link KeyIdentityProvider}
-     *
-     * @param pairs The key pairs {@link Iterable} - ignored if {@code null} (i.e., returns
-     * {@link #EMPTY_KEYS_PROVIDER}).
-     * @return The provider wrapper
-     */
-    static KeyIdentityProvider wrapKeyPairs(Iterable<KeyPair> pairs) {
-        return (pairs == null) ? EMPTY_KEYS_PROVIDER : () -> pairs;
-    }
-}


[02/51] [abbrv] mina-sshd git commit: [SSHD-842] Split Putty key files support code to separate artifact

Posted by lg...@apache.org.
[SSHD-842] Split Putty key files support code to separate artifact


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

Branch: refs/heads/master
Commit: f60fcb0a547441f001b2a624022af0f73b80ce7f
Parents: 10de190
Author: Goldstein Lyor <ly...@cb4.com>
Authored: Thu Sep 6 10:43:46 2018 +0300
Committer: Lyor Goldstein <ly...@gmail.com>
Committed: Thu Sep 6 19:07:54 2018 +0300

----------------------------------------------------------------------
 README.md                                       |  44 +++-
 assembly/pom.xml                                |   5 +
 pom.xml                                         |   1 +
 sshd-cli/pom.xml                                |   5 +
 .../loader/putty/AbstractPuttyKeyDecoder.java   | 218 -------------------
 .../keys/loader/putty/DSSPuttyKeyDecoder.java   |  65 ------
 .../keys/loader/putty/ECDSAPuttyKeyDecoder.java |  98 ---------
 .../keys/loader/putty/EdDSAPuttyKeyDecoder.java |  68 ------
 .../putty/PuttyKeyPairResourceParser.java       | 200 -----------------
 .../keys/loader/putty/PuttyKeyReader.java       |  75 -------
 .../config/keys/loader/putty/PuttyKeyUtils.java |  67 ------
 .../keys/loader/putty/RSAPuttyKeyDecoder.java   |  72 ------
 .../keys/loader/putty/PuttyKeyUtilsTest.java    | 153 -------------
 ...KeyUtilsTest-ecdsa-sha2-nistp256-KeyPair.ppk |  10 -
 ...KeyUtilsTest-ecdsa-sha2-nistp384-KeyPair.ppk |  11 -
 ...KeyUtilsTest-ecdsa-sha2-nistp521-KeyPair.ppk |  12 -
 .../putty/PuttyKeyUtilsTest-ssh-dss-KeyPair.ppk |  17 --
 .../PuttyKeyUtilsTest-ssh-ed25519-KeyPair.ppk   |   9 -
 .../putty/PuttyKeyUtilsTest-ssh-rsa-KeyPair.ppk |  18 --
 ...-AES-256-CBC-ecdsa-sha2-nistp256-KeyPair.ppk |  10 -
 ...-AES-256-CBC-ecdsa-sha2-nistp384-KeyPair.ppk |  11 -
 ...-AES-256-CBC-ecdsa-sha2-nistp521-KeyPair.ppk |  12 -
 ...t-passphrase-AES-256-CBC-ssh-dss-KeyPair.ppk |  17 --
 ...ssphrase-AES-256-CBC-ssh-ed25519-KeyPair.ppk |   9 -
 ...t-passphrase-AES-256-CBC-ssh-rsa-KeyPair.ppk |  18 --
 sshd-putty/pom.xml                              | 107 +++++++++
 .../loader/putty/AbstractPuttyKeyDecoder.java   | 218 +++++++++++++++++++
 .../keys/loader/putty/DSSPuttyKeyDecoder.java   |  65 ++++++
 .../keys/loader/putty/ECDSAPuttyKeyDecoder.java |  98 +++++++++
 .../keys/loader/putty/EdDSAPuttyKeyDecoder.java |  68 ++++++
 .../putty/PuttyKeyPairResourceParser.java       | 200 +++++++++++++++++
 .../keys/loader/putty/PuttyKeyReader.java       |  75 +++++++
 .../config/keys/loader/putty/PuttyKeyUtils.java |  67 ++++++
 .../keys/loader/putty/RSAPuttyKeyDecoder.java   |  72 ++++++
 sshd-putty/src/main/resources/.gitignore        |   0
 .../keys/loader/putty/PuttyKeyUtilsTest.java    | 153 +++++++++++++
 sshd-putty/src/test/resources/.gitignore        |   0
 sshd-putty/src/test/resources/log4j.properties  |  38 ++++
 ...KeyUtilsTest-ecdsa-sha2-nistp256-KeyPair.ppk |  10 +
 ...KeyUtilsTest-ecdsa-sha2-nistp384-KeyPair.ppk |  11 +
 ...KeyUtilsTest-ecdsa-sha2-nistp521-KeyPair.ppk |  12 +
 .../putty/PuttyKeyUtilsTest-ssh-dss-KeyPair.ppk |  17 ++
 .../PuttyKeyUtilsTest-ssh-ed25519-KeyPair.ppk   |   9 +
 .../putty/PuttyKeyUtilsTest-ssh-rsa-KeyPair.ppk |  18 ++
 ...-AES-256-CBC-ecdsa-sha2-nistp256-KeyPair.ppk |  10 +
 ...-AES-256-CBC-ecdsa-sha2-nistp384-KeyPair.ppk |  11 +
 ...-AES-256-CBC-ecdsa-sha2-nistp521-KeyPair.ppk |  12 +
 ...t-passphrase-AES-256-CBC-ssh-dss-KeyPair.ppk |  17 ++
 ...ssphrase-AES-256-CBC-ssh-ed25519-KeyPair.ppk |   9 +
 ...t-passphrase-AES-256-CBC-ssh-rsa-KeyPair.ppk |  18 ++
 50 files changed, 1366 insertions(+), 1174 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/README.md
----------------------------------------------------------------------
diff --git a/README.md b/README.md
index cad3ea8..4fb0f17 100644
--- a/README.md
+++ b/README.md
@@ -1578,13 +1578,49 @@ The code contains [support for "wrapper" protocols](https://issues.apache.org/ji
 
 * `SshServer/ServerSession#setServerProxyAcceptor` - sets a proxy that intercept the 1st incoming packet before being processed by the server
 
-## Useful extra components in _sshd-contrib_
+## Configuration/data files parsing support
+
+Most of the configuration data files parsing support resides in the _sshd-common_ artfiact:
+
+```xml
+    <dependency>
+        <groupId>org.apache.sshd</groupId>
+        <artifactId>sshd-common</artifactId>
+        <version>...same version as the rest of the artifacts...</version>
+    </dependency>
+```
+
+The code contains support for parsing the [_authorized_keys_](http://man.openbsd.org/sshd.8#AUTHORIZED_KEYS_FILE_FORMAT),
+[_known\_hosts_](http://www.manpagez.com/man/8/sshd/), [_ssh\_config_, _sshd\_config_](https://www.freebsd.org/cgi/man.cgi?query=ssh_config&sektion=5),
+and [_~/config_](http://www.gsp.com/cgi-bin/man.cgi?topic=ssh_config) files. The code resides in the _sshd-common_ artifact - specifically
+the `KeyUtils#getPublicKeyEntryDecoder`, `AuthorizedKeyEntry#readAuthorizedKeys`, `KnownHostEntry#readKnownHostEntries`
+and `HostConfigEntry#readHostConfigEntries`.
+
+### PEM/OpenSSH
 
-* PUTTY key file(s) readers - see `org.apache.sshd.common.config.keys.loader.putty` package - specifically `PuttyKeyUtils#DEFAULT_INSTANCE KeyPairResourceParser`.
+The common code contains built-in support for parsing PEM and/or _OpenSSH_ formatted key files and using them for authentication purposes.
+As mentioned previously, it can leverage _Bouncycastle_ if available, but can do most of the work without it as well. For _ed25519_ support,
+one must provide the _eddsa_ artifact dependency.
 
+### [PUTTY](https://www.putty.org/)
+
+The code contains built-in support for parsing PUTTY key files (usually _.ppk_) and using them same as SSH ones as key-pair
+providers for autentication purposes. The PUTTY key file(s) readers are contained in the `org.apache.sshd.common.config.keys.loader.putty`
+package (specifically `PuttyKeyUtils#DEFAULT_INSTANCE KeyPairResourceParser`) of the _sshd-putty_ artifact. **Note:** the artifact should
+be included as an extra dependency:
+
+```xml
+    <dependency>
+        <groupId>org.apache.sshd</groupId>
+        <artifactId>sshd-putty</artifactId>
+        <version>...same version as the rest of the artifacts...</version>
+    </dependency>
+```
+
+## Useful extra components in _sshd-contrib_
 
-* `InteractivePasswordIdentityProvider` - helps implement a `PasswordIdentityProvider` by delegating calls to `UserInteraction#getUpdatedPassword`.
-The way to use it would be as follows:
+* `InteractivePasswordIdentityProvider` - helps implement a `PasswordIdentityProvider` by delegating calls
+to `UserInteraction#getUpdatedPassword`. The way to use it would be as follows:
 
 
 ```java

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/assembly/pom.xml
----------------------------------------------------------------------
diff --git a/assembly/pom.xml b/assembly/pom.xml
index 93011a8..58f20a0 100644
--- a/assembly/pom.xml
+++ b/assembly/pom.xml
@@ -43,6 +43,11 @@
         </dependency>
         <dependency>
             <groupId>org.apache.sshd</groupId>
+            <artifactId>sshd-putty</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sshd</groupId>
             <artifactId>sshd-core</artifactId>
             <version>${project.version}</version>
         </dependency>

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index a684d78..46d068e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1136,6 +1136,7 @@
 
     <modules>
         <module>sshd-common</module>
+        <module>sshd-putty</module>
         <module>sshd-core</module>
         <module>sshd-mina</module>
         <module>sshd-netty</module>

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-cli/pom.xml
----------------------------------------------------------------------
diff --git a/sshd-cli/pom.xml b/sshd-cli/pom.xml
index 3babf6b..7b2c33a 100644
--- a/sshd-cli/pom.xml
+++ b/sshd-cli/pom.xml
@@ -52,6 +52,11 @@
             <artifactId>sshd-sftp</artifactId>
             <version>${project.version}</version>
         </dependency>
+        <dependency>
+            <groupId>org.apache.sshd</groupId>
+            <artifactId>sshd-putty</artifactId>
+            <version>${project.version}</version>
+        </dependency>
 
             <!-- Test dependencies -->
         <dependency>

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/AbstractPuttyKeyDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/AbstractPuttyKeyDecoder.java b/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/AbstractPuttyKeyDecoder.java
deleted file mode 100644
index d2428e2..0000000
--- a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/AbstractPuttyKeyDecoder.java
+++ /dev/null
@@ -1,218 +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.config.keys.loader.putty;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.StreamCorruptedException;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.util.Base64;
-import java.util.Base64.Decoder;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.config.keys.impl.AbstractIdentityResourceLoader;
-import org.apache.sshd.common.config.keys.loader.KeyPairResourceParser;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-
-/**
- * @param <PUB> Generic public key type
- * @param <PRV> Generic private key type
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class AbstractPuttyKeyDecoder<PUB extends PublicKey, PRV extends PrivateKey>
-                extends AbstractIdentityResourceLoader<PUB, PRV>
-                implements PuttyKeyPairResourceParser<PUB, PRV> {
-    public static final String ENCRYPTION_HEADER = "Encryption";
-
-    protected AbstractPuttyKeyDecoder(Class<PUB> pubType, Class<PRV> prvType, Collection<String> names) {
-        super(pubType, prvType, names);
-    }
-
-    @Override
-    public boolean canExtractKeyPairs(String resourceKey, List<String> lines)
-            throws IOException, GeneralSecurityException {
-        if (!PuttyKeyPairResourceParser.super.canExtractKeyPairs(resourceKey, lines)) {
-            return false;
-        }
-
-        for (String l : lines) {
-            l = GenericUtils.trimToEmpty(l);
-            if (!l.startsWith(KEY_FILE_HEADER_PREFIX)) {
-                continue;
-            }
-
-            int pos = l.indexOf(':');
-            if ((pos <= 0) || (pos >= (l.length() - 1))) {
-                return false;
-            }
-
-            Collection<String> supported = getSupportedTypeNames();
-            String typeValue = l.substring(pos + 1).trim();
-            return supported.contains(typeValue);
-        }
-
-        return false;
-    }
-
-    @Override
-    public Collection<KeyPair> loadKeyPairs(
-            String resourceKey, FilePasswordProvider passwordProvider, List<String> lines)
-                    throws IOException, GeneralSecurityException {
-        List<String> pubLines = Collections.emptyList();
-        List<String> prvLines = Collections.emptyList();
-        String prvEncryption = null;
-        for (int index = 0, numLines = lines.size(); index < numLines; index++) {
-            String l = lines.get(index);
-            l = GenericUtils.trimToEmpty(l);
-            int pos = l.indexOf(':');
-            if ((pos <= 0) || (pos >= (l.length() - 1))) {
-                continue;
-            }
-
-            String hdrName = l.substring(0, pos).trim();
-            String hdrValue = l.substring(pos + 1).trim();
-            switch (hdrName) {
-                case ENCRYPTION_HEADER:
-                    if (prvEncryption != null) {
-                        throw new StreamCorruptedException("Duplicate " + hdrName + " in" + resourceKey);
-                    }
-                    prvEncryption = hdrValue;
-                    break;
-                case PUBLIC_LINES_HEADER:
-                    pubLines = extractDataLines(resourceKey, lines, index + 1, hdrName, hdrValue, pubLines);
-                    index += pubLines.size();
-                    break;
-                case PRIVATE_LINES_HEADER:
-                    prvLines = extractDataLines(resourceKey, lines, index + 1, hdrName, hdrValue, prvLines);
-                    index += prvLines.size();
-                    break;
-                default:    // ignored
-            }
-        }
-
-        return loadKeyPairs(resourceKey, pubLines, prvLines, prvEncryption, passwordProvider);
-    }
-
-    public static List<String> extractDataLines(
-            String resourceKey, List<String> lines, int startIndex, String hdrName, String hdrValue, List<String> curLines)
-                throws IOException {
-        if (GenericUtils.size(curLines) > 0) {
-            throw new StreamCorruptedException("Duplicate " + hdrName + " in " + resourceKey);
-        }
-
-        int numLines;
-        try {
-            numLines = Integer.parseInt(hdrValue);
-        } catch (NumberFormatException e) {
-            throw new StreamCorruptedException("Bad " + hdrName + " value (" + hdrValue + ") in " + resourceKey);
-        }
-
-        int endIndex = startIndex + numLines;
-        int totalLines = lines.size();
-        if (endIndex > totalLines) {
-            throw new StreamCorruptedException("Excessive " + hdrName + " value (" + hdrValue + ") in " + resourceKey);
-        }
-
-        return lines.subList(startIndex, endIndex);
-    }
-
-    public Collection<KeyPair> loadKeyPairs(
-            String resourceKey, List<String> pubLines, List<String> prvLines, String prvEncryption, FilePasswordProvider passwordProvider)
-                throws IOException, GeneralSecurityException {
-        return loadKeyPairs(resourceKey,
-                KeyPairResourceParser.joinDataLines(pubLines), KeyPairResourceParser.joinDataLines(prvLines),
-                prvEncryption, passwordProvider);
-    }
-
-    public Collection<KeyPair> loadKeyPairs(
-            String resourceKey, String pubData, String prvData, String prvEncryption, FilePasswordProvider passwordProvider)
-                throws IOException, GeneralSecurityException {
-        Decoder b64Decoder = Base64.getDecoder();
-        byte[] pubBytes = b64Decoder.decode(pubData);
-        byte[] prvBytes = b64Decoder.decode(prvData);
-        String password = null;
-        if ((GenericUtils.length(prvEncryption) > 0)
-                && (!NO_PRIVATE_KEY_ENCRYPTION_VALUE.equalsIgnoreCase(prvEncryption))) {
-            password = passwordProvider.getPassword(resourceKey);
-        }
-
-        if (GenericUtils.isEmpty(prvEncryption)
-                || NO_PRIVATE_KEY_ENCRYPTION_VALUE.equalsIgnoreCase(prvEncryption)
-                || GenericUtils.isEmpty(password)) {
-            return loadKeyPairs(resourceKey, pubBytes, prvBytes);
-        }
-
-        // format is "<cipher><bits>-<mode>" - e.g., "aes256-cbc"
-        int pos = prvEncryption.indexOf('-');
-        if (pos <= 0) {
-            throw new StreamCorruptedException("Missing private key encryption mode in " + prvEncryption);
-        }
-
-        String mode = prvEncryption.substring(pos + 1).toUpperCase();
-        String algName = null;
-        int numBits = 0;
-        for (int index = 0; index < pos; index++) {
-            char ch = prvEncryption.charAt(index);
-            if ((ch >= '0') && (ch <= '9')) {
-                algName = prvEncryption.substring(0, index).toUpperCase();
-                numBits = Integer.parseInt(prvEncryption.substring(index, pos));
-                break;
-            }
-        }
-
-        if (GenericUtils.isEmpty(algName) || (numBits <= 0)) {
-            throw new StreamCorruptedException("Missing private key encryption algorithm details in " + prvEncryption);
-        }
-
-        prvBytes = PuttyKeyPairResourceParser.decodePrivateKeyBytes(prvBytes, algName, numBits, mode, password);
-        return loadKeyPairs(resourceKey, pubBytes, prvBytes);
-    }
-
-    public Collection<KeyPair> loadKeyPairs(String resourceKey, byte[] pubData, byte[] prvData)
-            throws IOException, GeneralSecurityException {
-        ValidateUtils.checkNotNullAndNotEmpty(pubData, "No public key data in %s", resourceKey);
-        ValidateUtils.checkNotNullAndNotEmpty(prvData, "No private key data in %s", resourceKey);
-        try (InputStream pubStream = new ByteArrayInputStream(pubData);
-             InputStream prvStream = new ByteArrayInputStream(prvData)) {
-            return loadKeyPairs(resourceKey, pubStream, prvStream);
-        }
-    }
-
-    public Collection<KeyPair> loadKeyPairs(String resourceKey, InputStream pubData, InputStream prvData)
-            throws IOException, GeneralSecurityException {
-        try (PuttyKeyReader pubReader =
-                new PuttyKeyReader(ValidateUtils.checkNotNull(pubData, "No public key data in %s", resourceKey));
-             PuttyKeyReader prvReader =
-                new PuttyKeyReader(ValidateUtils.checkNotNull(prvData, "No private key data in %s", resourceKey))) {
-            return loadKeyPairs(resourceKey, pubReader, prvReader);
-        }
-    }
-
-    public abstract Collection<KeyPair> loadKeyPairs(String resourceKey, PuttyKeyReader pubReader, PuttyKeyReader prvReader)
-            throws IOException, GeneralSecurityException;
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/DSSPuttyKeyDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/DSSPuttyKeyDecoder.java b/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/DSSPuttyKeyDecoder.java
deleted file mode 100644
index 366aead..0000000
--- a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/DSSPuttyKeyDecoder.java
+++ /dev/null
@@ -1,65 +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.config.keys.loader.putty;
-
-import java.io.IOException;
-import java.math.BigInteger;
-import java.security.GeneralSecurityException;
-import java.security.KeyFactory;
-import java.security.KeyPair;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.interfaces.DSAPrivateKey;
-import java.security.interfaces.DSAPublicKey;
-import java.security.spec.DSAPrivateKeySpec;
-import java.security.spec.DSAPublicKeySpec;
-import java.util.Collection;
-import java.util.Collections;
-
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class DSSPuttyKeyDecoder extends AbstractPuttyKeyDecoder<DSAPublicKey, DSAPrivateKey> {
-    public static final DSSPuttyKeyDecoder INSTANCE = new DSSPuttyKeyDecoder();
-
-    public DSSPuttyKeyDecoder() {
-        super(DSAPublicKey.class, DSAPrivateKey.class, Collections.singletonList(KeyPairProvider.SSH_DSS));
-    }
-
-    @Override
-    public Collection<KeyPair> loadKeyPairs(String resourceKey, PuttyKeyReader pubReader, PuttyKeyReader prvReader)
-            throws IOException, GeneralSecurityException {
-        pubReader.skip();   // skip version
-
-        BigInteger p = pubReader.readInt();
-        BigInteger q = pubReader.readInt();
-        BigInteger g = pubReader.readInt();
-        BigInteger y = pubReader.readInt();
-        BigInteger x = prvReader.readInt();
-        KeyFactory kf = SecurityUtils.getKeyFactory(KeyUtils.DSS_ALGORITHM);
-        PublicKey pubKey = kf.generatePublic(new DSAPublicKeySpec(y, p, q, g));
-        PrivateKey prvKey = kf.generatePrivate(new DSAPrivateKeySpec(x, p, q, g));
-        return Collections.singletonList(new KeyPair(pubKey, prvKey));
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/ECDSAPuttyKeyDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/ECDSAPuttyKeyDecoder.java b/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/ECDSAPuttyKeyDecoder.java
deleted file mode 100644
index a257ff8..0000000
--- a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/ECDSAPuttyKeyDecoder.java
+++ /dev/null
@@ -1,98 +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.config.keys.loader.putty;
-
-import java.io.IOException;
-import java.math.BigInteger;
-import java.security.GeneralSecurityException;
-import java.security.KeyFactory;
-import java.security.KeyPair;
-import java.security.NoSuchAlgorithmException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.interfaces.ECPrivateKey;
-import java.security.interfaces.ECPublicKey;
-import java.security.spec.ECParameterSpec;
-import java.security.spec.ECPoint;
-import java.security.spec.ECPrivateKeySpec;
-import java.security.spec.ECPublicKeySpec;
-import java.security.spec.InvalidKeySpecException;
-import java.util.Collection;
-import java.util.Collections;
-
-import org.apache.sshd.common.cipher.ECCurves;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.util.buffer.BufferUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * TODO Add javadoc
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class ECDSAPuttyKeyDecoder extends AbstractPuttyKeyDecoder<ECPublicKey, ECPrivateKey> {
-    public static final ECDSAPuttyKeyDecoder INSTANCE = new ECDSAPuttyKeyDecoder();
-
-    public ECDSAPuttyKeyDecoder() {
-        super(ECPublicKey.class, ECPrivateKey.class, ECCurves.KEY_TYPES);
-    }
-
-    @Override
-    public Collection<KeyPair> loadKeyPairs(String resourceKey, PuttyKeyReader pubReader, PuttyKeyReader prvReader)
-            throws IOException, GeneralSecurityException {
-        if (!SecurityUtils.isECCSupported()) {
-            throw new NoSuchAlgorithmException("ECC not supported for " + resourceKey);
-        }
-
-        String keyType = pubReader.readString();
-        ECCurves curve = ECCurves.fromKeyType(keyType);
-        if (curve == null) {
-            throw new InvalidKeySpecException("Not an EC curve name: " + keyType);
-        }
-
-        String encCurveName = pubReader.readString();
-        String keyCurveName = curve.getName();
-        if (!keyCurveName.equals(encCurveName)) {
-            throw new InvalidKeySpecException("Mismatched key curve name (" + keyCurveName + ") vs. encoded one (" + encCurveName + ")");
-        }
-
-        byte[] octets = pubReader.read();
-        ECPoint w;
-        try {
-            w = ECCurves.octetStringToEcPoint(octets);
-            if (w == null) {
-                throw new InvalidKeySpecException("No public ECPoint generated for curve=" + keyCurveName
-                        + " from octets=" + BufferUtils.toHex(':', octets));
-            }
-        } catch (RuntimeException e) {
-            throw new InvalidKeySpecException("Failed (" + e.getClass().getSimpleName() + ")"
-                    + " to generate public ECPoint for curve=" + keyCurveName
-                    + " from octets=" + BufferUtils.toHex(':', octets)
-                    + ": " + e.getMessage());
-        }
-
-        KeyFactory kf = SecurityUtils.getKeyFactory(KeyUtils.EC_ALGORITHM);
-        ECParameterSpec paramSpec = curve.getParameters();
-        PublicKey pubKey = kf.generatePublic(new ECPublicKeySpec(w, paramSpec));
-
-        BigInteger s = prvReader.readInt();
-        PrivateKey prvKey = kf.generatePrivate(new ECPrivateKeySpec(s, paramSpec));
-        return Collections.singletonList(new KeyPair(pubKey, prvKey));
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/EdDSAPuttyKeyDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/EdDSAPuttyKeyDecoder.java b/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/EdDSAPuttyKeyDecoder.java
deleted file mode 100644
index f5980ab..0000000
--- a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/EdDSAPuttyKeyDecoder.java
+++ /dev/null
@@ -1,68 +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.config.keys.loader.putty;
-
-import java.io.IOException;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.security.NoSuchAlgorithmException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.spec.InvalidKeySpecException;
-import java.util.Collection;
-import java.util.Collections;
-
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.security.SecurityUtils;
-import org.apache.sshd.common.util.security.eddsa.EdDSASecurityProviderUtils;
-
-import net.i2p.crypto.eddsa.EdDSAPrivateKey;
-import net.i2p.crypto.eddsa.EdDSAPublicKey;
-
-/**
- * TODO Add javadoc
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class EdDSAPuttyKeyDecoder extends AbstractPuttyKeyDecoder<EdDSAPublicKey, EdDSAPrivateKey> {
-    public static final EdDSAPuttyKeyDecoder INSTANCE = new EdDSAPuttyKeyDecoder();
-
-    public EdDSAPuttyKeyDecoder() {
-        super(EdDSAPublicKey.class, EdDSAPrivateKey.class, Collections.singletonList(KeyPairProvider.SSH_ED25519));
-    }
-
-    @Override
-    public Collection<KeyPair> loadKeyPairs(String resourceKey, PuttyKeyReader pubReader, PuttyKeyReader prvReader)
-            throws IOException, GeneralSecurityException {
-        if (!SecurityUtils.isEDDSACurveSupported()) {
-            throw new NoSuchAlgorithmException(SecurityUtils.EDDSA + " provider not supported for " + resourceKey);
-        }
-
-        String keyType = pubReader.readString();
-        if (!KeyPairProvider.SSH_ED25519.equals(keyType)) {
-            throw new InvalidKeySpecException("Not an " + SecurityUtils.EDDSA + " key: " + keyType);
-        }
-
-        byte[] seed = pubReader.read();
-        PublicKey pubKey = EdDSASecurityProviderUtils.generateEDDSAPublicKey(seed);
-        seed = prvReader.read();
-        PrivateKey prvKey = EdDSASecurityProviderUtils.generateEDDSAPrivateKey(seed);
-        return Collections.singletonList(new KeyPair(pubKey, prvKey));
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyPairResourceParser.java
----------------------------------------------------------------------
diff --git a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyPairResourceParser.java b/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyPairResourceParser.java
deleted file mode 100644
index 06443d9..0000000
--- a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyPairResourceParser.java
+++ /dev/null
@@ -1,200 +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.config.keys.loader.putty;
-
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.spec.InvalidKeySpecException;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
-
-import javax.crypto.Cipher;
-import javax.crypto.spec.IvParameterSpec;
-import javax.crypto.spec.SecretKeySpec;
-
-import org.apache.sshd.common.config.keys.IdentityResourceLoader;
-import org.apache.sshd.common.config.keys.loader.KeyPairResourceParser;
-import org.apache.sshd.common.digest.BuiltinDigests;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-//CHECKSTYLE:OFF
-/**
- * Loads a {@link KeyPair} from PuTTY's &quot;.ppk&quot; file.
- * <P>Note(s):</P>
- * <UL>
- *      <P><LI>
- *      The file appears to be a text file but it doesn't have a fixed encoding like UTF-8.
- *      We use UTF-8 as the default encoding - since the important part is all ASCII,
- *      this shouldn't really hurt the interpretation of the key.
- *      </LI></P>
- *
- *      <P><LI>
- *      Based on code from <A HREF="https://github.com/kohsuke/trilead-putty-extension">Kohsuke's Trilead Putty Extension</A>
- *      </LI></P>
- *
- *      <P><LI>
- *      Encrypted keys requires AES-256-CBC support, which is available only if the
- *      <A HREF="http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html">
- *      Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files</A> are installed
- *      </LI></P>
- * </UL>
- *
- * <P>Sample PuTTY file format</P>
- * <PRE>
- * PuTTY-User-Key-File-2: ssh-rsa
- * Encryption: none
- * Comment: rsa-key-20080514
- * Public-Lines: 4
- * AAAAB3NzaC1yc2EAAAABJQAAAIEAiPVUpONjGeVrwgRPOqy3Ym6kF/f8bltnmjA2
- * BMdAtaOpiD8A2ooqtLS5zWYuc0xkW0ogoKvORN+RF4JI+uNUlkxWxnzJM9JLpnvA
- * HrMoVFaQ0cgDMIHtE1Ob1cGAhlNInPCRnGNJpBNcJ/OJye3yt7WqHP4SPCCLb6nL
- * nmBUrLM=
- * Private-Lines: 8
- * AAAAgGtYgJzpktzyFjBIkSAmgeVdozVhgKmF6WsDMUID9HKwtU8cn83h6h7ug8qA
- * hUWcvVxO201/vViTjWVz9ALph3uMnpJiuQaaNYIGztGJBRsBwmQW9738pUXcsUXZ
- * 79KJP01oHn6Wkrgk26DIOsz04QOBI6C8RumBO4+F1WdfueM9AAAAQQDmA4hcK8Bx
- * nVtEpcF310mKD3nsbJqARdw5NV9kCxPnEsmy7Sy1L4Ob/nTIrynbc3MA9HQVJkUz
- * 7V0va5Pjm/T7AAAAQQCYbnG0UEekwk0LG1Hkxh1OrKMxCw2KWMN8ac3L0LVBg/Tk
- * 8EnB2oT45GGeJaw7KzdoOMFZz0iXLsVLNUjNn2mpAAAAQQCN6SEfWqiNzyc/w5n/
- * lFVDHExfVUJp0wXv+kzZzylnw4fs00lC3k4PZDSsb+jYCMesnfJjhDgkUA0XPyo8
- * Emdk
- * Private-MAC: 50c45751d18d74c00fca395deb7b7695e3ed6f77
- * </PRE>
- * @param <PUB> Generic public key type
- * @param <PRV> Generic private key type
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-//CHECKSTYLE:ON
-public interface PuttyKeyPairResourceParser<PUB extends PublicKey, PRV extends PrivateKey>
-        extends IdentityResourceLoader<PUB, PRV>, KeyPairResourceParser {
-    String KEY_FILE_HEADER_PREFIX = "PuTTY-User-Key-File";
-    String PUBLIC_LINES_HEADER = "Public-Lines";
-    String PRIVATE_LINES_HEADER = "Private-Lines";
-    String PPK_FILE_SUFFIX = ".ppk";
-
-    List<String> KNOWN_HEADERS =
-            Collections.unmodifiableList(
-                    Arrays.asList(
-                            KEY_FILE_HEADER_PREFIX,
-                            PUBLIC_LINES_HEADER,
-                            PRIVATE_LINES_HEADER));
-
-    /**
-     * Value (case insensitive) used to denote that private key is not encrypted
-     */
-    String NO_PRIVATE_KEY_ENCRYPTION_VALUE = "none";
-
-    @Override
-    default boolean canExtractKeyPairs(String resourceKey, List<String> lines)
-            throws IOException, GeneralSecurityException {
-        if (GenericUtils.isEmpty(lines)) {
-            return false;
-        }
-
-        for (String l : lines) {
-            l = GenericUtils.trimToEmpty(l);
-            for (String hdr : KNOWN_HEADERS) {
-                if (l.startsWith(hdr)) {
-                    return true;
-                }
-            }
-        }
-
-        return false;
-    }
-
-    static byte[] decodePrivateKeyBytes(byte[] prvBytes, String algName, int numBits, String algMode, String password)
-            throws GeneralSecurityException {
-        Objects.requireNonNull(prvBytes, "No encrypted key bytes");
-        ValidateUtils.checkNotNullAndNotEmpty(algName, "No encryption algorithm", GenericUtils.EMPTY_OBJECT_ARRAY);
-        ValidateUtils.checkTrue(numBits > 0, "Invalid encryption key size: %d", numBits);
-        ValidateUtils.checkNotNullAndNotEmpty(algMode, "No encryption mode", GenericUtils.EMPTY_OBJECT_ARRAY);
-        ValidateUtils.checkNotNullAndNotEmpty(password, "No encryption password", GenericUtils.EMPTY_OBJECT_ARRAY);
-
-        if (!"AES".equalsIgnoreCase(algName)) {
-            throw new NoSuchAlgorithmException("decodePrivateKeyBytes(" + algName + "-" + numBits + "-" + algMode + ") N/A");
-        }
-
-        return decodePrivateKeyBytes(prvBytes, algName, algMode, numBits, new byte[16], toEncryptionKey(password));
-    }
-
-    static byte[] decodePrivateKeyBytes(
-            byte[] encBytes, String cipherName, String cipherMode, int numBits, byte[] initVector, byte[] keyValue)
-                    throws GeneralSecurityException {
-        String xform = cipherName + "/" + cipherMode + "/NoPadding";
-        int maxAllowedBits = Cipher.getMaxAllowedKeyLength(xform);
-        // see http://www.javamex.com/tutorials/cryptography/unrestricted_policy_files.shtml
-        if (numBits > maxAllowedBits) {
-            throw new InvalidKeySpecException("decodePrivateKeyBytes(" + xform + ")"
-                    + " required key length (" + numBits + ") exceeds max. available: " + maxAllowedBits);
-        }
-
-        SecretKeySpec skeySpec = new SecretKeySpec(keyValue, cipherName);
-        IvParameterSpec ivspec = new IvParameterSpec(initVector);
-        Cipher cipher = SecurityUtils.getCipher(xform);
-        cipher.init(Cipher.DECRYPT_MODE, skeySpec, ivspec);
-
-        return cipher.doFinal(encBytes);
-    }
-
-    /**
-     * Converts a pass-phrase into a key, by following the convention that PuTTY uses.
-     * Used to decrypt the private key when it's encrypted.
-     * @param passphrase the Password to be used as seed for the key - ignored
-     * if {@code null}/empty
-     * @return The encryption key bytes - {@code null} if no pass-phrase
-     * @throws GeneralSecurityException If cannot retrieve SHA-1 digest
-     * @see <A HREF="http://security.stackexchange.com/questions/71341/how-does-putty-derive-the-encryption-key-in-its-ppk-format">
-     * How does Putty derive the encryption key in its .ppk format ?</A>
-     */
-    static byte[] toEncryptionKey(String passphrase) throws GeneralSecurityException {
-        if (GenericUtils.isEmpty(passphrase)) {
-            return null;
-        }
-
-        MessageDigest hash = SecurityUtils.getMessageDigest(BuiltinDigests.sha1.getAlgorithm());
-        byte[] stateValue = {0, 0, 0, 0};
-        byte[] passBytes = passphrase.getBytes(StandardCharsets.UTF_8);
-        byte[] keyValue = new byte[32];
-        for (int i = 0, remLen = keyValue.length; i < 2; i++) {
-            hash.reset(); // just making sure
-
-            stateValue[3] = (byte) i;
-            hash.update(stateValue);
-            hash.update(passBytes);
-
-            byte[] digest = hash.digest();
-            System.arraycopy(digest, 0, keyValue, i * 20, Math.min(20, remLen));
-            remLen -= 20;
-        }
-
-        return keyValue;
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyReader.java
----------------------------------------------------------------------
diff --git a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyReader.java b/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyReader.java
deleted file mode 100644
index 4fb63d1..0000000
--- a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyReader.java
+++ /dev/null
@@ -1,75 +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.config.keys.loader.putty;
-
-import java.io.Closeable;
-import java.io.DataInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.StreamCorruptedException;
-import java.math.BigInteger;
-import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
-
-/**
- * Helper class for {@code Putty} key files decoders
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class PuttyKeyReader implements Closeable {
-    private final DataInputStream di;
-
-    public PuttyKeyReader(InputStream s) {
-        di = new DataInputStream(s);
-    }
-
-    public void skip() throws IOException {
-        int skipSize = di.readInt();
-        int effectiveSkip = di.skipBytes(skipSize);
-        if (skipSize != effectiveSkip) {
-            throw new StreamCorruptedException("Mismatched skip size: expected" + skipSize + ", actual=" + effectiveSkip);
-        }
-    }
-
-    public String readString() throws IOException {
-        return readString(StandardCharsets.UTF_8);
-    }
-
-    public String readString(Charset cs) throws IOException {
-        byte[] data = read();
-        return new String(data, cs);
-    }
-
-    public BigInteger readInt() throws IOException {
-        byte[] bytes = read();
-        return new BigInteger(bytes);
-    }
-
-    public byte[] read() throws IOException {
-        int len = di.readInt();
-        byte[] r = new byte[len];
-        di.readFully(r);
-        return r;
-    }
-
-    @Override
-    public void close() throws IOException {
-        di.close();
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtils.java
----------------------------------------------------------------------
diff --git a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtils.java b/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtils.java
deleted file mode 100644
index e750ace..0000000
--- a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtils.java
+++ /dev/null
@@ -1,67 +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.config.keys.loader.putty;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.NavigableMap;
-import java.util.TreeMap;
-
-import org.apache.sshd.common.config.keys.loader.KeyPairResourceParser;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public final class PuttyKeyUtils {
-    public static final List<PuttyKeyPairResourceParser<?, ?>> DEFAULT_PARSERS;
-
-    public static final NavigableMap<String, PuttyKeyPairResourceParser<?, ?>> BY_KEY_TYPE;
-
-    public static final KeyPairResourceParser DEFAULT_INSTANCE;
-
-    static {
-        List<PuttyKeyPairResourceParser<?, ?>> parsers = new ArrayList<>();
-        parsers.add(RSAPuttyKeyDecoder.INSTANCE);
-        parsers.add(DSSPuttyKeyDecoder.INSTANCE);
-        if (SecurityUtils.isECCSupported()) {
-            parsers.add(ECDSAPuttyKeyDecoder.INSTANCE);
-        }
-        if (SecurityUtils.isEDDSACurveSupported()) {
-            parsers.add(EdDSAPuttyKeyDecoder.INSTANCE);
-        }
-        NavigableMap<String, PuttyKeyPairResourceParser<?, ?>> map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
-        for (PuttyKeyPairResourceParser<?, ?> p : parsers) {
-            Collection<String> supported = p.getSupportedTypeNames();
-            for (String k : supported) {
-                map.put(k, p);
-            }
-        }
-        DEFAULT_PARSERS = Collections.unmodifiableList(parsers);
-        BY_KEY_TYPE = Collections.unmodifiableNavigableMap(map);
-        DEFAULT_INSTANCE = KeyPairResourceParser.aggregate(parsers);
-    }
-
-    private PuttyKeyUtils() {
-        throw new UnsupportedOperationException("No instance");
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/RSAPuttyKeyDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/RSAPuttyKeyDecoder.java b/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/RSAPuttyKeyDecoder.java
deleted file mode 100644
index 0a55d55..0000000
--- a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/RSAPuttyKeyDecoder.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.common.config.keys.loader.putty;
-
-import java.io.IOException;
-import java.math.BigInteger;
-import java.security.GeneralSecurityException;
-import java.security.KeyFactory;
-import java.security.KeyPair;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.interfaces.RSAPrivateKey;
-import java.security.interfaces.RSAPublicKey;
-import java.security.spec.RSAPrivateCrtKeySpec;
-import java.security.spec.RSAPrivateKeySpec;
-import java.security.spec.RSAPublicKeySpec;
-import java.util.Collection;
-import java.util.Collections;
-
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class RSAPuttyKeyDecoder extends AbstractPuttyKeyDecoder<RSAPublicKey, RSAPrivateKey> {
-    public static final RSAPuttyKeyDecoder INSTANCE = new RSAPuttyKeyDecoder();
-
-    public RSAPuttyKeyDecoder() {
-        super(RSAPublicKey.class, RSAPrivateKey.class, Collections.singletonList(KeyPairProvider.SSH_RSA));
-    }
-
-    @Override
-    public Collection<KeyPair> loadKeyPairs(String resourceKey, PuttyKeyReader pubReader, PuttyKeyReader prvReader)
-            throws IOException, GeneralSecurityException {
-        pubReader.skip();   // skip version
-
-        KeyFactory kf = SecurityUtils.getKeyFactory(KeyUtils.RSA_ALGORITHM);
-        BigInteger publicExp = pubReader.readInt();
-        BigInteger modulus = pubReader.readInt();
-        PublicKey pubKey = kf.generatePublic(new RSAPublicKeySpec(modulus, publicExp));
-
-        BigInteger privateExp = prvReader.readInt();
-        BigInteger primeP = prvReader.readInt();
-        BigInteger primeQ = prvReader.readInt();
-        BigInteger crtCoef = prvReader.readInt();
-        BigInteger primeExponentP = privateExp.mod(primeP.subtract(BigInteger.ONE));
-        BigInteger primeExponentQ = privateExp.mod(primeQ.subtract(BigInteger.ONE));
-        RSAPrivateKeySpec prvSpec = new RSAPrivateCrtKeySpec(
-                modulus, publicExp, privateExp, primeP, primeQ, primeExponentP, primeExponentQ, crtCoef);
-        PrivateKey prvKey = kf.generatePrivate(prvSpec);
-        return Collections.singletonList(new KeyPair(pubKey, prvKey));
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/test/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-contrib/src/test/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest.java b/sshd-contrib/src/test/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest.java
deleted file mode 100644
index 8b70f77..0000000
--- a/sshd-contrib/src/test/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest.java
+++ /dev/null
@@ -1,153 +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.config.keys.loader.putty;
-
-import java.io.IOException;
-import java.net.URL;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.util.Collection;
-import java.util.List;
-
-import org.apache.sshd.common.cipher.BuiltinCiphers;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.config.keys.PrivateKeyEntryDecoder;
-import org.apache.sshd.common.config.keys.loader.openssh.OpenSSHKeyPairResourceParser;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.Assume;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runner.RunWith;
-import org.junit.runners.MethodSorters;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-import org.junit.runners.Parameterized.UseParametersRunnerFactory;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@RunWith(Parameterized.class)   // see https://github.com/junit-team/junit/wiki/Parameterized-tests
-@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
-@Category({ NoIoTestCase.class })
-public class PuttyKeyUtilsTest extends BaseTestSupport {
-    public static final String PASSWORD = "super secret passphrase";
-
-    private final String keyType;
-    private final String regularFile;
-    private final String encryptedFile;
-    private final PuttyKeyPairResourceParser<?, ?> parser;
-
-    public PuttyKeyUtilsTest(String keyType) {
-        this.keyType = keyType;
-        this.parser = PuttyKeyUtils.BY_KEY_TYPE.get(keyType);
-        this.regularFile = getClass().getSimpleName()
-                + "-" + keyType + "-" + KeyPair.class.getSimpleName()
-                + PuttyKeyPairResourceParser.PPK_FILE_SUFFIX;
-        this.encryptedFile = PASSWORD.replace(' ', '-') + "-AES-256-CBC"
-                + "-" + keyType + "-" + KeyPair.class.getSimpleName()
-                + PuttyKeyPairResourceParser.PPK_FILE_SUFFIX;
-    }
-
-    @Parameters(name = "{0}")
-    public static List<Object[]> parameters() {
-        return parameterize(PuttyKeyUtils.BY_KEY_TYPE.keySet());
-    }
-
-    @Test
-    public void testCanDecodePuttyKeyFile() throws IOException, GeneralSecurityException {
-        for (String resource : new String[]{regularFile, encryptedFile}) {
-            URL url = getClass().getResource(resource);
-            if (GenericUtils.isSameReference(regularFile, resource)) {
-                assertNotNull("Missing test resource: " + resource, url);
-            } else {
-                if (url == null) {
-                    outputDebugMessage("Skip non-existing encrypted file: %s", resource);
-                    continue;
-                }
-            }
-
-            List<String> lines = IoUtils.readAllLines(url);
-            assertTrue(resource + " - can extract key pair", parser.canExtractKeyPairs(resource, lines));
-
-            for (PuttyKeyPairResourceParser<?, ?> other : PuttyKeyUtils.BY_KEY_TYPE.values()) {
-                if (parser == other) {
-                    continue;
-                }
-
-                assertFalse(other.getClass().getSimpleName() + "/" + resource + " - unexpected extraction capability",
-                        other.canExtractKeyPairs(resource, lines));
-            }
-        }
-    }
-
-    @Test
-    public void testDecodePuttyKeyFile() throws IOException, GeneralSecurityException {
-        URL url = getClass().getResource(regularFile);
-        assertNotNull("Missing test resource: " + regularFile, url);
-
-        Collection<KeyPair> keys = parser.loadKeyPairs(url, null);
-        assertEquals("Mismatched loaded keys count from " + regularFile, 1, GenericUtils.size(keys));
-        assertLoadedKeyPair(regularFile, keys.iterator().next());
-    }
-
-    @Test
-    public void testDecodeEncryptedPuttyKeyFile() throws IOException, GeneralSecurityException {
-        Assume.assumeTrue(BuiltinCiphers.aes256cbc.getTransformation() + " N/A", BuiltinCiphers.aes256cbc.isSupported());
-        URL url = getClass().getResource(encryptedFile);
-        Assume.assumeTrue("Skip non-existent encrypted file: " + encryptedFile, url != null);
-        assertNotNull("Missing test resource: " + encryptedFile, url);
-
-        Collection<KeyPair> keys = parser.loadKeyPairs(url, r -> PASSWORD);
-        assertEquals("Mismatched loaded keys count from " + encryptedFile, 1, GenericUtils.size(keys));
-
-        assertLoadedKeyPair(encryptedFile, keys.iterator().next());
-    }
-
-    private void assertLoadedKeyPair(String prefix, KeyPair kp) throws GeneralSecurityException {
-        assertNotNull(prefix + ": no key pair loaded", kp);
-
-        PublicKey pubKey = kp.getPublic();
-        assertNotNull(prefix + ": no public key loaded", pubKey);
-        assertEquals(prefix + ": mismatched public key type", keyType, KeyUtils.getKeyType(pubKey));
-
-        PrivateKey prvKey = kp.getPrivate();
-        assertNotNull(prefix + ": no private key loaded", prvKey);
-        assertEquals(prefix + ": mismatched private key type", keyType, KeyUtils.getKeyType(prvKey));
-
-        @SuppressWarnings("rawtypes")
-        PrivateKeyEntryDecoder decoder =
-            OpenSSHKeyPairResourceParser.getPrivateKeyEntryDecoder(prvKey);
-        assertNotNull("No private key decoder", decoder);
-
-        if (decoder.isPublicKeyRecoverySupported()) {
-            @SuppressWarnings("unchecked")
-            PublicKey recKey = decoder.recoverPublicKey(prvKey);
-            assertKeyEquals("Mismatched recovered public key", pubKey, recKey);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp256-KeyPair.ppk
----------------------------------------------------------------------
diff --git a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp256-KeyPair.ppk b/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp256-KeyPair.ppk
deleted file mode 100644
index 509538a..0000000
--- a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp256-KeyPair.ppk
+++ /dev/null
@@ -1,10 +0,0 @@
-PuTTY-User-Key-File-2: ecdsa-sha2-nistp256
-Encryption: none
-Comment: ecdsa-key-20170917
-Public-Lines: 3
-AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBM99zj2+E6AN
-xMZ/2SKFP/fAvPfUJUdsgJyn4g7nf36vNpoaRyq1FyHLxyT34AgTl1n3DwcaBXXC
-O5pCv6xFwYk=
-Private-Lines: 1
-AAAAIQDdmu/Dr68r6a0PNbQUN1bX+zS6J5jFsOlEAx8sR73Tuw==
-Private-MAC: 012e0d61593a431ae84beb6216dd29e4b203c1c0

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp384-KeyPair.ppk
----------------------------------------------------------------------
diff --git a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp384-KeyPair.ppk b/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp384-KeyPair.ppk
deleted file mode 100644
index 238261c..0000000
--- a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp384-KeyPair.ppk
+++ /dev/null
@@ -1,11 +0,0 @@
-PuTTY-User-Key-File-2: ecdsa-sha2-nistp384
-Encryption: none
-Comment: ecdsa-key-20170917
-Public-Lines: 3
-AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBNHjIMrdMfXw
-CUqBAhkZw0vXB+qypkiTcL1CmcopmPrKvGHFieFmedeCQotjwJkoAAeb5isZNOXy
-h+7TnHGNrE/pZkHuNwACilpOt659hbhR2OGHX0jdpb8y4RVkuPQssg==
-Private-Lines: 2
-AAAAMHNGt3UPG3evJVl1GRoXXnqTafWLDQdWA2A1tXi8oRW0hhHMRe9/v0SCGL7S
-nL3asg==
-Private-MAC: 6fb6e93559ecacfa468aa5ff9776e4d4283db5ba

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp521-KeyPair.ppk
----------------------------------------------------------------------
diff --git a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp521-KeyPair.ppk b/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp521-KeyPair.ppk
deleted file mode 100644
index f60a78e..0000000
--- a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp521-KeyPair.ppk
+++ /dev/null
@@ -1,12 +0,0 @@
-PuTTY-User-Key-File-2: ecdsa-sha2-nistp521
-Encryption: none
-Comment: ecdsa-key-20170917
-Public-Lines: 4
-AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBADGM237T9rT
-zE++sOFDN0VWfYfojlQ8dYP82OlgA24Yh0ZpOsezBBiHtHfMHl9tWHmch1YKmH7B
-lOfqbOgcIhz9cwA2V7Nu3IUGqxZT18LOXEpcdyDSphJ6jsy1urqBLrOz4DF6Udyr
-rFV4OQELovf8YdUsM91YPfe1DfnSRi1I5v20uA==
-Private-Lines: 2
-AAAAQgE28iZoHARx+2VzL7Y45FaY44TngX2b4StlC8wOlYF7NY/ba+Pt2RT/WcNL
-ytmLdj5QeV/fFJ9x8i00mTU6KCF2AA==
-Private-MAC: 7379e9986066087dff9339d2b0b968c2b31f45c7

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-dss-KeyPair.ppk
----------------------------------------------------------------------
diff --git a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-dss-KeyPair.ppk b/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-dss-KeyPair.ppk
deleted file mode 100644
index 59a8fac..0000000
--- a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-dss-KeyPair.ppk
+++ /dev/null
@@ -1,17 +0,0 @@
-PuTTY-User-Key-File-2: ssh-dss
-Encryption: none
-Comment: dsa-key-20130709
-Public-Lines: 10
-AAAAB3NzaC1kc3MAAACBAMg/IxsG5BxnF5gM7IKqqR0rftxZC+n5GlbO+J4H+iIb
-/KR8NBehkxG3CrBZMF96M2K1sEGYLob+3k4r71oWaPul8n5rt9kpd+JSq4iD2ygO
-yg6Kd1/YDBHoxneizy6I/bGsLwhAAKWcRNrXmYVKGzhrhvZWN12AJDq2mGdj3szL
-AAAAFQD7a2MltdUSF7FU3//SpW4WGjZbeQAAAIBf0nNsfKQL/TEMo7IpTrEMg5V0
-RnSigCX0+yUERS42GW/ZeCZBJw7oL2XZbuBtu63vMjDgVpnb92BdrcPgjJ7EFW6D
-lcyeuywStmg1ygXmDR2AQCxv0eX2CQgrdUczmRa155SDVUTvTQlO1IyKx0vwKAh1
-H7E3yJUfkTAJstbGYQAAAIEAtv+cdRfNevYFkp55jVqazc8zRLvfb64jzgc5oSJV
-c64kFs4yx+abYpGX9WxNxDlG6g2WiY8voDBB0YnUJsn0kVRjBKX9OceROxrfT4K4
-dVbQZsdt+SLaXWL4lGJFrFZL3LZqvySvq6xfhJfakQDDivW4hUOhFPXPHrE5/Ia3
-T7A=
-Private-Lines: 1
-AAAAFQCE6flGnmVCAbzo9YsbdJWBnxMnBA==
-Private-MAC: 6ace0a5e5bf23649c1375e91dcd71d1def6c6aa1

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-ed25519-KeyPair.ppk
----------------------------------------------------------------------
diff --git a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-ed25519-KeyPair.ppk b/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-ed25519-KeyPair.ppk
deleted file mode 100644
index 614ac69..0000000
--- a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-ed25519-KeyPair.ppk
+++ /dev/null
@@ -1,9 +0,0 @@
-PuTTY-User-Key-File-2: ssh-ed25519
-Encryption: none
-Comment: ed25519-key-20170917
-Public-Lines: 2
-AAAAC3NzaC1lZDI1NTE5AAAAIN7fuKSIM5TbAX/1I1Ts3tfyo5eEs7JpmKsegHs/
-9fIi
-Private-Lines: 1
-AAAAIADKJJPxsUp7JXLzm1zwk8UswW/lkiwPJ73CbqGvalgP
-Private-MAC: 28a22234152feaf1d9a6a10ca0ae3a51b5e6dd52

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-rsa-KeyPair.ppk
----------------------------------------------------------------------
diff --git a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-rsa-KeyPair.ppk b/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-rsa-KeyPair.ppk
deleted file mode 100644
index da2868e..0000000
--- a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-rsa-KeyPair.ppk
+++ /dev/null
@@ -1,18 +0,0 @@
-PuTTY-User-Key-File-2: ssh-rsa
-Encryption: none
-Comment: rsa-key-20130709
-Public-Lines: 4
-AAAAB3NzaC1yc2EAAAABJQAAAIBLSj7+SllQdWjkD8EN4sA/fUv/jhc+ssGmCYx3
-uoiPMxjKH3xUPWu4zxJzhdlgFy4gchzjU51fDS4Oel6xGoWbGKGe4ZLQdE/t8N8l
-jAfOm/5lGp5tFhHs9UHoSm/h3RsErWNjKPjTGIlID35IcOXVhfhp9fX0RU6y/ZBI
-PhM20w==
-Private-Lines: 8
-AAAAgBx89T2fl2qNSkh0qemUEWQhkmCyTfwMSUW+bIBUawXATpGrDHLmztBOWgIy
-pUb0A52S9iyAgLwugCEnYhl/qCxvoARH7ZyTdYAL4KjJDySxVuqeo/ZhLscYcMAz
-MOyn8g5cR4dRgEwJ1/pRuK8r4+Z96zJG4NlxlHsUjHuj7t1dAAAAQQCTrj48XKIX
-M3dxWLSsSXbUCOpmAOTviuz9LD0H1ik7a6ebr0P6GTl9z7iscBgzdjBIHMFcdvar
-ophUJ5iRanCvAAAAQQCCg1VU1H5FHMipRvvw8b/zRqDsx6GTZs03ffhyKrTl1Dcd
-0oKy5/U3kQwdXPOSlVZeyX8nUVE2o7DOh7INsX0dAAAAQHkPjxivrN0SQuVAx+tK
-uRJ8vGL7sBOZ9gdTS25T3BVEkhRt37aDcshrodzDCzd515cwhmbLSsOsgyxcTwcX
-7SA=
-Private-MAC: 2416438f1a7ebdd33d519f6102d843b5f2c565d4

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp256-KeyPair.ppk
----------------------------------------------------------------------
diff --git a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp256-KeyPair.ppk b/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp256-KeyPair.ppk
deleted file mode 100644
index 4c601c7..0000000
--- a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp256-KeyPair.ppk
+++ /dev/null
@@ -1,10 +0,0 @@
-PuTTY-User-Key-File-2: ecdsa-sha2-nistp256
-Encryption: aes256-cbc
-Comment: ecdsa-key-20170917
-Public-Lines: 3
-AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBM99zj2+E6AN
-xMZ/2SKFP/fAvPfUJUdsgJyn4g7nf36vNpoaRyq1FyHLxyT34AgTl1n3DwcaBXXC
-O5pCv6xFwYk=
-Private-Lines: 1
-/8MdniIqAaST5t3/bRx4mFdFxqN8jIwI0Hh7VTy8IV143j+PktgGIoHsUwTiEujQ
-Private-MAC: be4b37d4b65ad09e6890534a2ba355599da796c6

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp384-KeyPair.ppk
----------------------------------------------------------------------
diff --git a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp384-KeyPair.ppk b/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp384-KeyPair.ppk
deleted file mode 100644
index f57f5fd..0000000
--- a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp384-KeyPair.ppk
+++ /dev/null
@@ -1,11 +0,0 @@
-PuTTY-User-Key-File-2: ecdsa-sha2-nistp384
-Encryption: aes256-cbc
-Comment: ecdsa-key-20170917
-Public-Lines: 3
-AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBNHjIMrdMfXw
-CUqBAhkZw0vXB+qypkiTcL1CmcopmPrKvGHFieFmedeCQotjwJkoAAeb5isZNOXy
-h+7TnHGNrE/pZkHuNwACilpOt659hbhR2OGHX0jdpb8y4RVkuPQssg==
-Private-Lines: 2
-MOgJnSZ8ZqGHFVtQvYKpOyTGWjMVIjIOMIUwhbxBuTiQ7WEyNPn9jjTsSwXtJxrG
-UI6NGeIqZ41P2e8JINhMIg==
-Private-MAC: 5ec40946270150ddfca35cce61f4265d7bfe7b7f

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp521-KeyPair.ppk
----------------------------------------------------------------------
diff --git a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp521-KeyPair.ppk b/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp521-KeyPair.ppk
deleted file mode 100644
index 97077fd..0000000
--- a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp521-KeyPair.ppk
+++ /dev/null
@@ -1,12 +0,0 @@
-PuTTY-User-Key-File-2: ecdsa-sha2-nistp521
-Encryption: aes256-cbc
-Comment: ecdsa-key-20170917
-Public-Lines: 4
-AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBADGM237T9rT
-zE++sOFDN0VWfYfojlQ8dYP82OlgA24Yh0ZpOsezBBiHtHfMHl9tWHmch1YKmH7B
-lOfqbOgcIhz9cwA2V7Nu3IUGqxZT18LOXEpcdyDSphJ6jsy1urqBLrOz4DF6Udyr
-rFV4OQELovf8YdUsM91YPfe1DfnSRi1I5v20uA==
-Private-Lines: 2
-+44AQO4aflPquBZbdB3UtdLuXuHV2u1YoghxYXPFGdhskMt+XjJhUlrOHNX8rmgR
-E9nni474zGw9ni/3LIZwMILQI/xTfiQm3t6nKFV8nyI=
-Private-MAC: 99c25e348a84de4876163758ad13b2ad1dc43629

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-dss-KeyPair.ppk
----------------------------------------------------------------------
diff --git a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-dss-KeyPair.ppk b/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-dss-KeyPair.ppk
deleted file mode 100644
index 9da31f1..0000000
--- a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-dss-KeyPair.ppk
+++ /dev/null
@@ -1,17 +0,0 @@
-PuTTY-User-Key-File-2: ssh-dss
-Encryption: aes256-cbc
-Comment: dsa-key-20130909
-Public-Lines: 10
-AAAAB3NzaC1kc3MAAACBALVdprWfZ7+FiITCILnDkeMZ2ntkV2WjW5RcyiQvJvBO
-jCNiVtK87xATEOfBb20YvNZ/CibBjGS1TL5TBqRV5XleucPHMJZ5rXdJ2FH5oZnL
-kna3Et+L1/O/GQMmp2vfSFrO3n3+mI1Jozx3FoQO8jr1zIerJ5Mc4LKqsIQB9hvR
-AAAAFQD+z1y1/4ll4ax3rri8mkYgGDhqIQAAAIBVU4VJ7V7GoEQJ5WBMbpDEcLIZ
-KUgSHsJMQzWnLOi/DcsPjVMDX6FWGPLtrjd7fgInlPMCC/SPAhXdaXMvHZSkvBHV
-DfNjpsDgsxBnK1FKqRGtD49rETFGDl92EOsyBhv+9ymdOPX6R0hCqS+ulZheQPXI
-iHXdIvQK2Ev5Dy3xNgAAAIA8qhumHZcKss+Puuw+mY5J5Qt7Omv16MuDsYiVqrBq
-1V2C9gutx3Tu+n5QYi0xPlkkP/knMtkUZS+Wt3Dr8zPcEzNBc/Tm2EdYp11jZNx4
-4PM4ing+aCU5oGcg/7RS5CrY5Fn/rvgHqK22XiC8/U55iti44bWKvI6HCejExeZX
-iA==
-Private-Lines: 1
-64IdcIX48CNGjhcxsVN0vSNpT7S72e3FVdQ3t4ENAvI=
-Private-MAC: 8e62b44b5c080965e361936520be4feaa50285b1

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-ed25519-KeyPair.ppk
----------------------------------------------------------------------
diff --git a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-ed25519-KeyPair.ppk b/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-ed25519-KeyPair.ppk
deleted file mode 100644
index 668ef1e..0000000
--- a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-ed25519-KeyPair.ppk
+++ /dev/null
@@ -1,9 +0,0 @@
-PuTTY-User-Key-File-2: ssh-ed25519
-Encryption: aes256-cbc
-Comment: ed25519-key-20170917
-Public-Lines: 2
-AAAAC3NzaC1lZDI1NTE5AAAAIN7fuKSIM5TbAX/1I1Ts3tfyo5eEs7JpmKsegHs/
-9fIi
-Private-Lines: 1
-0cPG5BR80jQcJmHKs6IjpHS3R4/CTnudnJB4BcjaqKlRk0l603GVMDzTxkaICCb8
-Private-MAC: 381cff136b2516331ff4511cf382533fc14f0aeb

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-rsa-KeyPair.ppk
----------------------------------------------------------------------
diff --git a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-rsa-KeyPair.ppk b/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-rsa-KeyPair.ppk
deleted file mode 100644
index 2a11d04..0000000
--- a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-rsa-KeyPair.ppk
+++ /dev/null
@@ -1,18 +0,0 @@
-PuTTY-User-Key-File-2: ssh-rsa
-Encryption: aes256-cbc
-Comment: rsa-key-20130909
-Public-Lines: 4
-AAAAB3NzaC1yc2EAAAABJQAAAIEAhY5eZJrX0a3+rtEZCq0nu2zvAHp16nk93jhi
-p7c9tTDlGm9QEAgqzmuilEeUQ4BssxAvhCFEo/7Qbg4M7PwcA5cFkjXE4gj0YDJM
-ay7l2mb5aIoS/hACgNz54p/w/UgfQC1Vygt6QtvXXAW8Lh/YCN4Zw4ViROUhoYuy
-3K5SBYs=
-Private-Lines: 8
-mqcGPnrv9d1tYkJZSGaCy5REslPZ2xh8m7qAbN+bD1m7iQ77pLxlKyzs82rbRaC9
-KSnKwsbFl7o92NT+9yYKJ7ehXyWyrUXkn9KcPk7MzNVwMuWVDXwvHodGLCyVCLYq
-PNipvg2USHvnCjnnvtMysBRNJiHTMOaf/gSZLyaEuznYo3FEClMPzggY9b2nrxnV
-O1ttk1FJatkRflwFjn3A/R/GpowmBnkDyCkVlTvR+uBAg8iIy1Vzj5zIV9zmzfgx
-DxPot+Y81y+Xe3ohVh2s1FVvLw+KQbYbCQam5j0V/dTQ+oVWjCJBlibD3aVTGK0M
-Jswz8wPwXFo5N0yX/6ZTrshbvTzoO1bg0+HUu581ZSAeqttk9C1RLmWFS8YDm0Hn
-GhDXrjuAvKJ3cjeVJsumgVw45NYGARuzV24TlHUtU+eze8Y/0NsPJXoCfVoYjTjb
-fjlMh9rYbRdyNHXwYTzwbw==
-Private-MAC: f4c50b3da0b73c34e8989411fc48c884c09e20a0

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-putty/pom.xml
----------------------------------------------------------------------
diff --git a/sshd-putty/pom.xml b/sshd-putty/pom.xml
new file mode 100644
index 0000000..8e11385
--- /dev/null
+++ b/sshd-putty/pom.xml
@@ -0,0 +1,107 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+
+    <!--
+
+        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.
+    -->
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.sshd</groupId>
+        <artifactId>sshd</artifactId>
+        <version>2.0.1-SNAPSHOT</version>
+        <relativePath>..</relativePath>
+    </parent>
+
+    <artifactId>sshd-putty</artifactId>
+    <name>Apache Mina SSHD :: Putty key files support utilities</name>
+    <packaging>jar</packaging>
+    <inceptionYear>2018</inceptionYear>
+
+    <properties>
+        <projectRoot>${project.basedir}/..</projectRoot>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.sshd</groupId>
+            <artifactId>sshd-common</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.bouncycastle</groupId>
+            <artifactId>bcpg-jdk15on</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.bouncycastle</groupId>
+            <artifactId>bcpkix-jdk15on</artifactId>
+            <optional>true</optional>
+        </dependency>
+
+            <!-- For ed25519 support -->
+        <dependency>
+            <groupId>net.i2p.crypto</groupId>
+            <artifactId>eddsa</artifactId>
+            <optional>true</optional>
+        </dependency>
+
+            <!-- test dependencies -->
+        <dependency>
+            <groupId>org.apache.sshd</groupId>
+            <artifactId>sshd-common</artifactId>
+            <version>${project.version}</version>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>jcl-over-slf4j</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-log4j12</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <redirectTestOutputToFile>true</redirectTestOutputToFile>
+                    <reportsDirectory>${project.build.directory}/surefire-reports-putty</reportsDirectory>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/AbstractPuttyKeyDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/AbstractPuttyKeyDecoder.java b/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/AbstractPuttyKeyDecoder.java
new file mode 100644
index 0000000..d2428e2
--- /dev/null
+++ b/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/AbstractPuttyKeyDecoder.java
@@ -0,0 +1,218 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config.keys.loader.putty;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StreamCorruptedException;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.util.Base64;
+import java.util.Base64.Decoder;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.impl.AbstractIdentityResourceLoader;
+import org.apache.sshd.common.config.keys.loader.KeyPairResourceParser;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * @param <PUB> Generic public key type
+ * @param <PRV> Generic private key type
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractPuttyKeyDecoder<PUB extends PublicKey, PRV extends PrivateKey>
+                extends AbstractIdentityResourceLoader<PUB, PRV>
+                implements PuttyKeyPairResourceParser<PUB, PRV> {
+    public static final String ENCRYPTION_HEADER = "Encryption";
+
+    protected AbstractPuttyKeyDecoder(Class<PUB> pubType, Class<PRV> prvType, Collection<String> names) {
+        super(pubType, prvType, names);
+    }
+
+    @Override
+    public boolean canExtractKeyPairs(String resourceKey, List<String> lines)
+            throws IOException, GeneralSecurityException {
+        if (!PuttyKeyPairResourceParser.super.canExtractKeyPairs(resourceKey, lines)) {
+            return false;
+        }
+
+        for (String l : lines) {
+            l = GenericUtils.trimToEmpty(l);
+            if (!l.startsWith(KEY_FILE_HEADER_PREFIX)) {
+                continue;
+            }
+
+            int pos = l.indexOf(':');
+            if ((pos <= 0) || (pos >= (l.length() - 1))) {
+                return false;
+            }
+
+            Collection<String> supported = getSupportedTypeNames();
+            String typeValue = l.substring(pos + 1).trim();
+            return supported.contains(typeValue);
+        }
+
+        return false;
+    }
+
+    @Override
+    public Collection<KeyPair> loadKeyPairs(
+            String resourceKey, FilePasswordProvider passwordProvider, List<String> lines)
+                    throws IOException, GeneralSecurityException {
+        List<String> pubLines = Collections.emptyList();
+        List<String> prvLines = Collections.emptyList();
+        String prvEncryption = null;
+        for (int index = 0, numLines = lines.size(); index < numLines; index++) {
+            String l = lines.get(index);
+            l = GenericUtils.trimToEmpty(l);
+            int pos = l.indexOf(':');
+            if ((pos <= 0) || (pos >= (l.length() - 1))) {
+                continue;
+            }
+
+            String hdrName = l.substring(0, pos).trim();
+            String hdrValue = l.substring(pos + 1).trim();
+            switch (hdrName) {
+                case ENCRYPTION_HEADER:
+                    if (prvEncryption != null) {
+                        throw new StreamCorruptedException("Duplicate " + hdrName + " in" + resourceKey);
+                    }
+                    prvEncryption = hdrValue;
+                    break;
+                case PUBLIC_LINES_HEADER:
+                    pubLines = extractDataLines(resourceKey, lines, index + 1, hdrName, hdrValue, pubLines);
+                    index += pubLines.size();
+                    break;
+                case PRIVATE_LINES_HEADER:
+                    prvLines = extractDataLines(resourceKey, lines, index + 1, hdrName, hdrValue, prvLines);
+                    index += prvLines.size();
+                    break;
+                default:    // ignored
+            }
+        }
+
+        return loadKeyPairs(resourceKey, pubLines, prvLines, prvEncryption, passwordProvider);
+    }
+
+    public static List<String> extractDataLines(
+            String resourceKey, List<String> lines, int startIndex, String hdrName, String hdrValue, List<String> curLines)
+                throws IOException {
+        if (GenericUtils.size(curLines) > 0) {
+            throw new StreamCorruptedException("Duplicate " + hdrName + " in " + resourceKey);
+        }
+
+        int numLines;
+        try {
+            numLines = Integer.parseInt(hdrValue);
+        } catch (NumberFormatException e) {
+            throw new StreamCorruptedException("Bad " + hdrName + " value (" + hdrValue + ") in " + resourceKey);
+        }
+
+        int endIndex = startIndex + numLines;
+        int totalLines = lines.size();
+        if (endIndex > totalLines) {
+            throw new StreamCorruptedException("Excessive " + hdrName + " value (" + hdrValue + ") in " + resourceKey);
+        }
+
+        return lines.subList(startIndex, endIndex);
+    }
+
+    public Collection<KeyPair> loadKeyPairs(
+            String resourceKey, List<String> pubLines, List<String> prvLines, String prvEncryption, FilePasswordProvider passwordProvider)
+                throws IOException, GeneralSecurityException {
+        return loadKeyPairs(resourceKey,
+                KeyPairResourceParser.joinDataLines(pubLines), KeyPairResourceParser.joinDataLines(prvLines),
+                prvEncryption, passwordProvider);
+    }
+
+    public Collection<KeyPair> loadKeyPairs(
+            String resourceKey, String pubData, String prvData, String prvEncryption, FilePasswordProvider passwordProvider)
+                throws IOException, GeneralSecurityException {
+        Decoder b64Decoder = Base64.getDecoder();
+        byte[] pubBytes = b64Decoder.decode(pubData);
+        byte[] prvBytes = b64Decoder.decode(prvData);
+        String password = null;
+        if ((GenericUtils.length(prvEncryption) > 0)
+                && (!NO_PRIVATE_KEY_ENCRYPTION_VALUE.equalsIgnoreCase(prvEncryption))) {
+            password = passwordProvider.getPassword(resourceKey);
+        }
+
+        if (GenericUtils.isEmpty(prvEncryption)
+                || NO_PRIVATE_KEY_ENCRYPTION_VALUE.equalsIgnoreCase(prvEncryption)
+                || GenericUtils.isEmpty(password)) {
+            return loadKeyPairs(resourceKey, pubBytes, prvBytes);
+        }
+
+        // format is "<cipher><bits>-<mode>" - e.g., "aes256-cbc"
+        int pos = prvEncryption.indexOf('-');
+        if (pos <= 0) {
+            throw new StreamCorruptedException("Missing private key encryption mode in " + prvEncryption);
+        }
+
+        String mode = prvEncryption.substring(pos + 1).toUpperCase();
+        String algName = null;
+        int numBits = 0;
+        for (int index = 0; index < pos; index++) {
+            char ch = prvEncryption.charAt(index);
+            if ((ch >= '0') && (ch <= '9')) {
+                algName = prvEncryption.substring(0, index).toUpperCase();
+                numBits = Integer.parseInt(prvEncryption.substring(index, pos));
+                break;
+            }
+        }
+
+        if (GenericUtils.isEmpty(algName) || (numBits <= 0)) {
+            throw new StreamCorruptedException("Missing private key encryption algorithm details in " + prvEncryption);
+        }
+
+        prvBytes = PuttyKeyPairResourceParser.decodePrivateKeyBytes(prvBytes, algName, numBits, mode, password);
+        return loadKeyPairs(resourceKey, pubBytes, prvBytes);
+    }
+
+    public Collection<KeyPair> loadKeyPairs(String resourceKey, byte[] pubData, byte[] prvData)
+            throws IOException, GeneralSecurityException {
+        ValidateUtils.checkNotNullAndNotEmpty(pubData, "No public key data in %s", resourceKey);
+        ValidateUtils.checkNotNullAndNotEmpty(prvData, "No private key data in %s", resourceKey);
+        try (InputStream pubStream = new ByteArrayInputStream(pubData);
+             InputStream prvStream = new ByteArrayInputStream(prvData)) {
+            return loadKeyPairs(resourceKey, pubStream, prvStream);
+        }
+    }
+
+    public Collection<KeyPair> loadKeyPairs(String resourceKey, InputStream pubData, InputStream prvData)
+            throws IOException, GeneralSecurityException {
+        try (PuttyKeyReader pubReader =
+                new PuttyKeyReader(ValidateUtils.checkNotNull(pubData, "No public key data in %s", resourceKey));
+             PuttyKeyReader prvReader =
+                new PuttyKeyReader(ValidateUtils.checkNotNull(prvData, "No private key data in %s", resourceKey))) {
+            return loadKeyPairs(resourceKey, pubReader, prvReader);
+        }
+    }
+
+    public abstract Collection<KeyPair> loadKeyPairs(String resourceKey, PuttyKeyReader pubReader, PuttyKeyReader prvReader)
+            throws IOException, GeneralSecurityException;
+}


[26/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common utilities code from sshd-core into sshd-common (new artifact)

Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/KnownHostHashValue.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/KnownHostHashValue.java b/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/KnownHostHashValue.java
deleted file mode 100644
index 28a3d1f..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/KnownHostHashValue.java
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.client.config.hosts;
-
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
-import java.util.Base64;
-import java.util.Objects;
-
-import org.apache.sshd.common.Factory;
-import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.NamedResource;
-import org.apache.sshd.common.RuntimeSshException;
-import org.apache.sshd.common.mac.Mac;
-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;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class KnownHostHashValue {
-    /**
-     * Character used to indicate a hashed host pattern
-     */
-    public static final char HASHED_HOST_DELIMITER = '|';
-
-    public static final NamedFactory<Mac> DEFAULT_DIGEST = KnownHostDigest.SHA1;
-
-    private NamedFactory<Mac> digester = DEFAULT_DIGEST;
-    private byte[] saltValue;
-    private byte[] digestValue;
-
-    public KnownHostHashValue() {
-        super();
-    }
-
-    public NamedFactory<Mac> getDigester() {
-        return digester;
-    }
-
-    public void setDigester(NamedFactory<Mac> digester) {
-        this.digester = digester;
-    }
-
-    public byte[] getSaltValue() {
-        return saltValue;
-    }
-
-    public void setSaltValue(byte[] saltValue) {
-        this.saltValue = saltValue;
-    }
-
-    public byte[] getDigestValue() {
-        return digestValue;
-    }
-
-    public void setDigestValue(byte[] digestValue) {
-        this.digestValue = digestValue;
-    }
-
-    /**
-     * Checks if the host matches the hash
-     *
-     * @param host The host name/address - ignored if {@code null}/empty
-     * @return {@code true} if host matches the hash
-     * @throws RuntimeException If entry not properly initialized
-     */
-    public boolean isHostMatch(String host) {
-        if (GenericUtils.isEmpty(host)) {
-            return false;
-        }
-
-        try {
-            byte[] expected = getDigestValue();
-            byte[] actual = calculateHashValue(host, getDigester(), getSaltValue());
-            return Arrays.equals(expected, actual);
-        } catch (Throwable t) {
-            if (t instanceof RuntimeException) {
-                throw (RuntimeException) t;
-            }
-            throw new RuntimeSshException("Failed (" + t.getClass().getSimpleName() + ")"
-                    + " to calculate hash value: " + t.getMessage(), t);
-        }
-    }
-
-    @Override
-    public String toString() {
-        if ((getDigester() == null) || NumberUtils.isEmpty(getSaltValue()) || NumberUtils.isEmpty(getDigestValue())) {
-            return Objects.toString(getDigester(), null)
-                 + "-" + BufferUtils.toHex(':', getSaltValue())
-                 + "-" + BufferUtils.toHex(':', getDigestValue());
-        }
-
-        try {
-            return append(new StringBuilder(Byte.MAX_VALUE), this).toString();
-        } catch (IOException | RuntimeException e) {    // unexpected
-            return e.getClass().getSimpleName() + ": " + e.getMessage();
-        }
-    }
-
-    // see http://nms.lcs.mit.edu/projects/ssh/README.hashed-hosts
-    public static byte[] calculateHashValue(String host, Factory<? extends Mac> factory, byte[] salt) throws Exception {
-        return calculateHashValue(host, factory.create(), salt);
-    }
-
-    public static byte[] calculateHashValue(String host, Mac mac, byte[] salt) throws Exception {
-        mac.init(salt);
-
-        byte[] hostBytes = host.getBytes(StandardCharsets.UTF_8);
-        mac.update(hostBytes);
-        return mac.doFinal();
-    }
-
-    public static <A extends Appendable> A append(A sb, KnownHostHashValue hashValue) throws IOException {
-        return (hashValue == null) ? sb : append(sb, hashValue.getDigester(), hashValue.getSaltValue(), hashValue.getDigestValue());
-    }
-
-    public static <A extends Appendable> A append(A sb, NamedResource factory, byte[] salt, byte[] digest) throws IOException {
-        Base64.Encoder encoder = Base64.getEncoder();
-        sb.append(HASHED_HOST_DELIMITER).append(factory.getName());
-        sb.append(HASHED_HOST_DELIMITER).append(encoder.encodeToString(salt));
-        sb.append(HASHED_HOST_DELIMITER).append(encoder.encodeToString(digest));
-        return sb;
-    }
-
-    public static KnownHostHashValue parse(String patternString) {
-        String pattern = GenericUtils.replaceWhitespaceAndTrim(patternString);
-        return parse(pattern, GenericUtils.isEmpty(pattern) ? null : new KnownHostHashValue());
-    }
-
-    public static <V extends KnownHostHashValue> V parse(String patternString, V value) {
-        String pattern = GenericUtils.replaceWhitespaceAndTrim(patternString);
-        if (GenericUtils.isEmpty(pattern)) {
-            return value;
-        }
-
-        String[] components = GenericUtils.split(pattern, HASHED_HOST_DELIMITER);
-        ValidateUtils.checkTrue(components.length == 4 /* 1st one is empty */, "Invalid hash pattern (insufficient data): %s", pattern);
-        ValidateUtils.checkTrue(GenericUtils.isEmpty(components[0]), "Invalid hash pattern (unexpected extra data): %s", pattern);
-
-        NamedFactory<Mac> factory =
-                ValidateUtils.checkNotNull(KnownHostDigest.fromName(components[1]),
-                        "Invalid hash pattern (unknown digest): %s", pattern);
-        Base64.Decoder decoder = Base64.getDecoder();
-        value.setDigester(factory);
-        value.setSaltValue(decoder.decode(components[2]));
-        value.setDigestValue(decoder.decode(components[3]));
-        return value;
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcher.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcher.java b/sshd-core/src/main/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcher.java
deleted file mode 100644
index ab9929b..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcher.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.client.config.keys;
-
-import java.nio.file.Path;
-import java.security.KeyPair;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
-import java.util.function.Supplier;
-
-import org.apache.sshd.common.NamedResource;
-import org.apache.sshd.common.config.keys.BuiltinIdentities;
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.util.GenericUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class BuiltinClientIdentitiesWatcher extends ClientIdentitiesWatcher {
-    private final boolean supportedOnly;
-
-    public BuiltinClientIdentitiesWatcher(Path keysFolder, boolean supportedOnly,
-            ClientIdentityLoader loader, FilePasswordProvider provider, boolean strict) {
-        this(keysFolder, NamedResource.getNameList(BuiltinIdentities.VALUES), supportedOnly, loader, provider, strict);
-    }
-
-    public BuiltinClientIdentitiesWatcher(Path keysFolder, Collection<String> ids, boolean supportedOnly,
-            ClientIdentityLoader loader, FilePasswordProvider provider, boolean strict) {
-        this(keysFolder, ids, supportedOnly,
-             GenericUtils.supplierOf(Objects.requireNonNull(loader, "No client identity loader")),
-             GenericUtils.supplierOf(Objects.requireNonNull(provider, "No password provider")),
-             strict);
-    }
-
-    public BuiltinClientIdentitiesWatcher(Path keysFolder, boolean supportedOnly,
-            Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider, boolean strict) {
-        this(keysFolder, NamedResource.getNameList(BuiltinIdentities.VALUES), supportedOnly, loader, provider, strict);
-    }
-
-    public BuiltinClientIdentitiesWatcher(Path keysFolder, Collection<String> ids, boolean supportedOnly,
-            Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider, boolean strict) {
-        super(getBuiltinIdentitiesPaths(keysFolder, ids), loader, provider, strict);
-        this.supportedOnly = supportedOnly;
-    }
-
-    public final boolean isSupportedOnly() {
-        return supportedOnly;
-    }
-
-    @Override
-    public Iterable<KeyPair> loadKeys() {
-        return isSupportedOnly() ? loadKeys(this::isSupported) : super.loadKeys();
-    }
-
-    private boolean isSupported(KeyPair kp) {
-        BuiltinIdentities id = BuiltinIdentities.fromKeyPair(kp);
-        if ((id != null) && id.isSupported()) {
-            return true;
-        }
-        if (log.isDebugEnabled()) {
-            log.debug("loadKeys - remove unsupported identity={}, key-type={}, key={}",
-                    id, KeyUtils.getKeyType(kp), KeyUtils.getFingerPrint(kp.getPublic()));
-        }
-        return false;
-    }
-
-    public static List<Path> getDefaultBuiltinIdentitiesPaths(Path keysFolder) {
-        return getBuiltinIdentitiesPaths(keysFolder, NamedResource.getNameList(BuiltinIdentities.VALUES));
-    }
-
-    public static List<Path> getBuiltinIdentitiesPaths(Path keysFolder, Collection<String> ids) {
-        Objects.requireNonNull(keysFolder, "No keys folder");
-        if (GenericUtils.isEmpty(ids)) {
-            return Collections.emptyList();
-        }
-
-        List<Path> paths = new ArrayList<>(ids.size());
-        for (String id : ids) {
-            String fileName = ClientIdentity.getIdentityFileName(id);
-            paths.add(keysFolder.resolve(fileName));
-        }
-
-        return paths;
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentitiesWatcher.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentitiesWatcher.java b/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentitiesWatcher.java
deleted file mode 100644
index 094766f..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentitiesWatcher.java
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.client.config.keys;
-
-import java.nio.file.Path;
-import java.security.KeyPair;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.function.Function;
-import java.util.function.Predicate;
-import java.util.function.Supplier;
-import java.util.stream.Stream;
-
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.keyprovider.AbstractKeyPairProvider;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.GenericUtils;
-
-/**
- * Watches over a group of files that contains client identities
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class ClientIdentitiesWatcher extends AbstractKeyPairProvider implements KeyPairProvider {
-    private final Collection<ClientIdentityProvider> providers;
-
-    public ClientIdentitiesWatcher(Collection<? extends Path> paths,
-            ClientIdentityLoader loader, FilePasswordProvider provider) {
-        this(paths, loader, provider, true);
-    }
-
-    public ClientIdentitiesWatcher(Collection<? extends Path> paths,
-            ClientIdentityLoader loader, FilePasswordProvider provider, boolean strict) {
-        this(paths,
-             GenericUtils.supplierOf(Objects.requireNonNull(loader, "No client identity loader")),
-             GenericUtils.supplierOf(Objects.requireNonNull(provider, "No password provider")),
-             strict);
-    }
-
-    public ClientIdentitiesWatcher(Collection<? extends Path> paths,
-            Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider) {
-        this(paths, loader, provider, true);
-    }
-
-    public ClientIdentitiesWatcher(Collection<? extends Path> paths,
-            Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider, boolean strict) {
-        this(buildProviders(paths, loader, provider, strict));
-    }
-
-    public ClientIdentitiesWatcher(Collection<ClientIdentityProvider> providers) {
-        this.providers = providers;
-    }
-
-    @Override
-    public Iterable<KeyPair> loadKeys() {
-        return loadKeys(null);
-    }
-
-    protected Iterable<KeyPair> loadKeys(Predicate<? super KeyPair> filter) {
-        return () -> {
-            Stream<KeyPair> stream = safeMap(GenericUtils.stream(providers), this::doGetKeyPair);
-            if (filter != null) {
-                stream = stream.filter(filter);
-            }
-            return stream.iterator();
-        };
-    }
-
-    /**
-     * Performs a mapping operation on the stream, discarding any null values
-     * returned by the mapper.
-     *
-     * @param <U> Original type
-     * @param <V> Mapped type
-     * @param stream Original values stream
-     * @param mapper Mapper to target type
-     * @return Mapped stream
-     */
-    protected <U, V> Stream<V> safeMap(Stream<U> stream, Function<? super U, ? extends V> mapper) {
-        return stream.map(u -> Optional.ofNullable(mapper.apply(u)))
-                .filter(Optional::isPresent)
-                .map(Optional::get);
-    }
-
-    protected KeyPair doGetKeyPair(ClientIdentityProvider p) {
-        try {
-            KeyPair kp = p.getClientIdentity();
-            if (kp == null) {
-                if (log.isDebugEnabled()) {
-                    log.debug("loadKeys({}) no key loaded", p);
-                }
-            }
-            return kp;
-        } catch (Throwable e) {
-            log.warn("loadKeys({}) failed ({}) to load key: {}", p, e.getClass().getSimpleName(), e.getMessage());
-            if (log.isDebugEnabled()) {
-                log.debug("loadKeys(" + p + ") key load failure details", e);
-            }
-            return null;
-        }
-    }
-
-    public static List<ClientIdentityProvider> buildProviders(
-            Collection<? extends Path> paths, ClientIdentityLoader loader, FilePasswordProvider provider, boolean strict) {
-        return buildProviders(paths,
-                GenericUtils.supplierOf(Objects.requireNonNull(loader, "No client identity loader")),
-                GenericUtils.supplierOf(Objects.requireNonNull(provider, "No password provider")),
-                strict);
-    }
-
-    public static List<ClientIdentityProvider> buildProviders(
-            Collection<? extends Path> paths, Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider, boolean strict) {
-        if (GenericUtils.isEmpty(paths)) {
-            return Collections.emptyList();
-        }
-
-        return GenericUtils.map(paths, p -> new ClientIdentityFileWatcher(p, loader, provider, strict));
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentity.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentity.java b/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentity.java
deleted file mode 100644
index 9060fec..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentity.java
+++ /dev/null
@@ -1,325 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.client.config.keys;
-
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.LinkOption;
-import java.nio.file.Path;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Map;
-import java.util.TreeMap;
-import java.util.function.Function;
-
-import org.apache.sshd.client.SshClient;
-import org.apache.sshd.common.NamedResource;
-import org.apache.sshd.common.config.keys.BuiltinIdentities;
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.config.keys.IdentityUtils;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.config.keys.PublicKeyEntry;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.io.FileInfoExtractor;
-import org.apache.sshd.common.util.io.IoUtils;
-
-/**
- * Provides keys loading capability from the user's keys folder - e.g., {@code id_rsa}
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- * @see org.apache.sshd.common.util.security.SecurityUtils#getKeyPairResourceParser()
- */
-public final class ClientIdentity {
-
-    public static final String ID_FILE_PREFIX = "id_";
-
-    public static final String ID_FILE_SUFFIX = "";
-
-    public static final Function<String, String> ID_GENERATOR =
-            ClientIdentity::getIdentityFileName;
-
-    private ClientIdentity() {
-        throw new UnsupportedOperationException("No instance");
-    }
-
-    /**
-     * @param name The file name - ignored if {@code null}/empty
-     * @return The identity type - {@code null} if cannot determine it - e.g.,
-     * does not start with the {@link #ID_FILE_PREFIX}
-     */
-    public static String getIdentityType(String name) {
-        if (GenericUtils.isEmpty(name)
-                || (name.length() <= ID_FILE_PREFIX.length())
-                || (!name.startsWith(ID_FILE_PREFIX))) {
-            return null;
-        } else {
-            return name.substring(ID_FILE_PREFIX.length());
-        }
-    }
-
-    public static String getIdentityFileName(NamedResource r) {
-        return getIdentityFileName((r == null) ? null : r.getName());
-    }
-
-    /**
-     * @param type The identity type - e.g., {@code rsa} - ignored
-     *             if {@code null}/empty
-     * @return The matching file name for the identity - {@code null}
-     * if no name
-     * @see #ID_FILE_PREFIX
-     * @see #ID_FILE_SUFFIX
-     * @see IdentityUtils#getIdentityFileName(String, String, String)
-     */
-    public static String getIdentityFileName(String type) {
-        return IdentityUtils.getIdentityFileName(ID_FILE_PREFIX, type, ID_FILE_SUFFIX);
-    }
-
-    /**
-     * @param <C>           The generic client class
-     * @param client        The {@link SshClient} to updated
-     * @param strict        If {@code true} then files that do not have the required
-     *                      access rights are excluded from consideration
-     * @param supportedOnly If {@code true} then ignore identities that are not
-     *                      supported internally
-     * @param provider      A {@link FilePasswordProvider} - may be {@code null}
-     *                      if the loaded keys are <U>guaranteed</U> not to be encrypted. The argument
-     *                      to {@link FilePasswordProvider#getPassword(String)} is the path of the
-     *                      file whose key is to be loaded
-     * @param options       The {@link LinkOption}s to apply when checking
-     *                      for existence
-     * @return The updated <tt>client</tt> instance - provided a non-{@code null}
-     * {@link KeyPairProvider} was generated
-     * @throws IOException              If failed to access the file system
-     * @throws GeneralSecurityException If failed to load the keys
-     * @see #setKeyPairProvider(SshClient, Path, boolean, boolean, FilePasswordProvider, LinkOption...)
-     */
-    public static <C extends SshClient> C setKeyPairProvider(
-            C client, boolean strict, boolean supportedOnly, FilePasswordProvider provider, LinkOption... options)
-            throws IOException, GeneralSecurityException {
-        return setKeyPairProvider(client, PublicKeyEntry.getDefaultKeysFolderPath(), strict, supportedOnly, provider, options);
-    }
-
-    /**
-     * @param <C>           The generic client class
-     * @param client        The {@link SshClient} to updated
-     * @param dir           The folder to scan for the built-in identities
-     * @param strict        If {@code true} then files that do not have the required
-     *                      access rights are excluded from consideration
-     * @param supportedOnly If {@code true} then ignore identities that are not
-     *                      supported internally
-     * @param provider      A {@link FilePasswordProvider} - may be {@code null}
-     *                      if the loaded keys are <U>guaranteed</U> not to be encrypted. The argument
-     *                      to {@link FilePasswordProvider#getPassword(String)} is the path of the
-     *                      file whose key is to be loaded
-     * @param options       The {@link LinkOption}s to apply when checking
-     *                      for existence
-     * @return The updated <tt>client</tt> instance - provided a non-{@code null}
-     * {@link KeyPairProvider} was generated
-     * @throws IOException              If failed to access the file system
-     * @throws GeneralSecurityException If failed to load the keys
-     * @see #loadDefaultKeyPairProvider(Path, boolean, boolean, FilePasswordProvider, LinkOption...)
-     */
-    public static <C extends SshClient> C setKeyPairProvider(
-            C client, Path dir, boolean strict, boolean supportedOnly, FilePasswordProvider provider, LinkOption... options)
-            throws IOException, GeneralSecurityException {
-        KeyPairProvider kpp = loadDefaultKeyPairProvider(dir, strict, supportedOnly, provider, options);
-        if (kpp != null) {
-            client.setKeyPairProvider(kpp);
-        }
-
-        return client;
-    }
-
-    /**
-     * @param strict        If {@code true} then files that do not have the required
-     *                      access rights are excluded from consideration
-     * @param supportedOnly If {@code true} then ignore identities that are not
-     *                      supported internally
-     * @param provider      A {@link FilePasswordProvider} - may be {@code null}
-     *                      if the loaded keys are <U>guaranteed</U> not to be encrypted. The argument
-     *                      to {@link FilePasswordProvider#getPassword(String)} is the path of the
-     *                      file whose key is to be loaded
-     * @param options       The {@link LinkOption}s to apply when checking
-     *                      for existence
-     * @return A {@link KeyPair} for the identities - {@code null} if no identities
-     * available (e.g., after filtering unsupported ones or strict permissions)
-     * @throws IOException              If failed to access the file system
-     * @throws GeneralSecurityException If failed to load the keys
-     * @see PublicKeyEntry#getDefaultKeysFolderPath()
-     * @see #loadDefaultIdentities(Path, boolean, FilePasswordProvider, LinkOption...)
-     */
-    public static KeyPairProvider loadDefaultKeyPairProvider(
-            boolean strict, boolean supportedOnly, FilePasswordProvider provider, LinkOption... options)
-            throws IOException, GeneralSecurityException {
-        return loadDefaultKeyPairProvider(PublicKeyEntry.getDefaultKeysFolderPath(), strict, supportedOnly, provider, options);
-    }
-
-    /**
-     * @param dir           The folder to scan for the built-in identities
-     * @param strict        If {@code true} then files that do not have the required
-     *                      access rights are excluded from consideration
-     * @param supportedOnly If {@code true} then ignore identities that are not
-     *                      supported internally
-     * @param provider      A {@link FilePasswordProvider} - may be {@code null}
-     *                      if the loaded keys are <U>guaranteed</U> not to be encrypted. The argument
-     *                      to {@link FilePasswordProvider#getPassword(String)} is the path of the
-     *                      file whose key is to be loaded
-     * @param options       The {@link LinkOption}s to apply when checking
-     *                      for existence
-     * @return A {@link KeyPair} for the identities - {@code null} if no identities
-     * available (e.g., after filtering unsupported ones or strict permissions)
-     * @throws IOException              If failed to access the file system
-     * @throws GeneralSecurityException If failed to load the keys
-     * @see #loadDefaultIdentities(Path, boolean, FilePasswordProvider, LinkOption...)
-     * @see IdentityUtils#createKeyPairProvider(Map, boolean)
-     */
-    public static KeyPairProvider loadDefaultKeyPairProvider(
-            Path dir, boolean strict, boolean supportedOnly, FilePasswordProvider provider, LinkOption... options)
-            throws IOException, GeneralSecurityException {
-        Map<String, KeyPair> ids = loadDefaultIdentities(dir, strict, provider, options);
-        return IdentityUtils.createKeyPairProvider(ids, supportedOnly);
-    }
-
-    /**
-     * @param strict   If {@code true} then files that do not have the required
-     *                 access rights are excluded from consideration
-     * @param provider A {@link FilePasswordProvider} - may be {@code null}
-     *                 if the loaded keys are <U>guaranteed</U> not to be encrypted. The argument
-     *                 to {@link FilePasswordProvider#getPassword(String)} is the path of the
-     *                 file whose key is to be loaded
-     * @param options  The {@link LinkOption}s to apply when checking
-     *                 for existence
-     * @return A {@link Map} of the found files where key=identity type (case
-     * <U>insensitive</U>), value=the {@link KeyPair} of the identity
-     * @throws IOException              If failed to access the file system
-     * @throws GeneralSecurityException If failed to load the keys
-     * @see PublicKeyEntry#getDefaultKeysFolderPath()
-     * @see #loadDefaultIdentities(Path, boolean, FilePasswordProvider, LinkOption...)
-     */
-    public static Map<String, KeyPair> loadDefaultIdentities(boolean strict, FilePasswordProvider provider, LinkOption... options)
-            throws IOException, GeneralSecurityException {
-        return loadDefaultIdentities(PublicKeyEntry.getDefaultKeysFolderPath(), strict, provider, options);
-    }
-
-    /**
-     * @param dir      The folder to scan for the built-in identities
-     * @param strict   If {@code true} then files that do not have the required
-     *                 access rights are excluded from consideration
-     * @param provider A {@link FilePasswordProvider} - may be {@code null}
-     *                 if the loaded keys are <U>guaranteed</U> not to be encrypted. The argument
-     *                 to {@link FilePasswordProvider#getPassword(String)} is the path of the
-     *                 file whose key is to be loaded
-     * @param options  The {@link LinkOption}s to apply when checking
-     *                 for existence
-     * @return A {@link Map} of the found files where key=identity type (case
-     * <U>insensitive</U>), value=the {@link KeyPair} of the identity
-     * @throws IOException              If failed to access the file system
-     * @throws GeneralSecurityException If failed to load the keys
-     * @see #loadIdentities(Path, boolean, Collection, Function, FilePasswordProvider, LinkOption...)
-     * @see BuiltinIdentities
-     */
-    public static Map<String, KeyPair> loadDefaultIdentities(Path dir, boolean strict, FilePasswordProvider provider, LinkOption... options)
-            throws IOException, GeneralSecurityException {
-        return loadIdentities(dir, strict, BuiltinIdentities.NAMES, ID_GENERATOR, provider, options);
-    }
-
-    /**
-     * Scans a folder and loads all available identity files
-     *
-     * @param dir         The {@link Path} of the folder to scan - ignored if not exists
-     * @param strict      If {@code true} then files that do not have the required
-     *                    access rights are excluded from consideration
-     * @param types       The identity types - ignored if {@code null}/empty
-     * @param idGenerator A {@link Function} to derive the file name
-     *                    holding the specified type
-     * @param provider    A {@link FilePasswordProvider} - may be {@code null}
-     *                    if the loaded keys are <U>guaranteed</U> not to be encrypted. The argument
-     *                    to {@link FilePasswordProvider#getPassword(String)} is the path of the
-     *                    file whose key is to be loaded
-     * @param options     The {@link LinkOption}s to apply when checking
-     *                    for existence
-     * @return A {@link Map} of the found files where key=identity type (case
-     * <U>insensitive</U>), value=the {@link KeyPair} of the identity
-     * @throws IOException              If failed to access the file system
-     * @throws GeneralSecurityException If failed to load the keys
-     * @see #scanIdentitiesFolder(Path, boolean, Collection, Function, LinkOption...)
-     * @see IdentityUtils#loadIdentities(Map, FilePasswordProvider, java.nio.file.OpenOption...)
-     */
-    public static Map<String, KeyPair> loadIdentities(
-            Path dir, boolean strict, Collection<String> types, Function<String, String> idGenerator, FilePasswordProvider provider, LinkOption... options)
-            throws IOException, GeneralSecurityException {
-        Map<String, Path> paths = scanIdentitiesFolder(dir, strict, types, idGenerator, options);
-        return IdentityUtils.loadIdentities(paths, provider, IoUtils.EMPTY_OPEN_OPTIONS);
-    }
-
-    /**
-     * Scans a folder for possible identity files
-     *
-     * @param dir         The {@link Path} of the folder to scan - ignored if not exists
-     * @param strict      If {@code true} then files that do not have the required
-     *                    access rights are excluded from consideration
-     * @param types       The identity types - ignored if {@code null}/empty
-     * @param idGenerator A {@link Function} to derive the file name
-     *                    holding the specified type
-     * @param options     The {@link LinkOption}s to apply when checking
-     *                    for existence
-     * @return A {@link Map} of the found files where key=identity type (case
-     * <U>insensitive</U>), value=the {@link Path} of the file holding the key
-     * @throws IOException If failed to access the file system
-     * @see KeyUtils#validateStrictKeyFilePermissions(Path, LinkOption...)
-     */
-    public static Map<String, Path> scanIdentitiesFolder(
-            Path dir, boolean strict, Collection<String> types, Function<String, String> idGenerator, LinkOption... options)
-            throws IOException {
-        if (GenericUtils.isEmpty(types)) {
-            return Collections.emptyMap();
-        }
-
-        if (!Files.exists(dir, options)) {
-            return Collections.emptyMap();
-        }
-
-        ValidateUtils.checkTrue(FileInfoExtractor.ISDIR.infoOf(dir, options), "Not a directory: %s", dir);
-
-        Map<String, Path> paths = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
-        for (String t : types) {
-            String fileName = idGenerator.apply(t);
-            Path p = dir.resolve(fileName);
-            if (!Files.exists(p, options)) {
-                continue;
-            }
-
-            if (strict) {
-                if (KeyUtils.validateStrictKeyFilePermissions(p, options) != null) {
-                    continue;
-                }
-            }
-
-            Path prev = paths.put(t, p);
-            ValidateUtils.checkTrue(prev == null, "Multiple mappings for type=%s", t);
-        }
-
-        return paths;
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcher.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcher.java b/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcher.java
deleted file mode 100644
index a00cb24..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcher.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.client.config.keys;
-
-import java.io.IOException;
-import java.nio.file.Path;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.security.PublicKey;
-import java.util.Map;
-import java.util.Objects;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.function.Supplier;
-
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.common.util.io.ModifiableFileWatcher;
-
-/**
- * A {@link ClientIdentityProvider} that watches a given key file re-loading
- * its contents if it is ever modified, deleted or (re-)created
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class ClientIdentityFileWatcher extends ModifiableFileWatcher implements ClientIdentityProvider {
-    private final AtomicReference<KeyPair> identityHolder = new AtomicReference<>(null);
-    private final Supplier<ClientIdentityLoader> loaderHolder;
-    private final Supplier<FilePasswordProvider> providerHolder;
-    private final boolean strict;
-
-    public ClientIdentityFileWatcher(Path path, ClientIdentityLoader loader, FilePasswordProvider provider) {
-        this(path, loader, provider, true);
-    }
-
-    public ClientIdentityFileWatcher(Path path, ClientIdentityLoader loader, FilePasswordProvider provider, boolean strict) {
-        this(path,
-             GenericUtils.supplierOf(Objects.requireNonNull(loader, "No client identity loader")),
-             GenericUtils.supplierOf(Objects.requireNonNull(provider, "No password provider")),
-             strict);
-    }
-
-    public ClientIdentityFileWatcher(Path path, Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider) {
-        this(path, loader, provider, true);
-    }
-
-    public ClientIdentityFileWatcher(Path path, Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider, boolean strict) {
-        super(path);
-        this.loaderHolder = Objects.requireNonNull(loader, "No client identity loader");
-        this.providerHolder = Objects.requireNonNull(provider, "No password provider");
-        this.strict = strict;
-    }
-
-    public final boolean isStrict() {
-        return strict;
-    }
-
-    public final ClientIdentityLoader getClientIdentityLoader() {
-        return loaderHolder.get();
-    }
-
-    public final FilePasswordProvider getFilePasswordProvider() {
-        return providerHolder.get();
-    }
-
-    @Override
-    public KeyPair getClientIdentity() throws IOException, GeneralSecurityException {
-        if (checkReloadRequired()) {
-            KeyPair kp = identityHolder.getAndSet(null);     // start fresh
-            Path path = getPath();
-
-            if (exists()) {
-                KeyPair id = reloadClientIdentity(path);
-                if (!KeyUtils.compareKeyPairs(kp, id)) {
-                    if (log.isDebugEnabled()) {
-                        log.debug("getClientIdentity({}) identity {}", path, (kp == null) ? "loaded" : "re-loaded");
-                    }
-                }
-
-                updateReloadAttributes();
-                identityHolder.set(id);
-            }
-        }
-
-        return identityHolder.get();
-    }
-
-    protected KeyPair reloadClientIdentity(Path path) throws IOException, GeneralSecurityException {
-        if (isStrict()) {
-            Map.Entry<String, Object> violation = KeyUtils.validateStrictKeyFilePermissions(path, IoUtils.EMPTY_LINK_OPTIONS);
-            if (violation != null) {
-                if (log.isDebugEnabled()) {
-                    log.debug("reloadClientIdentity({}) ignore due to {}", path, violation.getKey());
-                }
-                return null;
-            }
-        }
-
-        String location = path.toString();
-        ClientIdentityLoader idLoader = Objects.requireNonNull(getClientIdentityLoader(), "No client identity loader");
-        if (idLoader.isValidLocation(location)) {
-            KeyPair kp = idLoader.loadClientIdentity(location, Objects.requireNonNull(getFilePasswordProvider(), "No file password provider"));
-            if (log.isTraceEnabled()) {
-                PublicKey key = (kp == null) ? null : kp.getPublic();
-                if (key != null) {
-                    log.trace("reloadClientIdentity({}) loaded {}-{}",
-                              location, KeyUtils.getKeyType(key), KeyUtils.getFingerPrint(key));
-
-                } else {
-                    log.trace("reloadClientIdentity({}) no key loaded", location);
-                }
-            }
-
-            return kp;
-        }
-
-        if (log.isDebugEnabled()) {
-            log.debug("reloadClientIdentity({}) invalid location", location);
-        }
-
-        return null;
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityLoader.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityLoader.java b/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityLoader.java
deleted file mode 100644
index 8b3a295..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityLoader.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.client.config.keys;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface ClientIdentityLoader {
-    /**
-     * <P>A default implementation that assumes a file location that <U>must</U> exist.</P>
-     *
-     * <P>
-     * <B>Note:</B> It calls {@link SecurityUtils#loadKeyPairIdentity(String, InputStream, FilePasswordProvider)}
-     * </P>
-     */
-    ClientIdentityLoader DEFAULT = new ClientIdentityLoader() {
-        @Override
-        public boolean isValidLocation(String location) throws IOException {
-            Path path = toPath(location);
-            return Files.exists(path, IoUtils.EMPTY_LINK_OPTIONS);
-        }
-
-        @Override
-        public KeyPair loadClientIdentity(String location, FilePasswordProvider provider) throws IOException, GeneralSecurityException {
-            Path path = toPath(location);
-            try (InputStream inputStream = Files.newInputStream(path, IoUtils.EMPTY_OPEN_OPTIONS)) {
-                return SecurityUtils.loadKeyPairIdentity(path.toString(), inputStream, provider);
-            }
-        }
-
-        @Override
-        public String toString() {
-            return "DEFAULT";
-        }
-
-        private Path toPath(String location) {
-            Path path = Paths.get(ValidateUtils.checkNotNullAndNotEmpty(location, "No location"));
-            path = path.toAbsolutePath();
-            path = path.normalize();
-            return path;
-        }
-    };
-
-    /**
-     * @param location The identity key-pair location - the actual meaning (file, URL, etc.)
-     * depends on the implementation.
-     * @return {@code true} if it represents a valid location - the actual meaning of
-     * the validity depends on the implementation
-     * @throws IOException If failed to validate the location
-     */
-    boolean isValidLocation(String location) throws IOException;
-
-    /**
-     * @param location The identity key-pair location - the actual meaning (file, URL, etc.)
-     * depends on the implementation.
-     * @param provider The {@link FilePasswordProvider} to consult if the location contains
-     * an encrypted identity
-     * @return The loaded {@link KeyPair} - {@code null} if location is empty
-     * and it is OK that it does not exist
-     * @throws IOException If failed to access / process the remote location
-     * @throws GeneralSecurityException If failed to convert the contents into
-     * a valid identity
-     */
-    KeyPair loadClientIdentity(String location, FilePasswordProvider provider) throws IOException, GeneralSecurityException;
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityProvider.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityProvider.java b/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityProvider.java
deleted file mode 100644
index bda4700..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityProvider.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.client.config.keys;
-
-import java.io.IOException;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FunctionalInterface
-public interface ClientIdentityProvider {
-    /**
-     * Provides a {@link KeyPair} representing the client identity
-     *
-     * @return The client identity - may be {@code null} if no currently
-     * available identity from this provider. <B>Note:</B> the provider
-     * may return a <U>different</U> value every time this method is called
-     * - e.g., if it is (re-)loading contents from a file.
-     * @throws IOException If failed to load the identity
-     * @throws GeneralSecurityException If failed to parse the identity
-     */
-    KeyPair getClientIdentity() throws IOException, GeneralSecurityException;
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/config/keys/DefaultClientIdentitiesWatcher.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/keys/DefaultClientIdentitiesWatcher.java b/sshd-core/src/main/java/org/apache/sshd/client/config/keys/DefaultClientIdentitiesWatcher.java
deleted file mode 100644
index 3afa129..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/keys/DefaultClientIdentitiesWatcher.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.client.config.keys;
-
-import java.nio.file.Path;
-import java.util.List;
-import java.util.Objects;
-import java.util.function.Supplier;
-
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.config.keys.PublicKeyEntry;
-import org.apache.sshd.common.util.GenericUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class DefaultClientIdentitiesWatcher extends BuiltinClientIdentitiesWatcher {
-    public DefaultClientIdentitiesWatcher(ClientIdentityLoader loader, FilePasswordProvider provider) {
-        this(loader, provider, true);
-    }
-
-    public DefaultClientIdentitiesWatcher(ClientIdentityLoader loader, FilePasswordProvider provider, boolean strict) {
-        this(true, loader, provider, strict);
-    }
-
-    public DefaultClientIdentitiesWatcher(boolean supportedOnly, ClientIdentityLoader loader, FilePasswordProvider provider, boolean strict) {
-        this(supportedOnly,
-             GenericUtils.supplierOf(Objects.requireNonNull(loader, "No client identity loader")),
-             GenericUtils.supplierOf(Objects.requireNonNull(provider, "No password provider")),
-             strict);
-    }
-
-    public DefaultClientIdentitiesWatcher(Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider) {
-        this(loader, provider, true);
-    }
-
-    public DefaultClientIdentitiesWatcher(Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider, boolean strict) {
-        this(true, loader, provider, strict);
-    }
-
-    public DefaultClientIdentitiesWatcher(boolean supportedOnly,
-            Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider, boolean strict) {
-        super(PublicKeyEntry.getDefaultKeysFolderPath(), supportedOnly, loader, provider, strict);
-    }
-
-    public static List<Path> getDefaultBuiltinIdentitiesPaths() {
-        return getDefaultBuiltinIdentitiesPaths(PublicKeyEntry.getDefaultKeysFolderPath());
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/keyverifier/KnownHostsServerKeyVerifier.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/keyverifier/KnownHostsServerKeyVerifier.java b/sshd-core/src/main/java/org/apache/sshd/client/keyverifier/KnownHostsServerKeyVerifier.java
index 7a5052d..c4cb849 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/keyverifier/KnownHostsServerKeyVerifier.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/keyverifier/KnownHostsServerKeyVerifier.java
@@ -46,7 +46,7 @@ import org.apache.sshd.client.session.ClientSession;
 import org.apache.sshd.common.Factory;
 import org.apache.sshd.common.FactoryManager;
 import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.config.SshConfigFileReader;
+import org.apache.sshd.common.config.ConfigFileReaderSupport;
 import org.apache.sshd.common.config.keys.AuthorizedKeyEntry;
 import org.apache.sshd.common.config.keys.KeyUtils;
 import org.apache.sshd.common.config.keys.PublicKeyEntry;
@@ -350,7 +350,7 @@ public class KnownHostsServerKeyVerifier
                         continue;
                     }
 
-                    int pos = line.indexOf(SshConfigFileReader.COMMENT_CHAR);
+                    int pos = line.indexOf(ConfigFileReaderSupport.COMMENT_CHAR);
                     if (pos == 0) {
                         lines.add(line);
                         continue;
@@ -703,7 +703,7 @@ public class KnownHostsServerKeyVerifier
                 KnownHostHashValue.append(sb, digester, salt, digestValue);
             } else {
                 int portValue = hostIdentity.getPort();
-                boolean nonDefaultPort = (portValue > 0) && (portValue != SshConfigFileReader.DEFAULT_PORT);
+                boolean nonDefaultPort = (portValue > 0) && (portValue != ConfigFileReaderSupport.DEFAULT_PORT);
                 if (nonDefaultPort) {
                     sb.append(HostPatternsHolder.NON_STANDARD_PORT_PATTERN_ENCLOSURE_START_DELIM);
                 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSession.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSession.java b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSession.java
index 1d68a20..31202bb 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSession.java
@@ -27,14 +27,17 @@ import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
 import java.rmi.RemoteException;
 import java.rmi.ServerException;
+import java.security.KeyPair;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.EnumSet;
+import java.util.Iterator;
 import java.util.Map;
 import java.util.Set;
 
 import org.apache.sshd.client.ClientAuthenticationManager;
 import org.apache.sshd.client.ClientFactoryManager;
+import org.apache.sshd.client.auth.password.PasswordIdentityProvider;
 import org.apache.sshd.client.channel.ChannelDirectTcpip;
 import org.apache.sshd.client.channel.ChannelExec;
 import org.apache.sshd.client.channel.ChannelShell;
@@ -46,6 +49,7 @@ import org.apache.sshd.client.session.forward.DynamicPortForwardingTracker;
 import org.apache.sshd.client.session.forward.ExplicitPortForwardingTracker;
 import org.apache.sshd.common.forward.PortForwardingManager;
 import org.apache.sshd.common.future.KeyExchangeFuture;
+import org.apache.sshd.common.keyprovider.KeyIdentityProvider;
 import org.apache.sshd.common.session.Session;
 import org.apache.sshd.common.util.io.NoCloseOutputStream;
 import org.apache.sshd.common.util.io.NullOutputStream;
@@ -353,4 +357,54 @@ public interface ClientSession
      * @throws IOException if a key exchange is already running
      */
     KeyExchangeFuture switchToNoneCipher() throws IOException;
+
+    /**
+     * Creates a &quot;unified&quot; {@link KeyIdentityProvider} of key pairs out of the registered
+     * {@link KeyPair} identities and the extra available ones as a single iterator
+     * of key pairs
+     *
+     *
+     * @param session The {@link ClientSession} - ignored if {@code null} (i.e., empty
+     * iterator returned)
+     * @return The wrapping KeyIdentityProvider
+     * @see ClientSession#getRegisteredIdentities()
+     * @see ClientSession#getKeyPairProvider()
+     */
+    static KeyIdentityProvider providerOf(ClientSession session) {
+        return session == null
+            ? KeyIdentityProvider.EMPTY_KEYS_PROVIDER
+            : KeyIdentityProvider.resolveKeyIdentityProvider(
+                    session.getRegisteredIdentities(), session.getKeyPairProvider());
+    }
+
+    /**
+     * Creates a &quot;unified&quot; {@link Iterator} of key pairs out of the registered
+     * {@link KeyPair} identities and the extra available ones as a single iterator
+     * of key pairs
+     *
+     * @param session The {@link ClientSession} - ignored if {@code null} (i.e., empty
+     * iterator returned)
+     * @return The wrapping iterator
+     * @see ClientSession#getRegisteredIdentities()
+     * @see ClientSession#getKeyPairProvider()
+     */
+    static Iterator<KeyPair> keyPairIteratorOf(ClientSession session) {
+        return KeyIdentityProvider.iteratorOf(providerOf(session));
+    }
+
+    /**
+     * Creates a &quot;unified&quot; {@link Iterator} of passwords out of the registered
+     * passwords and the extra available ones as a single iterator of passwords
+     *
+     * @param session The {@link ClientSession} - ignored if {@code null} (i.e., empty
+     * iterator returned)
+     * @return The wrapping iterator
+     * @see ClientSession#getRegisteredIdentities()
+     * @see ClientSession#getPasswordIdentityProvider()
+     */
+    static Iterator<String> passwordIteratorOf(ClientSession session) {
+        return (session == null)
+            ? Collections.<String>emptyIterator()
+            : PasswordIdentityProvider.iteratorOf(session.getRegisteredIdentities(), session.getPasswordIdentityProvider());
+    }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/simple/SimpleClientConfigurator.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/simple/SimpleClientConfigurator.java b/sshd-core/src/main/java/org/apache/sshd/client/simple/SimpleClientConfigurator.java
index 58913b4..31a994b 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/simple/SimpleClientConfigurator.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/simple/SimpleClientConfigurator.java
@@ -19,7 +19,7 @@
 
 package org.apache.sshd.client.simple;
 
-import org.apache.sshd.common.config.SshConfigFileReader;
+import org.apache.sshd.common.config.ConfigFileReaderSupport;
 
 /**
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
@@ -35,7 +35,7 @@ public interface SimpleClientConfigurator {
      */
     long DEFAULT_AUTHENTICATION_TIMEOUT = Long.MAX_VALUE;   // virtually infinite
 
-    int DEFAULT_PORT = SshConfigFileReader.DEFAULT_PORT;
+    int DEFAULT_PORT = ConfigFileReaderSupport.DEFAULT_PORT;
 
     /**
      * @return Current connect timeout (msec.) - always positive

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/AttributeStore.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/AttributeStore.java b/sshd-core/src/main/java/org/apache/sshd/common/AttributeStore.java
deleted file mode 100644
index 2b58d94..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/AttributeStore.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.common;
-
-import java.util.Objects;
-
-import org.apache.sshd.common.channel.Channel;
-import org.apache.sshd.common.session.Session;
-
-/**
- * Provides the capability to attach in-memory attributes to the entity
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface AttributeStore {
-    /**
-     * <P>
-     * Type safe key for storage of user attributes. Typically it is used as a static
-     * variable that is shared between the producer and the consumer. To further
-     * restrict access the setting or getting it from the store one can add static
-     * {@code get/set methods} e.g:
-     * </P>
-     *
-     * <pre>
-     * public static final AttributeKey&lt;MyValue&gt; MY_KEY = new AttributeKey&lt;MyValue&gt;();
-     *
-     * public static MyValue getMyValue(Session s) {
-     *   return s.getAttribute(MY_KEY);
-     * }
-     *
-     * public static void setMyValue(Session s, MyValue value) {
-     *   s.setAttribute(MY_KEY, value);
-     * }
-     * </pre>
-     *
-     * @param <T> type of value stored in the attribute.
-     * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
-     */
-    // CHECKSTYLE:OFF
-    class AttributeKey<T> {
-        public AttributeKey() {
-            super();
-        }
-    }
-    // CHECKSTYLE:ON
-
-    /**
-     * Returns the value of the user-defined attribute.
-     *
-     * @param <T> The generic attribute type
-     * @param key The key of the attribute; must not be {@code null}.
-     * @return {@code null} if there is no value associated with the specified key
-     */
-    <T> T getAttribute(AttributeKey<T> key);
-
-    /**
-     * Sets a user-defined attribute.
-     *
-     * @param <T>   The generic attribute type
-     * @param key   The key of the attribute; must not be {@code null}.
-     * @param value The value of the attribute; must not be {@code null}.
-     * @return The old value of the attribute; {@code null} if it is new.
-     */
-    <T> T setAttribute(AttributeKey<T> key, T value);
-
-    /**
-     * Removes the user-defined attribute
-     *
-     * @param <T> The generic attribute type
-     * @param key The key of the attribute; must not be {@code null}.
-     * @return The removed value; {@code null} if no previous value
-     */
-    <T> T removeAttribute(AttributeKey<T> key);
-
-    /**
-     * Attempts to resolve the associated value by going up the store's
-     * hierarchy (if any)
-     *
-     * @param <T> The generic attribute type
-     * @param key The key of the attribute; must not be {@code null}.
-     * @return {@code null} if there is no value associated with the specified key
-     */
-    <T> T resolveAttribute(AttributeKey<T> key);
-
-    /**
-     * @param <T> The generic attribute type
-     * @param manager The {@link FactoryManager} - ignored if {@code null}
-     * @param key The attribute key - never {@code null}
-     * @return Associated value - {@code null} if not found
-     */
-    static <T> T resolveAttribute(FactoryManager manager, AttributeKey<T> key) {
-        Objects.requireNonNull(key, "No key");
-        return (manager == null) ? null : manager.getAttribute(key);
-    }
-
-    /**
-     * Attempts to use the session's attribute, if not found then tries the factory manager
-     *
-     * @param <T> The generic attribute type
-     * @param session The {@link Session} - ignored if {@code null}
-     * @param key The attribute key - never {@code null}
-     * @return Associated value - {@code null} if not found
-     * @see Session#getFactoryManager()
-     * @see #resolveAttribute(FactoryManager, AttributeKey)
-     */
-    static <T> T resolveAttribute(Session session, AttributeKey<T> key) {
-        Objects.requireNonNull(key, "No key");
-        if (session == null) {
-            return null;
-        }
-
-        T value = session.getAttribute(key);
-        return (value != null) ? value : resolveAttribute(session.getFactoryManager(), key);
-    }
-
-    /**
-     * Attempts to use the channel attribute, if not found then tries the session
-     *
-     * @param <T> The generic attribute type
-     * @param channel The {@link Channel} - ignored if {@code null}
-     * @param key The attribute key - never {@code null}
-     * @return Associated value - {@code null} if not found
-     * @see Session#getFactoryManager()
-     * @see #resolveAttribute(Session, AttributeKey)
-     */
-    static <T> T resolveAttribute(Channel channel, AttributeKey<T> key) {
-        Objects.requireNonNull(key, "No key");
-        if (channel == null) {
-            return null;
-        }
-
-        T value = channel.getAttribute(key);
-        return (value != null) ? value : resolveAttribute(channel.getSession(), key);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/BuiltinFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/BuiltinFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/BuiltinFactory.java
deleted file mode 100644
index 33b53a9..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/BuiltinFactory.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sshd.common;
-
-import java.util.Collection;
-import java.util.List;
-import java.util.stream.Collectors;
-
-import org.apache.sshd.common.util.GenericUtils;
-
-/**
- * A named optional factory.
- *
- * @param <T> The create object instance type
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface BuiltinFactory<T> extends NamedFactory<T>, OptionalFeature {
-    static <T, E extends BuiltinFactory<T>> List<NamedFactory<T>> setUpFactories(
-            boolean ignoreUnsupported, Collection<? extends E> preferred) {
-        return GenericUtils.stream(preferred)
-                .filter(f -> ignoreUnsupported || f.isSupported())
-                .collect(Collectors.toList());
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/Closeable.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/Closeable.java b/sshd-core/src/main/java/org/apache/sshd/common/Closeable.java
deleted file mode 100644
index 6a6f622..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/Closeable.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sshd.common;
-
-import java.io.IOException;
-import java.net.SocketTimeoutException;
-import java.nio.channels.Channel;
-import java.util.concurrent.TimeUnit;
-
-import org.apache.sshd.common.future.CloseFuture;
-import org.apache.sshd.common.future.SshFutureListener;
-
-/**
- * A {@code Closeable} is a resource that can be closed.
- * The close method is invoked to release resources that the object is
- * holding. The user can pre-register listeners to be notified
- * when resource close is completed (successfully or otherwise)
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface Closeable extends Channel {
-
-    /**
-     * Timeout (milliseconds) for waiting on a {@link CloseFuture} to successfully
-     * complete its action.
-     * @see #DEFAULT_CLOSE_WAIT_TIMEOUT
-     */
-    String CLOSE_WAIT_TIMEOUT = "sshd-close-wait-time";
-
-    /**
-     * Default value for {@link #CLOSE_WAIT_TIMEOUT} if none specified
-     */
-    long DEFAULT_CLOSE_WAIT_TIMEOUT = TimeUnit.SECONDS.toMillis(15L);
-
-    /**
-     * Close this resource asynchronously and return a future.
-     * Resources support two closing modes: a graceful mode
-     * which will cleanly close the resource and an immediate mode
-     * which will close the resources abruptly.
-     *
-     * @param immediately <code>true</code> if the resource should be shut down abruptly,
-     *                    <code>false</code> for a graceful close
-     * @return a {@link CloseFuture} representing the close request
-     */
-    CloseFuture close(boolean immediately);
-
-    /**
-     * Pre-register a listener to be informed when resource is closed. If
-     * resource is already closed, the listener will be invoked immediately
-     * and not registered for future notification
-     *
-     * @param listener The notification {@link SshFutureListener} - never {@code null}
-     */
-    void addCloseFutureListener(SshFutureListener<CloseFuture> listener);
-
-    /**
-     * Remove a pre-registered close event listener
-     *
-     * @param listener The register {@link SshFutureListener} - never {@code null}.
-     * Ignored if not registered or resource already closed
-     */
-    void removeCloseFutureListener(SshFutureListener<CloseFuture> listener);
-
-    /**
-     * Returns <code>true</code> if this object has been closed.
-     *
-     * @return <code>true</code> if closing
-     */
-    boolean isClosed();
-
-    /**
-     * Returns <code>true</code> if the {@link #close(boolean)} method
-     * has been called. Note that this method will return <code>true</code>
-     * even if this {@link #isClosed()} returns <code>true</code>.
-     *
-     * @return <code>true</code> if closing
-     */
-    boolean isClosing();
-
-    @Override
-    default boolean isOpen() {
-        return !(isClosed() || isClosing());
-    }
-
-    @Override
-    default void close() throws IOException {
-        Closeable.close(this);
-    }
-
-    static long getMaxCloseWaitTime(PropertyResolver resolver) {
-        return (resolver == null) ? DEFAULT_CLOSE_WAIT_TIMEOUT
-                : resolver.getLongProperty(CLOSE_WAIT_TIMEOUT, DEFAULT_CLOSE_WAIT_TIMEOUT);
-    }
-
-    static void close(Closeable closeable) throws IOException {
-        if (closeable == null) {
-            return;
-        }
-
-        if ((!closeable.isClosed()) && (!closeable.isClosing())) {
-            CloseFuture future = closeable.close(true);
-            long maxWait = (closeable instanceof PropertyResolver)
-                    ? getMaxCloseWaitTime((PropertyResolver) closeable) : DEFAULT_CLOSE_WAIT_TIMEOUT;
-            boolean successful = future.await(maxWait);
-            if (!successful) {
-                throw new SocketTimeoutException("Failed to receive closure confirmation within " + maxWait + " millis");
-            }
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/Factory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/Factory.java b/sshd-core/src/main/java/org/apache/sshd/common/Factory.java
deleted file mode 100644
index 93b351c..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/Factory.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sshd.common;
-
-import java.util.function.Supplier;
-
-/**
- * Factory is a simple interface that is used to create other objects.
- *
- * @param <T> type of object this factory will create
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FunctionalInterface
-public interface Factory<T> extends Supplier<T> {
-
-    @Override
-    default T get() {
-        return create();
-    }
-
-    /**
-     * @return A new instance
-     */
-    T create();
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/FactoryManager.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/FactoryManager.java b/sshd-core/src/main/java/org/apache/sshd/common/FactoryManager.java
index 31f0de6..22c7d49 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/FactoryManager.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/FactoryManager.java
@@ -19,6 +19,7 @@
 package org.apache.sshd.common;
 
 import java.util.List;
+import java.util.Objects;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
 
@@ -467,4 +468,19 @@ public interface FactoryManager
      */
     List<RequestHandler<ConnectionService>> getGlobalRequestHandlers();
 
+    @Override
+    default <T> T resolveAttribute(AttributeKey<T> key) {
+        return resolveAttribute(this, key);
+    }
+
+    /**
+     * @param <T> The generic attribute type
+     * @param manager The {@link FactoryManager} - ignored if {@code null}
+     * @param key The attribute key - never {@code null}
+     * @return Associated value - {@code null} if not found
+     */
+    static <T> T resolveAttribute(FactoryManager manager, AttributeKey<T> key) {
+        Objects.requireNonNull(key, "No key");
+        return (manager == null) ? null : manager.getAttribute(key);
+    }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/NamedFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/NamedFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/NamedFactory.java
deleted file mode 100644
index 5386447..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/NamedFactory.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sshd.common;
-
-import java.util.Collection;
-import java.util.List;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-
-/**
- * A named factory is a factory identified by a name.
- * Such names are used mainly in the algorithm negotiation at the beginning of the SSH connection.
- *
- * @param <T> The create object instance type
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface NamedFactory<T> extends Factory<T>, NamedResource {
-    /**
-     * Create an instance of the specified name by looking up the needed factory
-     * in the list.
-     *
-     * @param factories list of available factories
-     * @param name      the factory name to use
-     * @param <T>       type of object to create
-     * @return a newly created object or {@code null} if the factory is not in the list
-     */
-    static <T> T create(Collection<? extends NamedFactory<? extends T>> factories, String name) {
-        NamedFactory<? extends T> f = NamedResource.findByName(name, String.CASE_INSENSITIVE_ORDER, factories);
-        if (f != null) {
-            return f.create();
-        } else {
-            return null;
-        }
-    }
-
-    static <S extends OptionalFeature, T, E extends NamedFactory<T>> List<NamedFactory<T>> setUpTransformedFactories(
-            boolean ignoreUnsupported, Collection<? extends S> preferred, Function<? super S, ? extends E> xform) {
-        return preferred.stream()
-            .filter(f -> ignoreUnsupported || f.isSupported())
-            .map(xform)
-            .collect(Collectors.toList());
-    }
-
-    static <T, E extends NamedFactory<T> & OptionalFeature> List<NamedFactory<T>> setUpBuiltinFactories(
-            boolean ignoreUnsupported, Collection<? extends E> preferred) {
-        return preferred.stream()
-            .filter(f -> ignoreUnsupported || f.isSupported())
-            .collect(Collectors.toList());
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/NamedResource.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/NamedResource.java b/sshd-core/src/main/java/org/apache/sshd/common/NamedResource.java
deleted file mode 100644
index 813f53d..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/NamedResource.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.common;
-
-import java.util.Collection;
-import java.util.Comparator;
-import java.util.List;
-import java.util.function.Function;
-
-import org.apache.sshd.common.util.GenericUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FunctionalInterface
-public interface NamedResource {
-
-    /**
-     * Returns the value of {@link #getName()} - or {@code null} if argument is {@code null}
-     */
-    Function<NamedResource, String> NAME_EXTRACTOR = input -> input == null ? null : input.getName();
-
-    /**
-     * Compares 2 {@link NamedResource}s according to their {@link #getName()}
-     * value case <U>insensitive</U>
-     */
-    Comparator<NamedResource> BY_NAME_COMPARATOR = Comparator.comparing(NAME_EXTRACTOR, String.CASE_INSENSITIVE_ORDER);
-
-    /**
-     * @return The resource name
-     */
-    String getName();
-
-    /**
-     * @param resources The named resources
-     * @return A {@link List} of all the factories names - in same order
-     * as they appear in the input collection
-     */
-    static List<String> getNameList(Collection<? extends NamedResource> resources) {
-        return GenericUtils.map(resources, NamedResource::getName);
-    }
-
-    /**
-     * @param resources list of available resources
-     * @return A comma separated list of factory names
-     */
-    static String getNames(Collection<? extends NamedResource> resources) {
-        Collection<String> nameList = getNameList(resources);
-        return GenericUtils.join(nameList, ',');
-    }
-
-    /**
-     * Remove the resource identified by the name from the list.
-     *
-     * @param <R>       The generic resource type
-     * @param name      Name of the resource - ignored if {@code null}/empty
-     * @param c         The {@link Comparator} to decide whether the {@link NamedResource#getName()}
-     *                  matches the <tt>name</tt> parameter
-     * @param resources The {@link NamedResource} to check - ignored if {@code null}/empty
-     * @return the removed resource from the list or {@code null} if not in the list
-     */
-    static <R extends NamedResource> R removeByName(String name, Comparator<? super String> c, Collection<? extends R> resources) {
-        R r = findByName(name, c, resources);
-        if (r != null) {
-            resources.remove(r);
-        }
-        return r;
-    }
-
-    /**
-     * @param <R>       The generic resource type
-     * @param name      Name of the resource - ignored if {@code null}/empty
-     * @param c         The {@link Comparator} to decide whether the {@link NamedResource#getName()}
-     *                  matches the <tt>name</tt> parameter
-     * @param resources The {@link NamedResource} to check - ignored if {@code null}/empty
-     * @return The <U>first</U> resource whose name matches the parameter (by invoking
-     * {@link Comparator#compare(Object, Object)} - {@code null} if no match found
-     */
-    static <R extends NamedResource> R findByName(String name, Comparator<? super String> c, Collection<? extends R> resources) {
-        return GenericUtils.isEmpty(name)
-            ? null
-            : GenericUtils.stream(resources)
-                .filter(r -> c.compare(name, r.getName()) == 0)
-                .findFirst()
-                .orElse(null);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/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
deleted file mode 100644
index 1afa864..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/OptionalFeature.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.common;
-
-import java.util.Collection;
-
-import org.apache.sshd.common.util.GenericUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FunctionalInterface
-public interface OptionalFeature {
-    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();
-
-    static OptionalFeature of(boolean supported) {
-        return supported ? TRUE : FALSE;
-    }
-
-    static OptionalFeature all(Collection<? extends OptionalFeature> features) {
-        return () -> {
-            if (GenericUtils.isEmpty(features)) {
-                return false;
-            }
-
-            for (OptionalFeature f : features) {
-                if (!f.isSupported()) {
-                    return false;
-                }
-            }
-
-            return true;
-        };
-    }
-
-    static OptionalFeature any(Collection<? extends OptionalFeature> features) {
-        return () -> {
-            if (GenericUtils.isEmpty(features)) {
-                return false;
-            }
-
-            for (OptionalFeature f : features) {
-                if (f.isSupported()) {
-                    return true;
-                }
-            }
-
-            return false;
-        };
-    }
-}


[49/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common utilities code from sshd-core into sshd-common (new artifact)

Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/KnownHostHashValue.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/KnownHostHashValue.java b/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/KnownHostHashValue.java
new file mode 100644
index 0000000..28a3d1f
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/KnownHostHashValue.java
@@ -0,0 +1,170 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.client.config.hosts;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.Base64;
+import java.util.Objects;
+
+import org.apache.sshd.common.Factory;
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.RuntimeSshException;
+import org.apache.sshd.common.mac.Mac;
+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;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class KnownHostHashValue {
+    /**
+     * Character used to indicate a hashed host pattern
+     */
+    public static final char HASHED_HOST_DELIMITER = '|';
+
+    public static final NamedFactory<Mac> DEFAULT_DIGEST = KnownHostDigest.SHA1;
+
+    private NamedFactory<Mac> digester = DEFAULT_DIGEST;
+    private byte[] saltValue;
+    private byte[] digestValue;
+
+    public KnownHostHashValue() {
+        super();
+    }
+
+    public NamedFactory<Mac> getDigester() {
+        return digester;
+    }
+
+    public void setDigester(NamedFactory<Mac> digester) {
+        this.digester = digester;
+    }
+
+    public byte[] getSaltValue() {
+        return saltValue;
+    }
+
+    public void setSaltValue(byte[] saltValue) {
+        this.saltValue = saltValue;
+    }
+
+    public byte[] getDigestValue() {
+        return digestValue;
+    }
+
+    public void setDigestValue(byte[] digestValue) {
+        this.digestValue = digestValue;
+    }
+
+    /**
+     * Checks if the host matches the hash
+     *
+     * @param host The host name/address - ignored if {@code null}/empty
+     * @return {@code true} if host matches the hash
+     * @throws RuntimeException If entry not properly initialized
+     */
+    public boolean isHostMatch(String host) {
+        if (GenericUtils.isEmpty(host)) {
+            return false;
+        }
+
+        try {
+            byte[] expected = getDigestValue();
+            byte[] actual = calculateHashValue(host, getDigester(), getSaltValue());
+            return Arrays.equals(expected, actual);
+        } catch (Throwable t) {
+            if (t instanceof RuntimeException) {
+                throw (RuntimeException) t;
+            }
+            throw new RuntimeSshException("Failed (" + t.getClass().getSimpleName() + ")"
+                    + " to calculate hash value: " + t.getMessage(), t);
+        }
+    }
+
+    @Override
+    public String toString() {
+        if ((getDigester() == null) || NumberUtils.isEmpty(getSaltValue()) || NumberUtils.isEmpty(getDigestValue())) {
+            return Objects.toString(getDigester(), null)
+                 + "-" + BufferUtils.toHex(':', getSaltValue())
+                 + "-" + BufferUtils.toHex(':', getDigestValue());
+        }
+
+        try {
+            return append(new StringBuilder(Byte.MAX_VALUE), this).toString();
+        } catch (IOException | RuntimeException e) {    // unexpected
+            return e.getClass().getSimpleName() + ": " + e.getMessage();
+        }
+    }
+
+    // see http://nms.lcs.mit.edu/projects/ssh/README.hashed-hosts
+    public static byte[] calculateHashValue(String host, Factory<? extends Mac> factory, byte[] salt) throws Exception {
+        return calculateHashValue(host, factory.create(), salt);
+    }
+
+    public static byte[] calculateHashValue(String host, Mac mac, byte[] salt) throws Exception {
+        mac.init(salt);
+
+        byte[] hostBytes = host.getBytes(StandardCharsets.UTF_8);
+        mac.update(hostBytes);
+        return mac.doFinal();
+    }
+
+    public static <A extends Appendable> A append(A sb, KnownHostHashValue hashValue) throws IOException {
+        return (hashValue == null) ? sb : append(sb, hashValue.getDigester(), hashValue.getSaltValue(), hashValue.getDigestValue());
+    }
+
+    public static <A extends Appendable> A append(A sb, NamedResource factory, byte[] salt, byte[] digest) throws IOException {
+        Base64.Encoder encoder = Base64.getEncoder();
+        sb.append(HASHED_HOST_DELIMITER).append(factory.getName());
+        sb.append(HASHED_HOST_DELIMITER).append(encoder.encodeToString(salt));
+        sb.append(HASHED_HOST_DELIMITER).append(encoder.encodeToString(digest));
+        return sb;
+    }
+
+    public static KnownHostHashValue parse(String patternString) {
+        String pattern = GenericUtils.replaceWhitespaceAndTrim(patternString);
+        return parse(pattern, GenericUtils.isEmpty(pattern) ? null : new KnownHostHashValue());
+    }
+
+    public static <V extends KnownHostHashValue> V parse(String patternString, V value) {
+        String pattern = GenericUtils.replaceWhitespaceAndTrim(patternString);
+        if (GenericUtils.isEmpty(pattern)) {
+            return value;
+        }
+
+        String[] components = GenericUtils.split(pattern, HASHED_HOST_DELIMITER);
+        ValidateUtils.checkTrue(components.length == 4 /* 1st one is empty */, "Invalid hash pattern (insufficient data): %s", pattern);
+        ValidateUtils.checkTrue(GenericUtils.isEmpty(components[0]), "Invalid hash pattern (unexpected extra data): %s", pattern);
+
+        NamedFactory<Mac> factory =
+                ValidateUtils.checkNotNull(KnownHostDigest.fromName(components[1]),
+                        "Invalid hash pattern (unknown digest): %s", pattern);
+        Base64.Decoder decoder = Base64.getDecoder();
+        value.setDigester(factory);
+        value.setSaltValue(decoder.decode(components[2]));
+        value.setDigestValue(decoder.decode(components[3]));
+        return value;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcher.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcher.java b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcher.java
new file mode 100644
index 0000000..ab9929b
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcher.java
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.client.config.keys;
+
+import java.nio.file.Path;
+import java.security.KeyPair;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Supplier;
+
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.config.keys.BuiltinIdentities;
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class BuiltinClientIdentitiesWatcher extends ClientIdentitiesWatcher {
+    private final boolean supportedOnly;
+
+    public BuiltinClientIdentitiesWatcher(Path keysFolder, boolean supportedOnly,
+            ClientIdentityLoader loader, FilePasswordProvider provider, boolean strict) {
+        this(keysFolder, NamedResource.getNameList(BuiltinIdentities.VALUES), supportedOnly, loader, provider, strict);
+    }
+
+    public BuiltinClientIdentitiesWatcher(Path keysFolder, Collection<String> ids, boolean supportedOnly,
+            ClientIdentityLoader loader, FilePasswordProvider provider, boolean strict) {
+        this(keysFolder, ids, supportedOnly,
+             GenericUtils.supplierOf(Objects.requireNonNull(loader, "No client identity loader")),
+             GenericUtils.supplierOf(Objects.requireNonNull(provider, "No password provider")),
+             strict);
+    }
+
+    public BuiltinClientIdentitiesWatcher(Path keysFolder, boolean supportedOnly,
+            Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider, boolean strict) {
+        this(keysFolder, NamedResource.getNameList(BuiltinIdentities.VALUES), supportedOnly, loader, provider, strict);
+    }
+
+    public BuiltinClientIdentitiesWatcher(Path keysFolder, Collection<String> ids, boolean supportedOnly,
+            Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider, boolean strict) {
+        super(getBuiltinIdentitiesPaths(keysFolder, ids), loader, provider, strict);
+        this.supportedOnly = supportedOnly;
+    }
+
+    public final boolean isSupportedOnly() {
+        return supportedOnly;
+    }
+
+    @Override
+    public Iterable<KeyPair> loadKeys() {
+        return isSupportedOnly() ? loadKeys(this::isSupported) : super.loadKeys();
+    }
+
+    private boolean isSupported(KeyPair kp) {
+        BuiltinIdentities id = BuiltinIdentities.fromKeyPair(kp);
+        if ((id != null) && id.isSupported()) {
+            return true;
+        }
+        if (log.isDebugEnabled()) {
+            log.debug("loadKeys - remove unsupported identity={}, key-type={}, key={}",
+                    id, KeyUtils.getKeyType(kp), KeyUtils.getFingerPrint(kp.getPublic()));
+        }
+        return false;
+    }
+
+    public static List<Path> getDefaultBuiltinIdentitiesPaths(Path keysFolder) {
+        return getBuiltinIdentitiesPaths(keysFolder, NamedResource.getNameList(BuiltinIdentities.VALUES));
+    }
+
+    public static List<Path> getBuiltinIdentitiesPaths(Path keysFolder, Collection<String> ids) {
+        Objects.requireNonNull(keysFolder, "No keys folder");
+        if (GenericUtils.isEmpty(ids)) {
+            return Collections.emptyList();
+        }
+
+        List<Path> paths = new ArrayList<>(ids.size());
+        for (String id : ids) {
+            String fileName = ClientIdentity.getIdentityFileName(id);
+            paths.add(keysFolder.resolve(fileName));
+        }
+
+        return paths;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentitiesWatcher.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentitiesWatcher.java b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentitiesWatcher.java
new file mode 100644
index 0000000..094766f
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentitiesWatcher.java
@@ -0,0 +1,139 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.client.config.keys;
+
+import java.nio.file.Path;
+import java.security.KeyPair;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+import java.util.stream.Stream;
+
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.keyprovider.AbstractKeyPairProvider;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * Watches over a group of files that contains client identities
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class ClientIdentitiesWatcher extends AbstractKeyPairProvider implements KeyPairProvider {
+    private final Collection<ClientIdentityProvider> providers;
+
+    public ClientIdentitiesWatcher(Collection<? extends Path> paths,
+            ClientIdentityLoader loader, FilePasswordProvider provider) {
+        this(paths, loader, provider, true);
+    }
+
+    public ClientIdentitiesWatcher(Collection<? extends Path> paths,
+            ClientIdentityLoader loader, FilePasswordProvider provider, boolean strict) {
+        this(paths,
+             GenericUtils.supplierOf(Objects.requireNonNull(loader, "No client identity loader")),
+             GenericUtils.supplierOf(Objects.requireNonNull(provider, "No password provider")),
+             strict);
+    }
+
+    public ClientIdentitiesWatcher(Collection<? extends Path> paths,
+            Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider) {
+        this(paths, loader, provider, true);
+    }
+
+    public ClientIdentitiesWatcher(Collection<? extends Path> paths,
+            Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider, boolean strict) {
+        this(buildProviders(paths, loader, provider, strict));
+    }
+
+    public ClientIdentitiesWatcher(Collection<ClientIdentityProvider> providers) {
+        this.providers = providers;
+    }
+
+    @Override
+    public Iterable<KeyPair> loadKeys() {
+        return loadKeys(null);
+    }
+
+    protected Iterable<KeyPair> loadKeys(Predicate<? super KeyPair> filter) {
+        return () -> {
+            Stream<KeyPair> stream = safeMap(GenericUtils.stream(providers), this::doGetKeyPair);
+            if (filter != null) {
+                stream = stream.filter(filter);
+            }
+            return stream.iterator();
+        };
+    }
+
+    /**
+     * Performs a mapping operation on the stream, discarding any null values
+     * returned by the mapper.
+     *
+     * @param <U> Original type
+     * @param <V> Mapped type
+     * @param stream Original values stream
+     * @param mapper Mapper to target type
+     * @return Mapped stream
+     */
+    protected <U, V> Stream<V> safeMap(Stream<U> stream, Function<? super U, ? extends V> mapper) {
+        return stream.map(u -> Optional.ofNullable(mapper.apply(u)))
+                .filter(Optional::isPresent)
+                .map(Optional::get);
+    }
+
+    protected KeyPair doGetKeyPair(ClientIdentityProvider p) {
+        try {
+            KeyPair kp = p.getClientIdentity();
+            if (kp == null) {
+                if (log.isDebugEnabled()) {
+                    log.debug("loadKeys({}) no key loaded", p);
+                }
+            }
+            return kp;
+        } catch (Throwable e) {
+            log.warn("loadKeys({}) failed ({}) to load key: {}", p, e.getClass().getSimpleName(), e.getMessage());
+            if (log.isDebugEnabled()) {
+                log.debug("loadKeys(" + p + ") key load failure details", e);
+            }
+            return null;
+        }
+    }
+
+    public static List<ClientIdentityProvider> buildProviders(
+            Collection<? extends Path> paths, ClientIdentityLoader loader, FilePasswordProvider provider, boolean strict) {
+        return buildProviders(paths,
+                GenericUtils.supplierOf(Objects.requireNonNull(loader, "No client identity loader")),
+                GenericUtils.supplierOf(Objects.requireNonNull(provider, "No password provider")),
+                strict);
+    }
+
+    public static List<ClientIdentityProvider> buildProviders(
+            Collection<? extends Path> paths, Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider, boolean strict) {
+        if (GenericUtils.isEmpty(paths)) {
+            return Collections.emptyList();
+        }
+
+        return GenericUtils.map(paths, p -> new ClientIdentityFileWatcher(p, loader, provider, strict));
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentity.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentity.java b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentity.java
new file mode 100644
index 0000000..931d96f
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentity.java
@@ -0,0 +1,268 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.client.config.keys;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.Path;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.function.Function;
+
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.config.keys.BuiltinIdentities;
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.IdentityUtils;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.config.keys.PublicKeyEntry;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.io.FileInfoExtractor;
+import org.apache.sshd.common.util.io.IoUtils;
+
+/**
+ * Provides keys loading capability from the user's keys folder - e.g., {@code id_rsa}
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ * @see org.apache.sshd.common.util.security.SecurityUtils#getKeyPairResourceParser()
+ */
+public final class ClientIdentity {
+
+    public static final String ID_FILE_PREFIX = "id_";
+
+    public static final String ID_FILE_SUFFIX = "";
+
+    public static final Function<String, String> ID_GENERATOR =
+            ClientIdentity::getIdentityFileName;
+
+    private ClientIdentity() {
+        throw new UnsupportedOperationException("No instance");
+    }
+
+    /**
+     * @param name The file name - ignored if {@code null}/empty
+     * @return The identity type - {@code null} if cannot determine it - e.g.,
+     * does not start with the {@link #ID_FILE_PREFIX}
+     */
+    public static String getIdentityType(String name) {
+        if (GenericUtils.isEmpty(name)
+                || (name.length() <= ID_FILE_PREFIX.length())
+                || (!name.startsWith(ID_FILE_PREFIX))) {
+            return null;
+        } else {
+            return name.substring(ID_FILE_PREFIX.length());
+        }
+    }
+
+    public static String getIdentityFileName(NamedResource r) {
+        return getIdentityFileName((r == null) ? null : r.getName());
+    }
+
+    /**
+     * @param type The identity type - e.g., {@code rsa} - ignored
+     *             if {@code null}/empty
+     * @return The matching file name for the identity - {@code null}
+     * if no name
+     * @see #ID_FILE_PREFIX
+     * @see #ID_FILE_SUFFIX
+     * @see IdentityUtils#getIdentityFileName(String, String, String)
+     */
+    public static String getIdentityFileName(String type) {
+        return IdentityUtils.getIdentityFileName(ID_FILE_PREFIX, type, ID_FILE_SUFFIX);
+    }
+
+    /**
+     * @param strict        If {@code true} then files that do not have the required
+     *                      access rights are excluded from consideration
+     * @param supportedOnly If {@code true} then ignore identities that are not
+     *                      supported internally
+     * @param provider      A {@link FilePasswordProvider} - may be {@code null}
+     *                      if the loaded keys are <U>guaranteed</U> not to be encrypted. The argument
+     *                      to {@link FilePasswordProvider#getPassword(String)} is the path of the
+     *                      file whose key is to be loaded
+     * @param options       The {@link LinkOption}s to apply when checking
+     *                      for existence
+     * @return A {@link KeyPair} for the identities - {@code null} if no identities
+     * available (e.g., after filtering unsupported ones or strict permissions)
+     * @throws IOException              If failed to access the file system
+     * @throws GeneralSecurityException If failed to load the keys
+     * @see PublicKeyEntry#getDefaultKeysFolderPath()
+     * @see #loadDefaultIdentities(Path, boolean, FilePasswordProvider, LinkOption...)
+     */
+    public static KeyPairProvider loadDefaultKeyPairProvider(
+            boolean strict, boolean supportedOnly, FilePasswordProvider provider, LinkOption... options)
+            throws IOException, GeneralSecurityException {
+        return loadDefaultKeyPairProvider(PublicKeyEntry.getDefaultKeysFolderPath(), strict, supportedOnly, provider, options);
+    }
+
+    /**
+     * @param dir           The folder to scan for the built-in identities
+     * @param strict        If {@code true} then files that do not have the required
+     *                      access rights are excluded from consideration
+     * @param supportedOnly If {@code true} then ignore identities that are not
+     *                      supported internally
+     * @param provider      A {@link FilePasswordProvider} - may be {@code null}
+     *                      if the loaded keys are <U>guaranteed</U> not to be encrypted. The argument
+     *                      to {@link FilePasswordProvider#getPassword(String)} is the path of the
+     *                      file whose key is to be loaded
+     * @param options       The {@link LinkOption}s to apply when checking
+     *                      for existence
+     * @return A {@link KeyPair} for the identities - {@code null} if no identities
+     * available (e.g., after filtering unsupported ones or strict permissions)
+     * @throws IOException              If failed to access the file system
+     * @throws GeneralSecurityException If failed to load the keys
+     * @see #loadDefaultIdentities(Path, boolean, FilePasswordProvider, LinkOption...)
+     * @see IdentityUtils#createKeyPairProvider(Map, boolean)
+     */
+    public static KeyPairProvider loadDefaultKeyPairProvider(
+            Path dir, boolean strict, boolean supportedOnly, FilePasswordProvider provider, LinkOption... options)
+            throws IOException, GeneralSecurityException {
+        Map<String, KeyPair> ids = loadDefaultIdentities(dir, strict, provider, options);
+        return IdentityUtils.createKeyPairProvider(ids, supportedOnly);
+    }
+
+    /**
+     * @param strict   If {@code true} then files that do not have the required
+     *                 access rights are excluded from consideration
+     * @param provider A {@link FilePasswordProvider} - may be {@code null}
+     *                 if the loaded keys are <U>guaranteed</U> not to be encrypted. The argument
+     *                 to {@link FilePasswordProvider#getPassword(String)} is the path of the
+     *                 file whose key is to be loaded
+     * @param options  The {@link LinkOption}s to apply when checking
+     *                 for existence
+     * @return A {@link Map} of the found files where key=identity type (case
+     * <U>insensitive</U>), value=the {@link KeyPair} of the identity
+     * @throws IOException              If failed to access the file system
+     * @throws GeneralSecurityException If failed to load the keys
+     * @see PublicKeyEntry#getDefaultKeysFolderPath()
+     * @see #loadDefaultIdentities(Path, boolean, FilePasswordProvider, LinkOption...)
+     */
+    public static Map<String, KeyPair> loadDefaultIdentities(boolean strict, FilePasswordProvider provider, LinkOption... options)
+            throws IOException, GeneralSecurityException {
+        return loadDefaultIdentities(PublicKeyEntry.getDefaultKeysFolderPath(), strict, provider, options);
+    }
+
+    /**
+     * @param dir      The folder to scan for the built-in identities
+     * @param strict   If {@code true} then files that do not have the required
+     *                 access rights are excluded from consideration
+     * @param provider A {@link FilePasswordProvider} - may be {@code null}
+     *                 if the loaded keys are <U>guaranteed</U> not to be encrypted. The argument
+     *                 to {@link FilePasswordProvider#getPassword(String)} is the path of the
+     *                 file whose key is to be loaded
+     * @param options  The {@link LinkOption}s to apply when checking
+     *                 for existence
+     * @return A {@link Map} of the found files where key=identity type (case
+     * <U>insensitive</U>), value=the {@link KeyPair} of the identity
+     * @throws IOException              If failed to access the file system
+     * @throws GeneralSecurityException If failed to load the keys
+     * @see #loadIdentities(Path, boolean, Collection, Function, FilePasswordProvider, LinkOption...)
+     * @see BuiltinIdentities
+     */
+    public static Map<String, KeyPair> loadDefaultIdentities(Path dir, boolean strict, FilePasswordProvider provider, LinkOption... options)
+            throws IOException, GeneralSecurityException {
+        return loadIdentities(dir, strict, BuiltinIdentities.NAMES, ID_GENERATOR, provider, options);
+    }
+
+    /**
+     * Scans a folder and loads all available identity files
+     *
+     * @param dir         The {@link Path} of the folder to scan - ignored if not exists
+     * @param strict      If {@code true} then files that do not have the required
+     *                    access rights are excluded from consideration
+     * @param types       The identity types - ignored if {@code null}/empty
+     * @param idGenerator A {@link Function} to derive the file name
+     *                    holding the specified type
+     * @param provider    A {@link FilePasswordProvider} - may be {@code null}
+     *                    if the loaded keys are <U>guaranteed</U> not to be encrypted. The argument
+     *                    to {@link FilePasswordProvider#getPassword(String)} is the path of the
+     *                    file whose key is to be loaded
+     * @param options     The {@link LinkOption}s to apply when checking
+     *                    for existence
+     * @return A {@link Map} of the found files where key=identity type (case
+     * <U>insensitive</U>), value=the {@link KeyPair} of the identity
+     * @throws IOException              If failed to access the file system
+     * @throws GeneralSecurityException If failed to load the keys
+     * @see #scanIdentitiesFolder(Path, boolean, Collection, Function, LinkOption...)
+     * @see IdentityUtils#loadIdentities(Map, FilePasswordProvider, java.nio.file.OpenOption...)
+     */
+    public static Map<String, KeyPair> loadIdentities(
+            Path dir, boolean strict, Collection<String> types, Function<String, String> idGenerator, FilePasswordProvider provider, LinkOption... options)
+            throws IOException, GeneralSecurityException {
+        Map<String, Path> paths = scanIdentitiesFolder(dir, strict, types, idGenerator, options);
+        return IdentityUtils.loadIdentities(paths, provider, IoUtils.EMPTY_OPEN_OPTIONS);
+    }
+
+    /**
+     * Scans a folder for possible identity files
+     *
+     * @param dir         The {@link Path} of the folder to scan - ignored if not exists
+     * @param strict      If {@code true} then files that do not have the required
+     *                    access rights are excluded from consideration
+     * @param types       The identity types - ignored if {@code null}/empty
+     * @param idGenerator A {@link Function} to derive the file name
+     *                    holding the specified type
+     * @param options     The {@link LinkOption}s to apply when checking
+     *                    for existence
+     * @return A {@link Map} of the found files where key=identity type (case
+     * <U>insensitive</U>), value=the {@link Path} of the file holding the key
+     * @throws IOException If failed to access the file system
+     * @see KeyUtils#validateStrictKeyFilePermissions(Path, LinkOption...)
+     */
+    public static Map<String, Path> scanIdentitiesFolder(
+            Path dir, boolean strict, Collection<String> types, Function<String, String> idGenerator, LinkOption... options)
+            throws IOException {
+        if (GenericUtils.isEmpty(types)) {
+            return Collections.emptyMap();
+        }
+
+        if (!Files.exists(dir, options)) {
+            return Collections.emptyMap();
+        }
+
+        ValidateUtils.checkTrue(FileInfoExtractor.ISDIR.infoOf(dir, options), "Not a directory: %s", dir);
+
+        Map<String, Path> paths = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+        for (String t : types) {
+            String fileName = idGenerator.apply(t);
+            Path p = dir.resolve(fileName);
+            if (!Files.exists(p, options)) {
+                continue;
+            }
+
+            if (strict) {
+                if (KeyUtils.validateStrictKeyFilePermissions(p, options) != null) {
+                    continue;
+                }
+            }
+
+            Path prev = paths.put(t, p);
+            ValidateUtils.checkTrue(prev == null, "Multiple mappings for type=%s", t);
+        }
+
+        return paths;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcher.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcher.java b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcher.java
new file mode 100644
index 0000000..a00cb24
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcher.java
@@ -0,0 +1,141 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.client.config.keys;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.security.PublicKey;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Supplier;
+
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.common.util.io.ModifiableFileWatcher;
+
+/**
+ * A {@link ClientIdentityProvider} that watches a given key file re-loading
+ * its contents if it is ever modified, deleted or (re-)created
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class ClientIdentityFileWatcher extends ModifiableFileWatcher implements ClientIdentityProvider {
+    private final AtomicReference<KeyPair> identityHolder = new AtomicReference<>(null);
+    private final Supplier<ClientIdentityLoader> loaderHolder;
+    private final Supplier<FilePasswordProvider> providerHolder;
+    private final boolean strict;
+
+    public ClientIdentityFileWatcher(Path path, ClientIdentityLoader loader, FilePasswordProvider provider) {
+        this(path, loader, provider, true);
+    }
+
+    public ClientIdentityFileWatcher(Path path, ClientIdentityLoader loader, FilePasswordProvider provider, boolean strict) {
+        this(path,
+             GenericUtils.supplierOf(Objects.requireNonNull(loader, "No client identity loader")),
+             GenericUtils.supplierOf(Objects.requireNonNull(provider, "No password provider")),
+             strict);
+    }
+
+    public ClientIdentityFileWatcher(Path path, Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider) {
+        this(path, loader, provider, true);
+    }
+
+    public ClientIdentityFileWatcher(Path path, Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider, boolean strict) {
+        super(path);
+        this.loaderHolder = Objects.requireNonNull(loader, "No client identity loader");
+        this.providerHolder = Objects.requireNonNull(provider, "No password provider");
+        this.strict = strict;
+    }
+
+    public final boolean isStrict() {
+        return strict;
+    }
+
+    public final ClientIdentityLoader getClientIdentityLoader() {
+        return loaderHolder.get();
+    }
+
+    public final FilePasswordProvider getFilePasswordProvider() {
+        return providerHolder.get();
+    }
+
+    @Override
+    public KeyPair getClientIdentity() throws IOException, GeneralSecurityException {
+        if (checkReloadRequired()) {
+            KeyPair kp = identityHolder.getAndSet(null);     // start fresh
+            Path path = getPath();
+
+            if (exists()) {
+                KeyPair id = reloadClientIdentity(path);
+                if (!KeyUtils.compareKeyPairs(kp, id)) {
+                    if (log.isDebugEnabled()) {
+                        log.debug("getClientIdentity({}) identity {}", path, (kp == null) ? "loaded" : "re-loaded");
+                    }
+                }
+
+                updateReloadAttributes();
+                identityHolder.set(id);
+            }
+        }
+
+        return identityHolder.get();
+    }
+
+    protected KeyPair reloadClientIdentity(Path path) throws IOException, GeneralSecurityException {
+        if (isStrict()) {
+            Map.Entry<String, Object> violation = KeyUtils.validateStrictKeyFilePermissions(path, IoUtils.EMPTY_LINK_OPTIONS);
+            if (violation != null) {
+                if (log.isDebugEnabled()) {
+                    log.debug("reloadClientIdentity({}) ignore due to {}", path, violation.getKey());
+                }
+                return null;
+            }
+        }
+
+        String location = path.toString();
+        ClientIdentityLoader idLoader = Objects.requireNonNull(getClientIdentityLoader(), "No client identity loader");
+        if (idLoader.isValidLocation(location)) {
+            KeyPair kp = idLoader.loadClientIdentity(location, Objects.requireNonNull(getFilePasswordProvider(), "No file password provider"));
+            if (log.isTraceEnabled()) {
+                PublicKey key = (kp == null) ? null : kp.getPublic();
+                if (key != null) {
+                    log.trace("reloadClientIdentity({}) loaded {}-{}",
+                              location, KeyUtils.getKeyType(key), KeyUtils.getFingerPrint(key));
+
+                } else {
+                    log.trace("reloadClientIdentity({}) no key loaded", location);
+                }
+            }
+
+            return kp;
+        }
+
+        if (log.isDebugEnabled()) {
+            log.debug("reloadClientIdentity({}) invalid location", location);
+        }
+
+        return null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityLoader.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityLoader.java b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityLoader.java
new file mode 100644
index 0000000..8b3a295
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityLoader.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.client.config.keys;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface ClientIdentityLoader {
+    /**
+     * <P>A default implementation that assumes a file location that <U>must</U> exist.</P>
+     *
+     * <P>
+     * <B>Note:</B> It calls {@link SecurityUtils#loadKeyPairIdentity(String, InputStream, FilePasswordProvider)}
+     * </P>
+     */
+    ClientIdentityLoader DEFAULT = new ClientIdentityLoader() {
+        @Override
+        public boolean isValidLocation(String location) throws IOException {
+            Path path = toPath(location);
+            return Files.exists(path, IoUtils.EMPTY_LINK_OPTIONS);
+        }
+
+        @Override
+        public KeyPair loadClientIdentity(String location, FilePasswordProvider provider) throws IOException, GeneralSecurityException {
+            Path path = toPath(location);
+            try (InputStream inputStream = Files.newInputStream(path, IoUtils.EMPTY_OPEN_OPTIONS)) {
+                return SecurityUtils.loadKeyPairIdentity(path.toString(), inputStream, provider);
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "DEFAULT";
+        }
+
+        private Path toPath(String location) {
+            Path path = Paths.get(ValidateUtils.checkNotNullAndNotEmpty(location, "No location"));
+            path = path.toAbsolutePath();
+            path = path.normalize();
+            return path;
+        }
+    };
+
+    /**
+     * @param location The identity key-pair location - the actual meaning (file, URL, etc.)
+     * depends on the implementation.
+     * @return {@code true} if it represents a valid location - the actual meaning of
+     * the validity depends on the implementation
+     * @throws IOException If failed to validate the location
+     */
+    boolean isValidLocation(String location) throws IOException;
+
+    /**
+     * @param location The identity key-pair location - the actual meaning (file, URL, etc.)
+     * depends on the implementation.
+     * @param provider The {@link FilePasswordProvider} to consult if the location contains
+     * an encrypted identity
+     * @return The loaded {@link KeyPair} - {@code null} if location is empty
+     * and it is OK that it does not exist
+     * @throws IOException If failed to access / process the remote location
+     * @throws GeneralSecurityException If failed to convert the contents into
+     * a valid identity
+     */
+    KeyPair loadClientIdentity(String location, FilePasswordProvider provider) throws IOException, GeneralSecurityException;
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityProvider.java b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityProvider.java
new file mode 100644
index 0000000..bda4700
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityProvider.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.client.config.keys;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FunctionalInterface
+public interface ClientIdentityProvider {
+    /**
+     * Provides a {@link KeyPair} representing the client identity
+     *
+     * @return The client identity - may be {@code null} if no currently
+     * available identity from this provider. <B>Note:</B> the provider
+     * may return a <U>different</U> value every time this method is called
+     * - e.g., if it is (re-)loading contents from a file.
+     * @throws IOException If failed to load the identity
+     * @throws GeneralSecurityException If failed to parse the identity
+     */
+    KeyPair getClientIdentity() throws IOException, GeneralSecurityException;
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/client/config/keys/DefaultClientIdentitiesWatcher.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/config/keys/DefaultClientIdentitiesWatcher.java b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/DefaultClientIdentitiesWatcher.java
new file mode 100644
index 0000000..3afa129
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/DefaultClientIdentitiesWatcher.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.client.config.keys;
+
+import java.nio.file.Path;
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Supplier;
+
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.PublicKeyEntry;
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class DefaultClientIdentitiesWatcher extends BuiltinClientIdentitiesWatcher {
+    public DefaultClientIdentitiesWatcher(ClientIdentityLoader loader, FilePasswordProvider provider) {
+        this(loader, provider, true);
+    }
+
+    public DefaultClientIdentitiesWatcher(ClientIdentityLoader loader, FilePasswordProvider provider, boolean strict) {
+        this(true, loader, provider, strict);
+    }
+
+    public DefaultClientIdentitiesWatcher(boolean supportedOnly, ClientIdentityLoader loader, FilePasswordProvider provider, boolean strict) {
+        this(supportedOnly,
+             GenericUtils.supplierOf(Objects.requireNonNull(loader, "No client identity loader")),
+             GenericUtils.supplierOf(Objects.requireNonNull(provider, "No password provider")),
+             strict);
+    }
+
+    public DefaultClientIdentitiesWatcher(Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider) {
+        this(loader, provider, true);
+    }
+
+    public DefaultClientIdentitiesWatcher(Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider, boolean strict) {
+        this(true, loader, provider, strict);
+    }
+
+    public DefaultClientIdentitiesWatcher(boolean supportedOnly,
+            Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider, boolean strict) {
+        super(PublicKeyEntry.getDefaultKeysFolderPath(), supportedOnly, loader, provider, strict);
+    }
+
+    public static List<Path> getDefaultBuiltinIdentitiesPaths() {
+        return getDefaultBuiltinIdentitiesPaths(PublicKeyEntry.getDefaultKeysFolderPath());
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/AttributeStore.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/AttributeStore.java b/sshd-common/src/main/java/org/apache/sshd/common/AttributeStore.java
new file mode 100644
index 0000000..96f6ab1
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/AttributeStore.java
@@ -0,0 +1,97 @@
+/*
+ * 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;
+
+/**
+ * Provides the capability to attach in-memory attributes to the entity
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface AttributeStore {
+    /**
+     * <P>
+     * Type safe key for storage of user attributes. Typically it is used as a static
+     * variable that is shared between the producer and the consumer. To further
+     * restrict access the setting or getting it from the store one can add static
+     * {@code get/set methods} e.g:
+     * </P>
+     *
+     * <pre>
+     * public static final AttributeKey&lt;MyValue&gt; MY_KEY = new AttributeKey&lt;MyValue&gt;();
+     *
+     * public static MyValue getMyValue(Session s) {
+     *   return s.getAttribute(MY_KEY);
+     * }
+     *
+     * public static void setMyValue(Session s, MyValue value) {
+     *   s.setAttribute(MY_KEY, value);
+     * }
+     * </pre>
+     *
+     * @param <T> type of value stored in the attribute.
+     * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+     */
+    // CHECKSTYLE:OFF
+    class AttributeKey<T> {
+        public AttributeKey() {
+            super();
+        }
+    }
+    // CHECKSTYLE:ON
+
+    /**
+     * Returns the value of the user-defined attribute.
+     *
+     * @param <T> The generic attribute type
+     * @param key The key of the attribute; must not be {@code null}.
+     * @return {@code null} if there is no value associated with the specified key
+     */
+    <T> T getAttribute(AttributeKey<T> key);
+
+    /**
+     * Sets a user-defined attribute.
+     *
+     * @param <T>   The generic attribute type
+     * @param key   The key of the attribute; must not be {@code null}.
+     * @param value The value of the attribute; must not be {@code null}.
+     * @return The old value of the attribute; {@code null} if it is new.
+     */
+    <T> T setAttribute(AttributeKey<T> key, T value);
+
+    /**
+     * Removes the user-defined attribute
+     *
+     * @param <T> The generic attribute type
+     * @param key The key of the attribute; must not be {@code null}.
+     * @return The removed value; {@code null} if no previous value
+     */
+    <T> T removeAttribute(AttributeKey<T> key);
+
+    /**
+     * Attempts to resolve the associated value by going up the store's
+     * hierarchy (if any)
+     *
+     * @param <T> The generic attribute type
+     * @param key The key of the attribute; must not be {@code null}.
+     * @return {@code null} if there is no value associated with the specified key
+     */
+    <T> T resolveAttribute(AttributeKey<T> key);
+}
+

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/BuiltinFactory.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/BuiltinFactory.java b/sshd-common/src/main/java/org/apache/sshd/common/BuiltinFactory.java
new file mode 100644
index 0000000..33b53a9
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/BuiltinFactory.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * A named optional factory.
+ *
+ * @param <T> The create object instance type
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface BuiltinFactory<T> extends NamedFactory<T>, OptionalFeature {
+    static <T, E extends BuiltinFactory<T>> List<NamedFactory<T>> setUpFactories(
+            boolean ignoreUnsupported, Collection<? extends E> preferred) {
+        return GenericUtils.stream(preferred)
+                .filter(f -> ignoreUnsupported || f.isSupported())
+                .collect(Collectors.toList());
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/Closeable.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/Closeable.java b/sshd-common/src/main/java/org/apache/sshd/common/Closeable.java
new file mode 100644
index 0000000..6a6f622
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/Closeable.java
@@ -0,0 +1,126 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common;
+
+import java.io.IOException;
+import java.net.SocketTimeoutException;
+import java.nio.channels.Channel;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.sshd.common.future.CloseFuture;
+import org.apache.sshd.common.future.SshFutureListener;
+
+/**
+ * A {@code Closeable} is a resource that can be closed.
+ * The close method is invoked to release resources that the object is
+ * holding. The user can pre-register listeners to be notified
+ * when resource close is completed (successfully or otherwise)
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface Closeable extends Channel {
+
+    /**
+     * Timeout (milliseconds) for waiting on a {@link CloseFuture} to successfully
+     * complete its action.
+     * @see #DEFAULT_CLOSE_WAIT_TIMEOUT
+     */
+    String CLOSE_WAIT_TIMEOUT = "sshd-close-wait-time";
+
+    /**
+     * Default value for {@link #CLOSE_WAIT_TIMEOUT} if none specified
+     */
+    long DEFAULT_CLOSE_WAIT_TIMEOUT = TimeUnit.SECONDS.toMillis(15L);
+
+    /**
+     * Close this resource asynchronously and return a future.
+     * Resources support two closing modes: a graceful mode
+     * which will cleanly close the resource and an immediate mode
+     * which will close the resources abruptly.
+     *
+     * @param immediately <code>true</code> if the resource should be shut down abruptly,
+     *                    <code>false</code> for a graceful close
+     * @return a {@link CloseFuture} representing the close request
+     */
+    CloseFuture close(boolean immediately);
+
+    /**
+     * Pre-register a listener to be informed when resource is closed. If
+     * resource is already closed, the listener will be invoked immediately
+     * and not registered for future notification
+     *
+     * @param listener The notification {@link SshFutureListener} - never {@code null}
+     */
+    void addCloseFutureListener(SshFutureListener<CloseFuture> listener);
+
+    /**
+     * Remove a pre-registered close event listener
+     *
+     * @param listener The register {@link SshFutureListener} - never {@code null}.
+     * Ignored if not registered or resource already closed
+     */
+    void removeCloseFutureListener(SshFutureListener<CloseFuture> listener);
+
+    /**
+     * Returns <code>true</code> if this object has been closed.
+     *
+     * @return <code>true</code> if closing
+     */
+    boolean isClosed();
+
+    /**
+     * Returns <code>true</code> if the {@link #close(boolean)} method
+     * has been called. Note that this method will return <code>true</code>
+     * even if this {@link #isClosed()} returns <code>true</code>.
+     *
+     * @return <code>true</code> if closing
+     */
+    boolean isClosing();
+
+    @Override
+    default boolean isOpen() {
+        return !(isClosed() || isClosing());
+    }
+
+    @Override
+    default void close() throws IOException {
+        Closeable.close(this);
+    }
+
+    static long getMaxCloseWaitTime(PropertyResolver resolver) {
+        return (resolver == null) ? DEFAULT_CLOSE_WAIT_TIMEOUT
+                : resolver.getLongProperty(CLOSE_WAIT_TIMEOUT, DEFAULT_CLOSE_WAIT_TIMEOUT);
+    }
+
+    static void close(Closeable closeable) throws IOException {
+        if (closeable == null) {
+            return;
+        }
+
+        if ((!closeable.isClosed()) && (!closeable.isClosing())) {
+            CloseFuture future = closeable.close(true);
+            long maxWait = (closeable instanceof PropertyResolver)
+                    ? getMaxCloseWaitTime((PropertyResolver) closeable) : DEFAULT_CLOSE_WAIT_TIMEOUT;
+            boolean successful = future.await(maxWait);
+            if (!successful) {
+                throw new SocketTimeoutException("Failed to receive closure confirmation within " + maxWait + " millis");
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/Factory.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/Factory.java b/sshd-common/src/main/java/org/apache/sshd/common/Factory.java
new file mode 100644
index 0000000..93b351c
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/Factory.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common;
+
+import java.util.function.Supplier;
+
+/**
+ * Factory is a simple interface that is used to create other objects.
+ *
+ * @param <T> type of object this factory will create
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FunctionalInterface
+public interface Factory<T> extends Supplier<T> {
+
+    @Override
+    default T get() {
+        return create();
+    }
+
+    /**
+     * @return A new instance
+     */
+    T create();
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/NamedFactory.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/NamedFactory.java b/sshd-common/src/main/java/org/apache/sshd/common/NamedFactory.java
new file mode 100644
index 0000000..5386447
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/NamedFactory.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+/**
+ * A named factory is a factory identified by a name.
+ * Such names are used mainly in the algorithm negotiation at the beginning of the SSH connection.
+ *
+ * @param <T> The create object instance type
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface NamedFactory<T> extends Factory<T>, NamedResource {
+    /**
+     * Create an instance of the specified name by looking up the needed factory
+     * in the list.
+     *
+     * @param factories list of available factories
+     * @param name      the factory name to use
+     * @param <T>       type of object to create
+     * @return a newly created object or {@code null} if the factory is not in the list
+     */
+    static <T> T create(Collection<? extends NamedFactory<? extends T>> factories, String name) {
+        NamedFactory<? extends T> f = NamedResource.findByName(name, String.CASE_INSENSITIVE_ORDER, factories);
+        if (f != null) {
+            return f.create();
+        } else {
+            return null;
+        }
+    }
+
+    static <S extends OptionalFeature, T, E extends NamedFactory<T>> List<NamedFactory<T>> setUpTransformedFactories(
+            boolean ignoreUnsupported, Collection<? extends S> preferred, Function<? super S, ? extends E> xform) {
+        return preferred.stream()
+            .filter(f -> ignoreUnsupported || f.isSupported())
+            .map(xform)
+            .collect(Collectors.toList());
+    }
+
+    static <T, E extends NamedFactory<T> & OptionalFeature> List<NamedFactory<T>> setUpBuiltinFactories(
+            boolean ignoreUnsupported, Collection<? extends E> preferred) {
+        return preferred.stream()
+            .filter(f -> ignoreUnsupported || f.isSupported())
+            .collect(Collectors.toList());
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/NamedResource.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/NamedResource.java b/sshd-common/src/main/java/org/apache/sshd/common/NamedResource.java
new file mode 100644
index 0000000..813f53d
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/NamedResource.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common;
+
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.List;
+import java.util.function.Function;
+
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FunctionalInterface
+public interface NamedResource {
+
+    /**
+     * Returns the value of {@link #getName()} - or {@code null} if argument is {@code null}
+     */
+    Function<NamedResource, String> NAME_EXTRACTOR = input -> input == null ? null : input.getName();
+
+    /**
+     * Compares 2 {@link NamedResource}s according to their {@link #getName()}
+     * value case <U>insensitive</U>
+     */
+    Comparator<NamedResource> BY_NAME_COMPARATOR = Comparator.comparing(NAME_EXTRACTOR, String.CASE_INSENSITIVE_ORDER);
+
+    /**
+     * @return The resource name
+     */
+    String getName();
+
+    /**
+     * @param resources The named resources
+     * @return A {@link List} of all the factories names - in same order
+     * as they appear in the input collection
+     */
+    static List<String> getNameList(Collection<? extends NamedResource> resources) {
+        return GenericUtils.map(resources, NamedResource::getName);
+    }
+
+    /**
+     * @param resources list of available resources
+     * @return A comma separated list of factory names
+     */
+    static String getNames(Collection<? extends NamedResource> resources) {
+        Collection<String> nameList = getNameList(resources);
+        return GenericUtils.join(nameList, ',');
+    }
+
+    /**
+     * Remove the resource identified by the name from the list.
+     *
+     * @param <R>       The generic resource type
+     * @param name      Name of the resource - ignored if {@code null}/empty
+     * @param c         The {@link Comparator} to decide whether the {@link NamedResource#getName()}
+     *                  matches the <tt>name</tt> parameter
+     * @param resources The {@link NamedResource} to check - ignored if {@code null}/empty
+     * @return the removed resource from the list or {@code null} if not in the list
+     */
+    static <R extends NamedResource> R removeByName(String name, Comparator<? super String> c, Collection<? extends R> resources) {
+        R r = findByName(name, c, resources);
+        if (r != null) {
+            resources.remove(r);
+        }
+        return r;
+    }
+
+    /**
+     * @param <R>       The generic resource type
+     * @param name      Name of the resource - ignored if {@code null}/empty
+     * @param c         The {@link Comparator} to decide whether the {@link NamedResource#getName()}
+     *                  matches the <tt>name</tt> parameter
+     * @param resources The {@link NamedResource} to check - ignored if {@code null}/empty
+     * @return The <U>first</U> resource whose name matches the parameter (by invoking
+     * {@link Comparator#compare(Object, Object)} - {@code null} if no match found
+     */
+    static <R extends NamedResource> R findByName(String name, Comparator<? super String> c, Collection<? extends R> resources) {
+        return GenericUtils.isEmpty(name)
+            ? null
+            : GenericUtils.stream(resources)
+                .filter(r -> c.compare(name, r.getName()) == 0)
+                .findFirst()
+                .orElse(null);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/OptionalFeature.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/OptionalFeature.java b/sshd-common/src/main/java/org/apache/sshd/common/OptionalFeature.java
new file mode 100644
index 0000000..1afa864
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/OptionalFeature.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common;
+
+import java.util.Collection;
+
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FunctionalInterface
+public interface OptionalFeature {
+    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();
+
+    static OptionalFeature of(boolean supported) {
+        return supported ? TRUE : FALSE;
+    }
+
+    static OptionalFeature all(Collection<? extends OptionalFeature> features) {
+        return () -> {
+            if (GenericUtils.isEmpty(features)) {
+                return false;
+            }
+
+            for (OptionalFeature f : features) {
+                if (!f.isSupported()) {
+                    return false;
+                }
+            }
+
+            return true;
+        };
+    }
+
+    static OptionalFeature any(Collection<? extends OptionalFeature> features) {
+        return () -> {
+            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/10de190e/sshd-common/src/main/java/org/apache/sshd/common/PropertyResolver.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/PropertyResolver.java b/sshd-common/src/main/java/org/apache/sshd/common/PropertyResolver.java
new file mode 100644
index 0000000..c333c7f
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/PropertyResolver.java
@@ -0,0 +1,124 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common;
+
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * Indicates an entity that can be configured using properties. The properties
+ * are simple name-value pairs where the actual value type depends on the
+ * property. Some automatic conversions may be available - e.g., from a string
+ * to a numeric or {@code boolean} value, or from {@code int} to {@code long},
+ * etc.. <B>Note:</B> implementations may decide to use case <U>insensitive</U>
+ * property names, therefore it is <U><B>highly discouraged</B></U> to use names
+ * that differ from each other only in case sensitivity. Also, implementations
+ * may choose to trim whitespaces, thus such are also highly discouraged.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface PropertyResolver {
+    /**
+     * An &quot;empty&quot; resolver with no properties and no parent
+     */
+    PropertyResolver EMPTY = new PropertyResolver() {
+        @Override
+        public PropertyResolver getParentPropertyResolver() {
+            return null;
+        }
+
+        @Override
+        public Map<String, Object> getProperties() {
+            return Collections.emptyMap();
+        }
+
+        @Override
+        public String toString() {
+            return "EMPTY";
+        }
+    };
+
+    /**
+     * @return The parent resolver that can be used to query for missing
+     *         properties - {@code null} if no parent
+     */
+    PropertyResolver getParentPropertyResolver();
+
+    /**
+     * <P>
+     * A map of properties that can be used to configure the SSH server or
+     * client. This map will never be changed by either the server or client and
+     * is not supposed to be changed at runtime (changes are not bound to have
+     * any effect on a running client or server), though it may affect the
+     * creation of sessions later as these values are usually not cached.
+     * </P>
+     *
+     * <P>
+     * <B>Note:</B> the <U>type</U> of the mapped property should match the
+     * expected configuration value type - {@code Long, Integer, Boolean,
+     * String}, etc.... If it doesn't, the {@code toString()} result of the
+     * mapped value is used to convert it to the required type. E.g., if the
+     * mapped value is the <U>string</U> &quot;1234&quot; and the expected value
+     * is a {@code long} then it will be parsed into one. Also, if the mapped
+     * value is an {@code Integer} but a {@code long} is expected, then it will
+     * be converted into one.
+     * </P>
+     *
+     * @return a valid <code>Map</code> containing configuration values, never
+     *         {@code null}
+     */
+    Map<String, Object> getProperties();
+
+    default long getLongProperty(String name, long def) {
+        return PropertyResolverUtils.getLongProperty(this, name, def);
+    }
+
+    default Long getLong(String name) {
+        return PropertyResolverUtils.getLong(this, name);
+    }
+
+    default int getIntProperty(String name, int def) {
+        return PropertyResolverUtils.getIntProperty(this, name, def);
+    }
+
+    default Integer getInteger(String name) {
+        return PropertyResolverUtils.getInteger(this, name);
+    }
+
+    default boolean getBooleanProperty(String name, boolean def) {
+        return PropertyResolverUtils.getBooleanProperty(this, name, def);
+    }
+
+    default Boolean getBoolean(String name) {
+        return PropertyResolverUtils.getBoolean(this, name);
+    }
+
+    default String getStringProperty(String name, String def) {
+        return PropertyResolverUtils.getStringProperty(this, name, def);
+    }
+
+    default String getString(String name) {
+        return PropertyResolverUtils.getString(this, name);
+    }
+
+    default Object getObject(String name) {
+        return PropertyResolverUtils.getObject(this, name);
+    }
+}


[22/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common utilities code from sshd-core into sshd-common (new artifact)

Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/KeyRandomArt.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/KeyRandomArt.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/KeyRandomArt.java
deleted file mode 100644
index b59dbd0..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/KeyRandomArt.java
+++ /dev/null
@@ -1,310 +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.config.keys;
-
-import java.io.IOException;
-import java.io.StreamCorruptedException;
-import java.security.KeyPair;
-import java.security.PublicKey;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Objects;
-
-import org.apache.sshd.common.Factory;
-import org.apache.sshd.common.digest.Digest;
-import org.apache.sshd.common.keyprovider.KeyIdentityProvider;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-
-/**
- * Draw an ASCII-Art representing the fingerprint so human brain can
- * profit from its built-in pattern recognition ability.
- * This technique is called "random art" and can be found in some
- * scientific publications like this original paper:
- *
- * &quot;Hash Visualization: a New Technique to improve Real-World Security&quot;,
- * Perrig A. and Song D., 1999, International Workshop on Cryptographic
- * Techniques and E-Commerce (CrypTEC '99)
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- * @see <a href="http://sparrow.ece.cmu.edu/~adrian/projects/validation/validation.pdf">Original article</a>
- * @see <a href="http://opensource.apple.com/source/OpenSSH/OpenSSH-175/openssh/key.c">C implementation</a>
- */
-public class KeyRandomArt {
-    public static final int FLDBASE = 8;
-    public static final int FLDSIZE_Y = FLDBASE + 1;
-    public static final int FLDSIZE_X = FLDBASE * 2 + 1;
-    public static final String AUGMENTATION_STRING = " .o+=*BOX@%&#/^SE";
-
-    private final String algorithm;
-    private final int keySize;
-    private final char[][] field = new char[FLDSIZE_X][FLDSIZE_Y];
-
-    public KeyRandomArt(PublicKey key) throws Exception {
-        this(key, KeyUtils.getDefaultFingerPrintFactory());
-    }
-
-    public KeyRandomArt(PublicKey key, Factory<? extends Digest> f) throws Exception {
-        this(key, Objects.requireNonNull(f, "No digest factory").create());
-    }
-
-    public KeyRandomArt(PublicKey key, Digest d) throws Exception {
-        this(Objects.requireNonNull(key, "No key provided").getAlgorithm(),
-             KeyUtils.getKeySize(key),
-             KeyUtils.getRawFingerprint(Objects.requireNonNull(d, "No key digest"), key));
-    }
-
-    /**
-     * @param algorithm The key algorithm
-     * @param keySize The key size in bits
-     * @param digest The key digest
-     */
-    public KeyRandomArt(String algorithm, int keySize, byte[] digest) {
-        this.algorithm = ValidateUtils.checkNotNullAndNotEmpty(algorithm, "No algorithm provided");
-        ValidateUtils.checkTrue(keySize > 0, "Invalid key size: %d", keySize);
-        this.keySize = keySize;
-        Objects.requireNonNull(digest, "No key digest provided");
-
-        int x = FLDSIZE_X / 2;
-        int y = FLDSIZE_Y / 2;
-        int len = AUGMENTATION_STRING.length() - 1;
-        for (int i = 0; i < digest.length; i++) {
-            /* each byte conveys four 2-bit move commands */
-            int input = digest[i] & 0xFF;
-            for (int b = 0; b < 4; b++) {
-                /* evaluate 2 bit, rest is shifted later */
-                x += ((input & 0x1) != 0) ? 1 : -1;
-                y += ((input & 0x2) != 0) ? 1 : -1;
-
-                /* assure we are still in bounds */
-                x = Math.max(x, 0);
-                y = Math.max(y, 0);
-                x = Math.min(x, FLDSIZE_X - 1);
-                y = Math.min(y, FLDSIZE_Y - 1);
-
-                /* augment the field */
-                if (field[x][y] < (len - 2)) {
-                    field[x][y]++;
-                }
-                input = input >> 2;
-            }
-        }
-
-        /* mark starting point and end point*/
-        field[FLDSIZE_X / 2][FLDSIZE_Y / 2] = (char) (len - 1);
-        field[x][y] = (char) len;
-    }
-
-    public String getAlgorithm() {
-        return algorithm;
-    }
-
-    public int getKeySize() {
-        return keySize;
-    }
-
-    /**
-     * Outputs the generated random art
-     *
-     * @param <A> The {@link Appendable} output writer
-     * @param sb The writer
-     * @return The updated writer instance
-     * @throws IOException If failed to write the combined result
-     */
-    public <A extends Appendable> A append(A sb) throws IOException {
-        // Upper border
-        String s = String.format("+--[%4s %4d]", getAlgorithm(), getKeySize());
-        sb.append(s);
-        for (int index = s.length(); index <= FLDSIZE_X; index++) {
-            sb.append('-');
-        }
-        sb.append('+');
-        sb.append('\n');
-
-        // contents
-        int len = AUGMENTATION_STRING.length() - 1;
-        for (int y = 0; y < FLDSIZE_Y; y++) {
-            sb.append('|');
-            for (int x = 0; x < FLDSIZE_X; x++) {
-                char ch = field[x][y];
-                sb.append(AUGMENTATION_STRING.charAt(Math.min(ch, len)));
-            }
-            sb.append('|');
-            sb.append('\n');
-        }
-
-        // lower border
-        sb.append('+');
-        for (int index = 0; index < FLDSIZE_X; index++) {
-            sb.append('-');
-        }
-
-        sb.append('+');
-        sb.append('\n');
-        return sb;
-    }
-
-    @Override
-    public String toString() {
-        try {
-            return append(new StringBuilder((FLDSIZE_X + 4) * (FLDSIZE_Y + 3))).toString();
-        } catch (IOException e) {
-            return e.getClass().getSimpleName();    // unexpected
-        }
-    }
-
-    /**
-     * Combines the arts in a user-friendly way so they are aligned with each other
-     *
-     * @param separator The separator to use between the arts - if empty char
-     * ('\0') then no separation is done
-     * @param arts The {@link KeyRandomArt}s to combine - ignored if {@code null}/empty
-     * @return The combined result
-     */
-    public static String combine(char separator, Collection<? extends KeyRandomArt> arts) {
-        if (GenericUtils.isEmpty(arts)) {
-            return "";
-        }
-
-        try {
-            return combine(new StringBuilder(arts.size() * (FLDSIZE_X + 4) * (FLDSIZE_Y + 3)), separator, arts).toString();
-        } catch (IOException e) {
-            return e.getClass().getSimpleName();    // unexpected
-        }
-    }
-
-    /**
-     * Creates the combined representation of the random art entries for the provided keys
-     *
-     * @param separator The separator to use between the arts - if empty char
-     * ('\0') then no separation is done
-     * @param provider The {@link KeyIdentityProvider} - ignored if {@code null}
-     * or has no keys to provide
-     * @return The combined representation
-     * @throws Exception If failed to extract or combine the entries
-     * @see #combine(Appendable, char, KeyIdentityProvider)
-     */
-    public static String combine(char separator, KeyIdentityProvider provider) throws Exception {
-        return combine(new StringBuilder(4 * (FLDSIZE_X + 4) * (FLDSIZE_Y + 3)), separator, provider).toString();
-    }
-
-    /**
-     * Appends the combined random art entries for the provided keys
-     *
-     * @param <A> The {@link Appendable} output writer
-     * @param sb The writer
-     * @param separator The separator to use between the arts - if empty char
-     * ('\0') then no separation is done
-     * @param provider The {@link KeyIdentityProvider} - ignored if {@code null}
-     * or has no keys to provide
-     * @return The updated writer instance
-     * @throws Exception If failed to extract or write the entries
-     * @see #generate(KeyIdentityProvider)
-     * @see #combine(Appendable, char, Collection)
-     */
-    public static <A extends Appendable> A combine(A sb, char separator, KeyIdentityProvider provider) throws Exception {
-        return combine(sb, separator, generate(provider));
-    }
-
-    /**
-     * Extracts and generates random art entries for all key in the provider
-     *
-     * @param provider The {@link KeyIdentityProvider} - ignored if {@code null}
-     * or has no keys to provide
-     * @return The extracted {@link KeyRandomArt}s
-     * @throws Exception If failed to extract the entries
-     * @see KeyIdentityProvider#loadKeys()
-     */
-    public static Collection<KeyRandomArt> generate(KeyIdentityProvider provider) throws Exception {
-        Iterable<KeyPair> keys = (provider == null) ? null : provider.loadKeys();
-        Iterator<KeyPair> iter = (keys == null) ? null : keys.iterator();
-        if ((iter == null) || (!iter.hasNext())) {
-            return Collections.emptyList();
-        }
-
-        Collection<KeyRandomArt> arts = new LinkedList<>();
-        do {
-            KeyPair kp = iter.next();
-            KeyRandomArt a = new KeyRandomArt(kp.getPublic());
-            arts.add(a);
-        } while (iter.hasNext());
-
-        return arts;
-    }
-
-    /**
-     * Combines the arts in a user-friendly way so they are aligned with each other
-     *
-     * @param <A> The {@link Appendable} output writer
-     * @param sb The writer
-     * @param separator The separator to use between the arts - if empty char
-     * ('\0') then no separation is done
-     * @param arts The {@link KeyRandomArt}s to combine - ignored if {@code null}/empty
-     * @return The updated writer instance
-     * @throws IOException If failed to write the combined result
-     */
-    public static <A extends Appendable> A combine(A sb, char separator, Collection<? extends KeyRandomArt> arts) throws IOException {
-        if (GenericUtils.isEmpty(arts)) {
-            return sb;
-        }
-
-        List<String[]> allLines = new ArrayList<>(arts.size());
-        int numLines = -1;
-        for (KeyRandomArt a : arts) {
-            String s = a.toString();
-            String[] lines = GenericUtils.split(s, '\n');
-            if (numLines <= 0) {
-                numLines = lines.length;
-            } else {
-                if (numLines != lines.length) {
-                    throw new StreamCorruptedException("Mismatched lines count: expected=" + numLines + ", actual=" + lines.length);
-                }
-            }
-
-            for (int index = 0; index < lines.length; index++) {
-                String l = lines[index];
-                if ((l.length() > 0) && (l.charAt(l.length() - 1) == '\r')) {
-                    l = l.substring(0, l.length() - 1);
-                    lines[index] = l;
-                }
-            }
-
-            allLines.add(lines);
-        }
-
-        for (int row = 0; row < numLines; row++) {
-            for (int index = 0; index < allLines.size(); index++) {
-                String[] lines = allLines.get(index);
-                String l = lines[row];
-                sb.append(l);
-                if ((index > 0) && (separator != '\0')) {
-                    sb.append(separator);
-                }
-            }
-            sb.append('\n');
-        }
-
-        return sb;
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/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
deleted file mode 100644
index 84b019f..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/KeyUtils.java
+++ /dev/null
@@ -1,937 +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.config.keys;
-
-import java.io.IOException;
-import java.math.BigInteger;
-import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.LinkOption;
-import java.nio.file.Path;
-import java.nio.file.attribute.PosixFilePermission;
-import java.security.GeneralSecurityException;
-import java.security.Key;
-import java.security.KeyFactory;
-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.RSAPrivateCrtKey;
-import java.security.interfaces.RSAPrivateKey;
-import java.security.interfaces.RSAPublicKey;
-import java.security.spec.DSAPublicKeySpec;
-import java.security.spec.ECParameterSpec;
-import java.security.spec.InvalidKeySpecException;
-import java.security.spec.RSAPublicKeySpec;
-import java.util.AbstractMap.SimpleImmutableEntry;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.TreeMap;
-import java.util.concurrent.atomic.AtomicReference;
-
-import org.apache.sshd.common.Factory;
-import org.apache.sshd.common.cipher.ECCurves;
-import org.apache.sshd.common.config.keys.impl.DSSPublicKeyEntryDecoder;
-import org.apache.sshd.common.config.keys.impl.ECDSAPublicKeyEntryDecoder;
-import org.apache.sshd.common.config.keys.impl.RSAPublicKeyDecoder;
-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.digest.DigestUtils;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.OsUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.buffer.Buffer;
-import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
-import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * Utility class for keys
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public final class KeyUtils {
-    /**
-     * Name of algorithm for RSA keys to be used when calling security provider
-     */
-    public static final String RSA_ALGORITHM = "RSA";
-
-    /**
-     * The most commonly used RSA public key exponent
-     */
-    public static final BigInteger DEFAULT_RSA_PUBLIC_EXPONENT = new BigInteger("65537");
-
-    /**
-     * Name of algorithm for DSS keys to be used when calling security provider
-     */
-    public static final String DSS_ALGORITHM = "DSA";
-
-    /**
-     * Name of algorithm for EC keys to be used when calling security provider
-     */
-    public static final String EC_ALGORITHM = "EC";
-
-    /**
-     * The {@link Set} of {@link PosixFilePermission} <U>not</U> allowed if strict
-     * permissions are enforced on key files
-     */
-    public static final Set<PosixFilePermission> STRICTLY_PROHIBITED_FILE_PERMISSION =
-            Collections.unmodifiableSet(
-                    EnumSet.of(PosixFilePermission.GROUP_READ, PosixFilePermission.GROUP_WRITE, PosixFilePermission.GROUP_EXECUTE,
-                            PosixFilePermission.OTHERS_READ, PosixFilePermission.OTHERS_WRITE, PosixFilePermission.OTHERS_EXECUTE));
-
-    /**
-     * System property that can be used to control the default fingerprint factory used for keys.
-     * If not set the {@link #DEFAULT_FINGERPRINT_DIGEST_FACTORY} is used
-     */
-    public static final String KEY_FINGERPRINT_FACTORY_PROP = "org.apache.sshd.keyFingerprintFactory";
-
-    /**
-     * The default {@link Factory} of {@link Digest}s initialized
-     * as the value of {@link #getDefaultFingerPrintFactory()} if not
-     * overridden by {@link #KEY_FINGERPRINT_FACTORY_PROP} or
-     * {@link #setDefaultFingerPrintFactory(DigestFactory)}
-     */
-    public static final DigestFactory DEFAULT_FINGERPRINT_DIGEST_FACTORY = BuiltinDigests.sha256;
-
-    private static final AtomicReference<DigestFactory> DEFAULT_DIGEST_HOLDER = new AtomicReference<>();
-
-    private static final Map<String, PublicKeyEntryDecoder<?, ?>> BY_KEY_TYPE_DECODERS_MAP =
-            new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
-
-    private static final Map<Class<?>, PublicKeyEntryDecoder<?, ?>> BY_KEY_CLASS_DECODERS_MAP =
-            new HashMap<>();
-
-    static {
-        registerPublicKeyEntryDecoder(RSAPublicKeyDecoder.INSTANCE);
-        registerPublicKeyEntryDecoder(DSSPublicKeyEntryDecoder.INSTANCE);
-
-        if (SecurityUtils.isECCSupported()) {
-            registerPublicKeyEntryDecoder(ECDSAPublicKeyEntryDecoder.INSTANCE);
-        }
-        if (SecurityUtils.isEDDSACurveSupported()) {
-            registerPublicKeyEntryDecoder(SecurityUtils.getEDDSAPublicKeyEntryDecoder());
-        }
-    }
-
-    private KeyUtils() {
-        throw new UnsupportedOperationException("No instance");
-    }
-
-    /**
-     * <P>Checks if a path has strict permissions</P>
-     * <UL>
-     * <LI><P>
-     * The path may not have {@link PosixFilePermission#OTHERS_EXECUTE}
-     * permission
-     * </P></LI>
-     *
-     * <LI><P>
-     * (For {@code Unix}) The path may not have group or others permissions
-     * </P></LI>
-     *
-     * <LI><P>
-     * (For {@code Unix}) If the path is a file, then its folder may not have
-     * group or others permissions
-     * </P></LI>
-     *
-     * <LI><P>
-     * The path must be owned by current user.
-     * </P></LI>
-     *
-     * <LI><P>
-     * (For {@code Unix}) The path may be owned by root.
-     * </P></LI>
-     *
-     * <LI><P>
-     * (For {@code Unix}) If the path is a file, then its folder must also
-     * have valid owner.
-     * </P></LI>
-     *
-     * </UL>
-     *
-     * @param path    The {@link Path} to be checked - ignored if {@code null}
-     *                or does not exist
-     * @param options The {@link LinkOption}s to use to query the file's permissions
-     * @return The violated permission as {@link SimpleImmutableEntry} where key is a message and
-     * value is the offending object {@link PosixFilePermission} or {@link String} for owner - {@code null}
-     * if no violations detected
-     * @throws IOException If failed to retrieve the permissions
-     * @see #STRICTLY_PROHIBITED_FILE_PERMISSION
-     */
-    public static SimpleImmutableEntry<String, Object> validateStrictKeyFilePermissions(Path path, LinkOption... options) throws IOException {
-        if ((path == null) || (!Files.exists(path, options))) {
-            return null;
-        }
-
-        Collection<PosixFilePermission> perms = IoUtils.getPermissions(path, options);
-        if (GenericUtils.isEmpty(perms)) {
-            return null;
-        }
-
-        if (perms.contains(PosixFilePermission.OTHERS_EXECUTE)) {
-            PosixFilePermission p = PosixFilePermission.OTHERS_EXECUTE;
-            return new SimpleImmutableEntry<>(String.format("Permissions violation (%s)", p), p);
-        }
-
-        if (OsUtils.isUNIX()) {
-            PosixFilePermission p = IoUtils.validateExcludedPermissions(perms, STRICTLY_PROHIBITED_FILE_PERMISSION);
-            if (p != null) {
-                return new SimpleImmutableEntry<>(String.format("Permissions violation (%s)", p), p);
-            }
-
-            if (Files.isRegularFile(path, options)) {
-                Path parent = path.getParent();
-                p = IoUtils.validateExcludedPermissions(IoUtils.getPermissions(parent, options), STRICTLY_PROHIBITED_FILE_PERMISSION);
-                if (p != null) {
-                    return new SimpleImmutableEntry<>(String.format("Parent permissions violation (%s)", p), p);
-                }
-            }
-        }
-
-        String owner = IoUtils.getFileOwner(path, options);
-        if (GenericUtils.isEmpty(owner)) {
-            // we cannot get owner
-            // general issue: jvm does not support permissions
-            // security issue: specific filesystem does not support permissions
-            return null;
-        }
-
-        String current = OsUtils.getCurrentUser();
-        Set<String> expected = new HashSet<>();
-        expected.add(current);
-        if (OsUtils.isUNIX()) {
-            // Windows "Administrator" was considered however in Windows most likely a group is used.
-            expected.add(OsUtils.ROOT_USER);
-        }
-
-        if (!expected.contains(owner)) {
-            return new SimpleImmutableEntry<>(String.format("Owner violation (%s)", owner), owner);
-        }
-
-        if (OsUtils.isUNIX()) {
-            if (Files.isRegularFile(path, options)) {
-                String parentOwner = IoUtils.getFileOwner(path.getParent(), options);
-                if ((!GenericUtils.isEmpty(parentOwner)) && (!expected.contains(parentOwner))) {
-                    return new SimpleImmutableEntry<>(String.format("Parent owner violation (%s)", parentOwner), parentOwner);
-                }
-            }
-        }
-
-        return null;
-    }
-
-    /**
-     * @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) {
-        Objects.requireNonNull(decoder, "No decoder specified");
-
-        Class<?> pubType = Objects.requireNonNull(decoder.getPublicKeyType(), "No public key type declared");
-        Class<?> prvType = Objects.requireNonNull(decoder.getPrivateKeyType(), "No private key type declared");
-        synchronized (BY_KEY_CLASS_DECODERS_MAP) {
-            BY_KEY_CLASS_DECODERS_MAP.put(pubType, decoder);
-            BY_KEY_CLASS_DECODERS_MAP.put(prvType, decoder);
-        }
-
-        Collection<String> names = ValidateUtils.checkNotNullAndNotEmpty(decoder.getSupportedTypeNames(), "No supported key type");
-        synchronized (BY_KEY_TYPE_DECODERS_MAP) {
-            for (String n : names) {
-                PublicKeyEntryDecoder<?, ?> prev = BY_KEY_TYPE_DECODERS_MAP.put(n, decoder);
-                if (prev != null) {
-                    //noinspection UnnecessaryContinue
-                    continue;   // debug breakpoint
-                }
-            }
-        }
-    }
-
-    /**
-     * @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<?, ?> getPublicKeyEntryDecoder(String keyType) {
-        if (GenericUtils.isEmpty(keyType)) {
-            return null;
-        }
-
-        synchronized (BY_KEY_TYPE_DECODERS_MAP) {
-            return BY_KEY_TYPE_DECODERS_MAP.get(keyType);
-        }
-    }
-
-    /**
-     * @param kp The {@link KeyPair} to examine - ignored if {@code null}
-     * @return The matching {@link PublicKeyEntryDecoder} provided <U>both</U>
-     * the public and private keys have the same decoder - {@code null} if no
-     * match found
-     * @see #getPublicKeyEntryDecoder(Key)
-     */
-    public static PublicKeyEntryDecoder<?, ?> getPublicKeyEntryDecoder(KeyPair kp) {
-        if (kp == null) {
-            return null;
-        }
-
-        PublicKeyEntryDecoder<?, ?> d1 = getPublicKeyEntryDecoder(kp.getPublic());
-        PublicKeyEntryDecoder<?, ?> d2 = getPublicKeyEntryDecoder(kp.getPrivate());
-        if (d1 == d2) {
-            return d1;
-        } else {
-            return null;    // some kind of mixed keys...
-        }
-    }
-
-    /**
-     * @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<?, ?> getPublicKeyEntryDecoder(Key key) {
-        if (key == null) {
-            return null;
-        } else {
-            return getPublicKeyEntryDecoder(key.getClass());
-        }
-    }
-
-    /**
-     * @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<?, ?> getPublicKeyEntryDecoder(Class<?> keyType) {
-        if ((keyType == null) || (!Key.class.isAssignableFrom(keyType))) {
-            return null;
-        }
-
-        synchronized (BY_KEY_TYPE_DECODERS_MAP) {
-            PublicKeyEntryDecoder<?, ?> decoder = BY_KEY_CLASS_DECODERS_MAP.get(keyType);
-            if (decoder != null) {
-                return decoder;
-            }
-
-            // in case it is a derived class
-            for (PublicKeyEntryDecoder<?, ?> dec : BY_KEY_CLASS_DECODERS_MAP.values()) {
-                Class<?> pubType = dec.getPublicKeyType();
-                Class<?> prvType = dec.getPrivateKeyType();
-                if (pubType.isAssignableFrom(keyType) || prvType.isAssignableFrom(keyType)) {
-                    return dec;
-                }
-            }
-        }
-
-        return null;
-    }
-
-    /**
-     * @return The default {@link DigestFactory}
-     * by the {@link #getFingerPrint(PublicKey)} and {@link #getFingerPrint(String)}
-     * methods
-     * @see #KEY_FINGERPRINT_FACTORY_PROP
-     * @see #setDefaultFingerPrintFactory(DigestFactory)
-     */
-    public static DigestFactory getDefaultFingerPrintFactory() {
-        DigestFactory factory = null;
-        synchronized (DEFAULT_DIGEST_HOLDER) {
-            factory = DEFAULT_DIGEST_HOLDER.get();
-            if (factory != null) {
-                return factory;
-            }
-
-            String propVal = System.getProperty(KEY_FINGERPRINT_FACTORY_PROP);
-            if (GenericUtils.isEmpty(propVal)) {
-                factory = DEFAULT_FINGERPRINT_DIGEST_FACTORY;
-            } else {
-                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);
-        }
-
-        return factory;
-    }
-
-    /**
-     * @param f The {@link DigestFactory} of {@link Digest}s to be used - may
-     *          not be {@code null}
-     */
-    public static void setDefaultFingerPrintFactory(DigestFactory f) {
-        synchronized (DEFAULT_DIGEST_HOLDER) {
-            DEFAULT_DIGEST_HOLDER.set(Objects.requireNonNull(f, "No digest factory"));
-        }
-    }
-
-    /**
-     * @param key the public key - ignored if {@code null}
-     * @return the fingerprint or {@code null} if no key.
-     * <B>Note:</B> if exception encountered then returns the exception's simple class name
-     * @see #getFingerPrint(Factory, PublicKey)
-     */
-    public static String getFingerPrint(PublicKey key) {
-        return getFingerPrint(getDefaultFingerPrintFactory(), key);
-    }
-
-    /**
-     * @param password The {@link String} to digest - ignored if {@code null}/empty,
-     *                 otherwise its UTF-8 representation is used as input for the fingerprint
-     * @return The fingerprint - {@code null} if {@code null}/empty input.
-     * <B>Note:</B> if exception encountered then returns the exception's simple class name
-     * @see #getFingerPrint(String, Charset)
-     */
-    public static String getFingerPrint(String password) {
-        return getFingerPrint(password, StandardCharsets.UTF_8);
-    }
-
-    /**
-     * @param password The {@link String} to digest - ignored if {@code null}/empty
-     * @param charset  The {@link Charset} to use in order to convert the
-     *                 string to its byte representation to use as input for the fingerprint
-     * @return The fingerprint - {@code null} if {@code null}/empty input.
-     * <B>Note:</B> if exception encountered then returns the exception's simple class name
-     * @see #getFingerPrint(Factory, String, Charset)
-     * @see #getDefaultFingerPrintFactory()
-     */
-    public static String getFingerPrint(String password, Charset charset) {
-        return getFingerPrint(getDefaultFingerPrintFactory(), password, charset);
-    }
-
-    /**
-     * @param f   The {@link Factory} to create the {@link Digest} to use
-     * @param key the public key - ignored if {@code null}
-     * @return the fingerprint or {@code null} if no key.
-     * <B>Note:</B> if exception encountered then returns the exception's simple class name
-     * @see #getFingerPrint(Digest, PublicKey)
-     */
-    public static String getFingerPrint(Factory<? extends Digest> f, PublicKey key) {
-        return (key == null) ? null : getFingerPrint(Objects.requireNonNull(f, "No digest factory").create(), key);
-    }
-
-    /**
-     * @param d   The {@link Digest} to use
-     * @param key the public key - ignored if {@code null}
-     * @return the fingerprint or {@code null} if no key.
-     * <B>Note:</B> if exception encountered then returns the exception's simple class name
-     * @see DigestUtils#getFingerPrint(Digest, byte[], int, int)
-     */
-    public static String getFingerPrint(Digest d, PublicKey key) {
-        if (key == null) {
-            return null;
-        }
-
-        try {
-            Buffer buffer = new ByteArrayBuffer();
-            buffer.putRawPublicKey(key);
-            return DigestUtils.getFingerPrint(d, buffer.array(), 0, buffer.wpos());
-        } catch (Exception e) {
-            return e.getClass().getSimpleName();
-        }
-    }
-
-    public static byte[] getRawFingerprint(PublicKey key) throws Exception {
-        return getRawFingerprint(getDefaultFingerPrintFactory(), key);
-    }
-
-    public static byte[] getRawFingerprint(Factory<? extends Digest> f, PublicKey key) throws Exception {
-        return (key == null) ? null : getRawFingerprint(Objects.requireNonNull(f, "No digest factory").create(), key);
-    }
-
-    public static byte[] getRawFingerprint(Digest d, PublicKey key) throws Exception {
-        if (key == null) {
-            return null;
-        }
-
-        Buffer buffer = new ByteArrayBuffer();
-        buffer.putRawPublicKey(key);
-        return DigestUtils.getRawFingerprint(d, buffer.array(), 0, buffer.wpos());
-    }
-
-    /**
-     * @param f The {@link Factory} to create the {@link Digest} to use
-     * @param s The {@link String} to digest - ignored if {@code null}/empty,
-     *          otherwise its UTF-8 representation is used as input for the fingerprint
-     * @return The fingerprint - {@code null} if {@code null}/empty input.
-     * <B>Note:</B> if exception encountered then returns the exception's simple class name
-     * @see #getFingerPrint(Digest, String, Charset)
-     */
-    public static String getFingerPrint(Factory<? extends Digest> f, String s) {
-        return getFingerPrint(f, s, StandardCharsets.UTF_8);
-    }
-
-    /**
-     * @param f       The {@link Factory} to create the {@link Digest} to use
-     * @param s       The {@link String} to digest - ignored if {@code null}/empty
-     * @param charset The {@link Charset} to use in order to convert the
-     *                string to its byte representation to use as input for the fingerprint
-     * @return The fingerprint - {@code null} if {@code null}/empty input
-     * <B>Note:</B> if exception encountered then returns the exception's simple class name
-     * @see DigestUtils#getFingerPrint(Digest, String, Charset)
-     */
-    public static String getFingerPrint(Factory<? extends Digest> f, String s, Charset charset) {
-        return getFingerPrint(f.create(), s, charset);
-    }
-
-    /**
-     * @param d The {@link Digest} to use
-     * @param s The {@link String} to digest - ignored if {@code null}/empty,
-     *          otherwise its UTF-8 representation is used as input for the fingerprint
-     * @return The fingerprint - {@code null} if {@code null}/empty input.
-     * <B>Note:</B> if exception encountered then returns the exception's simple class name
-     * @see DigestUtils#getFingerPrint(Digest, String, Charset)
-     */
-    public static String getFingerPrint(Digest d, String s) {
-        return getFingerPrint(d, s, StandardCharsets.UTF_8);
-    }
-
-    /**
-     * @param d       The {@link Digest} to use to calculate the fingerprint
-     * @param s       The string to digest - ignored if {@code null}/empty
-     * @param charset The {@link Charset} to use in order to convert the
-     *                string to its byte representation to use as input for the fingerprint
-     * @return The fingerprint - {@code null} if {@code null}/empty input.
-     * <B>Note:</B> if exception encountered then returns the exception's simple class name
-     * @see DigestUtils#getFingerPrint(Digest, String, Charset)
-     */
-    public static String getFingerPrint(Digest d, String s, Charset charset) {
-        if (GenericUtils.isEmpty(s)) {
-            return null;
-        }
-
-        try {
-            return DigestUtils.getFingerPrint(d, s, charset);
-        } catch (Exception e) {
-            return e.getClass().getSimpleName();
-        }
-    }
-
-    /**
-     * @param expected The expected fingerprint if {@code null} or empty then returns a failure
-     * with the default fingerprint.
-     * @param key the {@link PublicKey} - if {@code null} then returns null.
-     * @return SimpleImmutableEntry<Boolean, String> - key is success indicator, value is actual fingerprint,
-     * {@code null} if no key.
-     * @see #getDefaultFingerPrintFactory()
-     * @see #checkFingerPrint(String, Factory, PublicKey)
-     */
-    public static SimpleImmutableEntry<Boolean, String> checkFingerPrint(String expected, PublicKey key) {
-        return checkFingerPrint(expected, getDefaultFingerPrintFactory(), key);
-    }
-
-    /**
-     * @param expected The expected fingerprint if {@code null} or empty then returns a failure
-     * with the default fingerprint.
-     * @param f The {@link Factory} to be used to generate the default {@link Digest} for the key
-     * @param key the {@link PublicKey} - if {@code null} then returns null.
-     * @return SimpleImmutableEntry<Boolean, String> - key is success indicator, value is actual fingerprint,
-     * {@code null} if no key.
-     */
-    public static SimpleImmutableEntry<Boolean, String> checkFingerPrint(String expected, Factory<? extends Digest> f, PublicKey key) {
-        return checkFingerPrint(expected, Objects.requireNonNull(f, "No digest factory").create(), key);
-    }
-
-    /**
-     * @param expected The expected fingerprint if {@code null} or empty then returns a failure
-     * with the default fingerprint.
-     * @param d The {@link Digest} to be used to generate the default fingerprint for the key
-     * @param key the {@link PublicKey} - if {@code null} then returns null.
-     * @return SimpleImmutableEntry<Boolean, String> - key is success indicator, value is actual fingerprint,
-     * {@code null} if no key.
-     */
-    public static SimpleImmutableEntry<Boolean, String> checkFingerPrint(String expected, Digest d, PublicKey key) {
-        if (key == null) {
-            return null;
-        }
-
-        if (GenericUtils.isEmpty(expected)) {
-            return new SimpleImmutableEntry<>(false, getFingerPrint(d, key));
-        }
-
-        // de-construct fingerprint
-        int pos = expected.indexOf(':');
-        if ((pos < 0) || (pos >= (expected.length() - 1))) {
-            return new SimpleImmutableEntry<>(false, getFingerPrint(d, key));
-        }
-
-        String name = expected.substring(0, pos);
-        String value = expected.substring(pos + 1);
-        DigestFactory expectedFactory;
-        // We know that all digest names have a length > 2 - if 2 (or less) then assume a pure HEX value
-        if (name.length() > 2) {
-            expectedFactory = BuiltinDigests.fromFactoryName(name);
-            if (expectedFactory == null) {
-                return new SimpleImmutableEntry<>(false, getFingerPrint(d, key));
-            }
-
-            expected = name.toUpperCase() + ":" + value;
-        } else {
-            expectedFactory = BuiltinDigests.md5;
-            expected = expectedFactory.getName().toUpperCase() + ":" + expected;
-        }
-
-        String fingerprint = getFingerPrint(expectedFactory, key);
-        boolean matches = BuiltinDigests.md5.getName().equals(expectedFactory.getName())
-                        ? expected.equalsIgnoreCase(fingerprint)    // HEX is case insensitive
-                        : expected.equals(fingerprint);
-        return new SimpleImmutableEntry<>(matches, fingerprint);
-    }
-
-    /**
-     * @param kp a key pair - ignored if {@code null}. If the private
-     *           key is non-{@code null} then it is used to determine the type,
-     *           otherwise the public one is used.
-     * @return the key type or {@code null} if cannot determine it
-     * @see #getKeyType(Key)
-     */
-    public static String getKeyType(KeyPair kp) {
-        if (kp == null) {
-            return null;
-        }
-        PrivateKey key = kp.getPrivate();
-        if (key != null) {
-            return getKeyType(key);
-        } else {
-            return getKeyType(kp.getPublic());
-        }
-    }
-
-    /**
-     * @param key a public or private key
-     * @return the key type or {@code null} if cannot determine it
-     */
-    public static String getKeyType(Key key) {
-        if (key == null) {
-            return null;
-        } else if (key instanceof DSAKey) {
-            return KeyPairProvider.SSH_DSS;
-        } else if (key instanceof RSAKey) {
-            return KeyPairProvider.SSH_RSA;
-        } else if (key instanceof ECKey) {
-            ECKey ecKey = (ECKey) key;
-            ECParameterSpec ecSpec = ecKey.getParams();
-            ECCurves curve = ECCurves.fromCurveParameters(ecSpec);
-            if (curve == null) {
-                return null;    // debug breakpoint
-            } else {
-                return curve.getKeyType();
-            }
-        } else if (SecurityUtils.EDDSA.equalsIgnoreCase(key.getAlgorithm())) {
-            return KeyPairProvider.SSH_ED25519;
-        }
-
-        return null;
-    }
-
-    /**
-     * Determines the key size in bits
-     *
-     * @param key The {@link Key} to examine - ignored if {@code null}
-     * @return The key size - non-positive value if cannot determine it
-     */
-    public static int getKeySize(Key key) {
-        if (key == null) {
-            return -1;
-        } else if (key instanceof RSAKey) {
-            BigInteger n = ((RSAKey) key).getModulus();
-            return n.bitLength();
-        } else if (key instanceof DSAKey) {
-            DSAParams params = ((DSAKey) key).getParams();
-            BigInteger p = params.getP();
-            return p.bitLength();
-        } else if (key instanceof ECKey) {
-            ECParameterSpec ecSpec = ((ECKey) key).getParams();
-            ECCurves curve = ECCurves.fromCurveParameters(ecSpec);
-            if (curve != null) {
-                return curve.getKeySize();
-            }
-        } else if (SecurityUtils.EDDSA.equalsIgnoreCase(key.getAlgorithm())) {
-            return SecurityUtils.getEDDSAKeySize(key);
-        }
-
-        return -1;
-    }
-
-    /**
-     * @param key    The {@link PublicKey} to be checked - ignored if {@code null}
-     * @param keySet The keys to be searched - ignored if {@code null}/empty
-     * @return The matching {@link PublicKey} from the keys or {@code null} if
-     * no match found
-     * @see #compareKeys(PublicKey, PublicKey)
-     */
-    public static PublicKey findMatchingKey(PublicKey key, PublicKey... keySet) {
-        if (key == null || GenericUtils.isEmpty(keySet)) {
-            return null;
-        } else {
-            return findMatchingKey(key, Arrays.asList(keySet));
-        }
-    }
-
-    /**
-     * @param key    The {@link PublicKey} to be checked - ignored if {@code null}
-     * @param keySet The keys to be searched - ignored if {@code null}/empty
-     * @return The matching {@link PublicKey} from the keys or {@code null} if
-     * no match found
-     * @see #compareKeys(PublicKey, PublicKey)
-     */
-    public static PublicKey findMatchingKey(PublicKey key, Collection<? extends PublicKey> keySet) {
-        if (key == null || GenericUtils.isEmpty(keySet)) {
-            return null;
-        }
-        for (PublicKey k : keySet) {
-            if (compareKeys(key, k)) {
-                return k;
-            }
-        }
-        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
-        } else {
-            return compareKeys(k1.getPublic(), k2.getPublic())
-                && compareKeys(k1.getPrivate(), k2.getPrivate());
-        }
-    }
-
-    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));
-        } else if ((k1 instanceof DSAPublicKey) && (k2 instanceof DSAPublicKey)) {
-            return compareDSAKeys(DSAPublicKey.class.cast(k1), DSAPublicKey.class.cast(k2));
-        } else if ((k1 instanceof ECPublicKey) && (k2 instanceof ECPublicKey)) {
-            return compareECKeys(ECPublicKey.class.cast(k1), ECPublicKey.class.cast(k2));
-        } else if ((k1 != null) && SecurityUtils.EDDSA.equalsIgnoreCase(k1.getAlgorithm())
-                    && (k2 != null) && SecurityUtils.EDDSA.equalsIgnoreCase(k2.getAlgorithm())) {
-            return SecurityUtils.compareEDDSAPPublicKeys(k1, k2);
-        } else {
-            return false;   // either key is null or not of same class
-        }
-    }
-
-    public static PublicKey recoverPublicKey(PrivateKey key) throws GeneralSecurityException {
-        if (key instanceof RSAPrivateKey) {
-            return recoverRSAPublicKey((RSAPrivateKey) key);
-        } else if (key instanceof DSAPrivateKey) {
-            return recoverDSAPublicKey((DSAPrivateKey) key);
-        } else if ((key != null) && SecurityUtils.EDDSA.equalsIgnoreCase(key.getAlgorithm())) {
-            return SecurityUtils.recoverEDDSAPublicKey(key);
-        } else {
-            return null;
-        }
-    }
-
-    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 if ((k1 != null) && SecurityUtils.EDDSA.equalsIgnoreCase(k1.getAlgorithm())
-                    && (k2 != null) && SecurityUtils.EDDSA.equalsIgnoreCase(k2.getAlgorithm())) {
-            return SecurityUtils.compareEDDSAPrivateKeys(k1, k2);
-        } else {
-            return false;   // either key is null or not of same class
-        }
-    }
-
-    public static boolean compareRSAKeys(RSAPublicKey k1, RSAPublicKey k2) {
-        if (Objects.equals(k1, k2)) {
-            return true;
-        } else if (k1 == null || k2 == null) {
-            return false;   // both null is covered by Objects#equals
-        } else {
-            return Objects.equals(k1.getPublicExponent(), k2.getPublicExponent())
-                && Objects.equals(k1.getModulus(), k2.getModulus());
-        }
-    }
-
-    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 {
-            return Objects.equals(k1.getModulus(), k2.getModulus())
-                && Objects.equals(k1.getPrivateExponent(), k2.getPrivateExponent());
-        }
-    }
-
-    public static RSAPublicKey recoverRSAPublicKey(RSAPrivateKey privateKey) throws GeneralSecurityException {
-        if (privateKey instanceof RSAPrivateCrtKey) {
-            return recoverFromRSAPrivateCrtKey((RSAPrivateCrtKey) privateKey);
-        } else {
-            // Not ideal, but best we can do under the circumstances
-            return recoverRSAPublicKey(privateKey.getModulus(), DEFAULT_RSA_PUBLIC_EXPONENT);
-        }
-    }
-
-    public static RSAPublicKey recoverFromRSAPrivateCrtKey(RSAPrivateCrtKey rsaKey) throws GeneralSecurityException {
-        return recoverRSAPublicKey(rsaKey.getPrimeP(), rsaKey.getPrimeQ(), rsaKey.getPublicExponent());
-    }
-
-    public static RSAPublicKey recoverRSAPublicKey(BigInteger p, BigInteger q, BigInteger publicExponent) throws GeneralSecurityException {
-        return recoverRSAPublicKey(p.multiply(q), publicExponent);
-    }
-
-    public static RSAPublicKey recoverRSAPublicKey(BigInteger modulus, BigInteger publicExponent) throws GeneralSecurityException {
-        KeyFactory kf = SecurityUtils.getKeyFactory(RSA_ALGORITHM);
-        return (RSAPublicKey) kf.generatePublic(new RSAPublicKeySpec(modulus, publicExponent));
-    }
-
-    public static boolean compareDSAKeys(DSAPublicKey k1, DSAPublicKey k2) {
-        if (Objects.equals(k1, k2)) {
-            return true;
-        } else if (k1 == null || k2 == null) {
-            return false;   // both null is covered by Objects#equals
-        } else {
-            return Objects.equals(k1.getY(), k2.getY())
-                && compareDSAParams(k1.getParams(), k2.getParams());
-        }
-    }
-
-    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 {
-            return Objects.equals(k1.getX(), k2.getX())
-                && compareDSAParams(k1.getParams(), k2.getParams());
-        }
-    }
-
-    public static boolean compareDSAParams(DSAParams p1, DSAParams p2) {
-        if (Objects.equals(p1, p2)) {
-            return true;
-        } else if (p1 == null || p2 == null) {
-            return false;   // both null is covered by Objects#equals
-        } else {
-            return Objects.equals(p1.getG(), p2.getG())
-                && Objects.equals(p1.getP(), p2.getP())
-                && Objects.equals(p1.getQ(), p2.getQ());
-        }
-    }
-
-    // based on code from https://github.com/alexo/SAML-2.0/blob/master/java-opensaml/opensaml-security-api/src/main/java/org/opensaml/xml/security/SecurityHelper.java
-    public static DSAPublicKey recoverDSAPublicKey(DSAPrivateKey privateKey) throws GeneralSecurityException {
-        DSAParams keyParams = privateKey.getParams();
-        BigInteger p = keyParams.getP();
-        BigInteger x = privateKey.getX();
-        BigInteger q = keyParams.getQ();
-        BigInteger g = keyParams.getG();
-        BigInteger y = g.modPow(x, p);
-        KeyFactory kf = SecurityUtils.getKeyFactory(DSS_ALGORITHM);
-        return (DSAPublicKey) kf.generatePublic(new DSAPublicKeySpec(y, p, q, g));
-    }
-
-    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 {
-            return Objects.equals(k1.getS(), k2.getS())
-                && compareECParams(k1.getParams(), k2.getParams());
-        }
-    }
-
-    public static boolean compareECKeys(ECPublicKey k1, ECPublicKey k2) {
-        if (Objects.equals(k1, k2)) {
-            return true;
-        } else if (k1 == null || k2 == null) {
-            return false;   // both null is covered by Objects#equals
-        } else {
-            return Objects.equals(k1.getW(), k2.getW())
-                && compareECParams(k1.getParams(), k2.getParams());
-        }
-    }
-
-    public static boolean compareECParams(ECParameterSpec s1, ECParameterSpec s2) {
-        if (Objects.equals(s1, s2)) {
-            return true;
-        } else if (s1 == null || s2 == null) {
-            return false;   // both null is covered by Objects#equals
-        } else {
-            return Objects.equals(s1.getOrder(), s2.getOrder())
-                && (s1.getCofactor() == s2.getCofactor())
-                && Objects.equals(s1.getGenerator(), s2.getGenerator())
-                && Objects.equals(s1.getCurve(), s2.getCurve());
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/PrivateKeyEntryDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/PrivateKeyEntryDecoder.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/PrivateKeyEntryDecoder.java
deleted file mode 100644
index 6dbeee9..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/PrivateKeyEntryDecoder.java
+++ /dev/null
@@ -1,142 +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.config.keys;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.StreamCorruptedException;
-import java.security.GeneralSecurityException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.spec.InvalidKeySpecException;
-import java.util.Collection;
-import java.util.Objects;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.NumberUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-
-/**
- * @param <PUB> Type of {@link PublicKey}
- * @param <PRV> Type of {@link PrivateKey}
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface PrivateKeyEntryDecoder<PUB extends PublicKey, PRV extends PrivateKey>
-            extends KeyEntryResolver<PUB, PRV>, PrivateKeyEntryResolver {
-
-    @Override
-    default PrivateKey resolve(String keyType, byte[] keyData) throws IOException, GeneralSecurityException {
-        ValidateUtils.checkNotNullAndNotEmpty(keyType, "No key type provided");
-        Collection<String> supported = getSupportedTypeNames();
-        if ((GenericUtils.size(supported) > 0) && supported.contains(keyType)) {
-            return decodePrivateKey(FilePasswordProvider.EMPTY, keyData);
-        }
-
-        throw new InvalidKeySpecException("resolve(" + keyType + ") not in listed supported types: " + supported);
-    }
-
-    /**
-     * @param passwordProvider The {@link FilePasswordProvider} to use
-     * in case the data is encrypted - may be {@code null} if no encrypted
-     * data is expected
-     * @param keyData The key data bytes in {@code OpenSSH} format (after
-     *                BASE64 decoding) - ignored if {@code null}/empty
-     * @return The decoded {@link PrivateKey} - or {@code null} if no data
-     * @throws IOException              If failed to decode the key
-     * @throws GeneralSecurityException If failed to generate the key
-     */
-    default PRV decodePrivateKey(FilePasswordProvider passwordProvider, byte... keyData)
-            throws IOException, GeneralSecurityException {
-        return decodePrivateKey(passwordProvider, keyData, 0, NumberUtils.length(keyData));
-    }
-
-    default PRV decodePrivateKey(FilePasswordProvider passwordProvider, byte[] keyData, int offset, int length)
-            throws IOException, GeneralSecurityException {
-        if (length <= 0) {
-            return null;
-        }
-
-        try (InputStream stream = new ByteArrayInputStream(keyData, offset, length)) {
-            return decodePrivateKey(passwordProvider, stream);
-        }
-    }
-
-    default PRV decodePrivateKey(FilePasswordProvider passwordProvider, InputStream keyData)
-            throws IOException, GeneralSecurityException {
-        // the actual data is preceded by a string that repeats the key type
-        String type = KeyEntryResolver.decodeString(keyData);
-        if (GenericUtils.isEmpty(type)) {
-            throw new StreamCorruptedException("Missing key type string");
-        }
-
-        Collection<String> supported = getSupportedTypeNames();
-        if (GenericUtils.isEmpty(supported) || (!supported.contains(type))) {
-            throw new InvalidKeySpecException("Reported key type (" + type + ") not in supported list: " + supported);
-        }
-
-        return decodePrivateKey(type, passwordProvider, keyData);
-    }
-
-    /**
-     * @param keyType The reported / encode key type
-     * @param passwordProvider The {@link FilePasswordProvider} to use
-     * in case the data is encrypted - may be {@code null} if no encrypted
-     * data is expected
-     * @param keyData The key data bytes stream positioned after the key type decoding
-     *                and making sure it is one of the supported types
-     * @return The decoded {@link PrivateKey}
-     * @throws IOException              If failed to read from the data stream
-     * @throws GeneralSecurityException If failed to generate the key
-     */
-    PRV decodePrivateKey(String keyType, FilePasswordProvider passwordProvider, InputStream keyData)
-            throws IOException, GeneralSecurityException;
-
-    /**
-     * Encodes the {@link PrivateKey} using the {@code OpenSSH} format - same
-     * one used by the {@code decodePublicKey} method(s)
-     *
-     * @param s   The {@link OutputStream} to write the data to
-     * @param key The {@link PrivateKey} - may not be {@code null}
-     * @return The key type value - one of the {@link #getSupportedTypeNames()} or
-     * {@code null} if encoding not supported
-     * @throws IOException If failed to generate the encoding
-     */
-    default String encodePrivateKey(OutputStream s, PRV key) throws IOException {
-        Objects.requireNonNull(key, "No private key provided");
-        return null;
-    }
-
-    default boolean isPublicKeyRecoverySupported() {
-        return false;
-    }
-
-    /**
-     * Attempts to recover the public key given the private one
-     *
-     * @param prvKey The {@link PrivateKey}
-     * @return The recovered {@link PublicKey} - {@code null} if cannot recover it
-     * @throws GeneralSecurityException If failed to generate the public key
-     */
-    default PUB recoverPublicKey(PRV prvKey) throws GeneralSecurityException {
-        return null;
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/PrivateKeyEntryResolver.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/PrivateKeyEntryResolver.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/PrivateKeyEntryResolver.java
deleted file mode 100644
index 1e4c91e..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/PrivateKeyEntryResolver.java
+++ /dev/null
@@ -1,70 +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.config.keys;
-
-import java.io.IOException;
-import java.security.GeneralSecurityException;
-import java.security.PrivateKey;
-import java.security.spec.InvalidKeySpecException;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FunctionalInterface
-public interface PrivateKeyEntryResolver {
-    /**
-     * A resolver that ignores all input
-     */
-    PrivateKeyEntryResolver IGNORING = new PrivateKeyEntryResolver() {
-        @Override
-        public PrivateKey resolve(String keyType, byte[] keyData) throws IOException, GeneralSecurityException {
-            return null;
-        }
-
-        @Override
-        public String toString() {
-            return "IGNORING";
-        }
-    };
-
-    /**
-     * A resolver that fails on all input
-     */
-    PrivateKeyEntryResolver FAILING = new PrivateKeyEntryResolver() {
-        @Override
-        public PrivateKey resolve(String keyType, byte[] keyData) throws IOException, GeneralSecurityException {
-            throw new InvalidKeySpecException("Failing resolver on key type=" + keyType);
-        }
-
-        @Override
-        public String toString() {
-            return "FAILING";
-        }
-    };
-
-    /**
-     * @param keyType The {@code OpenSSH} reported key type
-     * @param keyData The {@code OpenSSH} encoded key data
-     * @return The extracted {@link PrivateKey} - ignored if {@code null}
-     * @throws IOException If failed to parse the key data
-     * @throws GeneralSecurityException If failed to generate the key
-     */
-    PrivateKey resolve(String keyType, byte[] keyData) throws IOException, GeneralSecurityException;
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/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
deleted file mode 100644
index 1db3d2b..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntry.java
+++ /dev/null
@@ -1,286 +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.config.keys;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.Serializable;
-import java.io.StreamCorruptedException;
-import java.nio.file.Path;
-import java.security.GeneralSecurityException;
-import java.security.PublicKey;
-import java.security.spec.InvalidKeySpecException;
-import java.util.Arrays;
-import java.util.Base64;
-import java.util.Objects;
-
-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
- * the <A HREF="http://en.wikibooks.org/wiki/OpenSSH">OpenSSH</A> format:</P>
- *
- * <PRE>
- * &lt;key-type&gt; &lt;base64-encoded-public-key-data&gt;
- * </PRE>
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class PublicKeyEntry implements Serializable {
-
-    /**
-     * Character used to denote a comment line in the keys file
-     */
-    public static final char COMMENT_CHAR = '#';
-
-
-    /**
-     * Standard folder name used by OpenSSH to hold key files
-     */
-    public static final String STD_KEYFILE_FOLDER_NAME = ".ssh";
-
-    private static final long serialVersionUID = -585506072687602760L;
-
-    private String keyType;
-    private byte[] keyData;
-
-    public PublicKeyEntry() {
-        super();
-    }
-
-    public PublicKeyEntry(String keyType, byte... keyData) {
-        this.keyType = keyType;
-        this.keyData = keyData;
-    }
-
-    public String getKeyType() {
-        return keyType;
-    }
-
-    public void setKeyType(String value) {
-        this.keyType = value;
-    }
-
-    public byte[] getKeyData() {
-        return keyData;
-    }
-
-    public void setKeyData(byte[] value) {
-        this.keyData = value;
-    }
-
-    /**
-     * @param fallbackResolver The {@link PublicKeyEntryResolver} to consult if
-     * none of the built-in ones can be used. If {@code null} and no built-in
-     * resolver can be used then an {@link InvalidKeySpecException} is thrown.
-     * @return The resolved {@link PublicKey} - or {@code null} if could not be
-     * resolved. <B>Note:</B> may be called only after key type and data bytes
-     * have been set or exception(s) may be thrown
-     * @throws IOException              If failed to decode the key
-     * @throws GeneralSecurityException If failed to generate the key
-     */
-    public PublicKey resolvePublicKey(PublicKeyEntryResolver fallbackResolver) throws IOException, GeneralSecurityException {
-        String kt = getKeyType();
-        PublicKeyEntryResolver decoder = KeyUtils.getPublicKeyEntryDecoder(kt);
-        if (decoder == null) {
-            decoder = fallbackResolver;
-        }
-        if (decoder == null) {
-            throw new InvalidKeySpecException("No decoder available for key type=" + kt);
-        }
-
-        return decoder.resolve(kt, getKeyData());
-    }
-
-    /**
-     * @param sb The {@link Appendable} instance to encode the data into
-     * @param fallbackResolver The {@link PublicKeyEntryResolver} to consult if
-     * none of the built-in ones can be used. If {@code null} and no built-in
-     * resolver can be used then an {@link InvalidKeySpecException} is thrown.
-     * @return The {@link PublicKey} or {@code null} if could not resolve it
-     * @throws IOException              If failed to decode/encode the key
-     * @throws GeneralSecurityException If failed to generate the key
-     * @see #resolvePublicKey(PublicKeyEntryResolver)
-     */
-    public PublicKey appendPublicKey(Appendable sb, PublicKeyEntryResolver fallbackResolver) throws IOException, GeneralSecurityException {
-        PublicKey key = resolvePublicKey(fallbackResolver);
-        if (key != null) {
-            appendPublicKeyEntry(sb, key);
-        }
-        return key;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hashCode(getKeyType()) + Arrays.hashCode(getKeyData());
-    }
-
-    /*
-     * In case some derived class wants to define some "extended" equality
-     * without having to repeat this code
-     */
-    protected boolean isEquivalent(PublicKeyEntry e) {
-        if (this == e) {
-            return true;
-        }
-        return Objects.equals(getKeyType(), e.getKeyType())
-            && Arrays.equals(getKeyData(), e.getKeyData());
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (obj == null) {
-            return false;
-        }
-        if (this == obj) {
-            return true;
-        }
-        if (getClass() != obj.getClass()) {
-            return false;
-        }
-        return isEquivalent((PublicKeyEntry) obj);
-    }
-
-    @Override
-    public String toString() {
-        byte[] data = getKeyData();
-        Base64.Encoder encoder = Base64.getEncoder();
-        return getKeyType() + " " + (NumberUtils.isEmpty(data) ? "<no-key>" : encoder.encodeToString(data));
-    }
-
-    /**
-     * @param encData Assumed to contain at least {@code key-type base64-data}
-     * (anything beyond the BASE64 data is ignored) - ignored if {@code null}/empty
-     * @return A {@link PublicKeyEntry} or {@code null} if no data
-     * @throws IllegalArgumentException if bad format found
-     * @see #parsePublicKeyEntry(PublicKeyEntry, String)
-     */
-    public static PublicKeyEntry parsePublicKeyEntry(String encData) throws IllegalArgumentException {
-        String data = GenericUtils.replaceWhitespaceAndTrim(encData);
-        if (GenericUtils.isEmpty(data)) {
-            return null;
-        } else {
-            return parsePublicKeyEntry(new PublicKeyEntry(), data);
-        }
-    }
-
-    /**
-     * @param <E>   The generic entry type
-     * @param entry The {@link PublicKeyEntry} whose contents are to be
-     *              updated - ignored if {@code null}
-     * @param encData Assumed to contain at least {@code key-type base64-data} (anything
-     *              beyond the BASE64 data is ignored) - ignored if {@code null}/empty
-     * @return The updated entry instance
-     * @throws IllegalArgumentException if bad format found
-     */
-    public static <E extends PublicKeyEntry> E parsePublicKeyEntry(E entry, String encData) throws IllegalArgumentException {
-        String data = GenericUtils.replaceWhitespaceAndTrim(encData);
-        if (GenericUtils.isEmpty(data) || (entry == null)) {
-            return entry;
-        }
-
-        int startPos = data.indexOf(' ');
-        if (startPos <= 0) {
-            throw new IllegalArgumentException("Bad format (no key data delimiter): " + data);
-        }
-
-        int endPos = data.indexOf(' ', startPos + 1);
-        if (endPos <= startPos) {   // OK if no continuation beyond the BASE64 encoded data
-            endPos = data.length();
-        }
-
-        String keyType = data.substring(0, startPos);
-        String b64Data = data.substring(startPos + 1, endPos).trim();
-        Base64.Decoder decoder = Base64.getDecoder();
-        byte[] keyData = decoder.decode(b64Data);
-        if (NumberUtils.isEmpty(keyData)) {
-            throw new IllegalArgumentException("Bad format (no BASE64 key data): " + data);
-        }
-
-        entry.setKeyType(keyType);
-        entry.setKeyData(keyData);
-        return entry;
-    }
-
-    /**
-     * @param key The {@link PublicKey}
-     * @return The {@code OpenSSH} encoded data
-     * @throws IllegalArgumentException If failed to encode
-     * @see #appendPublicKeyEntry(Appendable, PublicKey)
-     */
-    public static String toString(PublicKey key) throws IllegalArgumentException {
-        try {
-            return appendPublicKeyEntry(new StringBuilder(Byte.MAX_VALUE), key).toString();
-        } catch (IOException e) {
-            throw new IllegalArgumentException("Failed (" + e.getClass().getSimpleName() + ") to encode: " + e.getMessage(), e);
-        }
-    }
-
-    /**
-     * Encodes a public key data the same way as the {@link #parsePublicKeyEntry(String)} expects it
-     *
-     * @param <A> The generic appendable class
-     * @param sb  The {@link Appendable} instance to encode the data into
-     * @param key The {@link PublicKey} - ignored if {@code null}
-     * @return The updated appendable instance
-     * @throws IOException If failed to append the data
-     */
-    public static <A extends Appendable> A appendPublicKeyEntry(A sb, PublicKey key) throws IOException {
-        if (key == null) {
-            return sb;
-        }
-
-        @SuppressWarnings("unchecked")
-        PublicKeyEntryDecoder<PublicKey, ?> decoder =
-            (PublicKeyEntryDecoder<PublicKey, ?>) KeyUtils.getPublicKeyEntryDecoder(key);
-        if (decoder == null) {
-            throw new StreamCorruptedException("Cannot retrieve decoder for key=" + key.getAlgorithm());
-        }
-
-        try (ByteArrayOutputStream s = new ByteArrayOutputStream(Byte.MAX_VALUE)) {
-            String keyType = decoder.encodePublicKey(s, key);
-            byte[] bytes = s.toByteArray();
-            Base64.Encoder encoder = Base64.getEncoder();
-            String b64Data = encoder.encodeToString(bytes);
-            sb.append(keyType).append(' ').append(b64Data);
-        }
-
-        return sb;
-    }
-
-    private static final class LazyDefaultKeysFolderHolder {
-        private static final Path PATH =
-            IdentityUtils.getUserHomeFolder().resolve(STD_KEYFILE_FOLDER_NAME);
-
-        private LazyDefaultKeysFolderHolder() {
-            throw new UnsupportedOperationException("No instance allowed");
-        }
-    }
-
-    /**
-     * @return The default OpenSSH folder used to hold key files - e.g.,
-     * {@code known_hosts}, {@code authorized_keys}, etc.
-     */
-    @SuppressWarnings("synthetic-access")
-    public static Path getDefaultKeysFolderPath() {
-        return LazyDefaultKeysFolderHolder.PATH;
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/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
deleted file mode 100644
index 7462e4a..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntryDecoder.java
+++ /dev/null
@@ -1,114 +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.config.keys;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.StreamCorruptedException;
-import java.security.GeneralSecurityException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.spec.InvalidKeySpecException;
-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;
-
-/**
- * Represents a decoder of an {@code OpenSSH} encoded key data
- *
- * @param <PUB> Type of {@link PublicKey}
- * @param <PRV> Type of {@link PrivateKey}
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface PublicKeyEntryDecoder<PUB extends PublicKey, PRV extends PrivateKey>
-        extends KeyEntryResolver<PUB, PRV>, PublicKeyEntryResolver {
-
-    @Override
-    default PublicKey resolve(String keyType, byte[] keyData) throws IOException, GeneralSecurityException {
-        ValidateUtils.checkNotNullAndNotEmpty(keyType, "No key type provided");
-        Collection<String> supported = getSupportedTypeNames();
-        if ((GenericUtils.size(supported) > 0) && supported.contains(keyType)) {
-            return decodePublicKey(keyData);
-        }
-
-        throw new InvalidKeySpecException("resolve(" + keyType + ") not in listed supported types: " + supported);
-    }
-
-    /**
-     * @param keyData The key data bytes in {@code OpenSSH} format (after
-     *                BASE64 decoding) - ignored if {@code null}/empty
-     * @return The decoded {@link PublicKey} - or {@code null} if no data
-     * @throws IOException              If failed to decode the key
-     * @throws GeneralSecurityException If failed to generate the key
-     */
-    default PUB decodePublicKey(byte... keyData) throws IOException, GeneralSecurityException {
-        return decodePublicKey(keyData, 0, NumberUtils.length(keyData));
-    }
-
-    default PUB decodePublicKey(byte[] keyData, int offset, int length) throws IOException, GeneralSecurityException {
-        if (length <= 0) {
-            return null;
-        }
-
-        try (InputStream stream = new ByteArrayInputStream(keyData, offset, length)) {
-            return decodePublicKey(stream);
-        }
-    }
-
-    default PUB decodePublicKey(InputStream keyData) throws IOException, GeneralSecurityException {
-        // the actual data is preceded by a string that repeats the key type
-        String type = KeyEntryResolver.decodeString(keyData);
-        if (GenericUtils.isEmpty(type)) {
-            throw new StreamCorruptedException("Missing key type string");
-        }
-
-        Collection<String> supported = getSupportedTypeNames();
-        if (GenericUtils.isEmpty(supported) || (!supported.contains(type))) {
-            throw new InvalidKeySpecException("Reported key type (" + type + ") not in supported list: " + supported);
-        }
-
-        return decodePublicKey(type, keyData);
-    }
-
-    /**
-     * @param keyType The reported / encode key type
-     * @param keyData The key data bytes stream positioned after the key type decoding
-     *                and making sure it is one of the supported types
-     * @return The decoded {@link PublicKey}
-     * @throws IOException              If failed to read from the data stream
-     * @throws GeneralSecurityException If failed to generate the key
-     */
-    PUB decodePublicKey(String keyType, InputStream keyData) throws IOException, GeneralSecurityException;
-
-    /**
-     * Encodes the {@link PublicKey} using the {@code OpenSSH} format - same
-     * one used by the {@code decodePublicKey} method(s)
-     *
-     * @param s   The {@link OutputStream} to write the data to
-     * @param key The {@link PublicKey} - may not be {@code null}
-     * @return The key type value - one of the {@link #getSupportedTypeNames()}
-     * @throws IOException If failed to generate the encoding
-     */
-    String encodePublicKey(OutputStream s, PUB key) throws IOException;
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntryResolver.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntryResolver.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntryResolver.java
deleted file mode 100644
index e5eb0ed..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntryResolver.java
+++ /dev/null
@@ -1,70 +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.config.keys;
-
-import java.io.IOException;
-import java.security.GeneralSecurityException;
-import java.security.PublicKey;
-import java.security.spec.InvalidKeySpecException;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FunctionalInterface
-public interface PublicKeyEntryResolver {
-    /**
-     * A resolver that ignores all input
-     */
-    PublicKeyEntryResolver IGNORING = new PublicKeyEntryResolver() {
-        @Override
-        public PublicKey resolve(String keyType, byte[] keyData) throws IOException, GeneralSecurityException {
-            return null;
-        }
-
-        @Override
-        public String toString() {
-            return "IGNORING";
-        }
-    };
-
-    /**
-     * A resolver that fails on all input
-     */
-    PublicKeyEntryResolver FAILING = new PublicKeyEntryResolver() {
-        @Override
-        public PublicKey resolve(String keyType, byte[] keyData) throws IOException, GeneralSecurityException {
-            throw new InvalidKeySpecException("Failing resolver on key type=" + keyType);
-        }
-
-        @Override
-        public String toString() {
-            return "FAILING";
-        }
-    };
-
-    /**
-     * @param keyType The {@code OpenSSH} reported key type
-     * @param keyData The {@code OpenSSH} encoded key data
-     * @return The extracted {@link PublicKey} - ignored if {@code null}
-     * @throws IOException If failed to parse the key data
-     * @throws GeneralSecurityException If failed to generate the key
-     */
-    PublicKey resolve(String keyType, byte[] keyData) throws IOException, GeneralSecurityException;
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractIdentityResourceLoader.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractIdentityResourceLoader.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractIdentityResourceLoader.java
deleted file mode 100644
index a2412bd..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractIdentityResourceLoader.java
+++ /dev/null
@@ -1,62 +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.config.keys.impl;
-
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.util.Collection;
-import java.util.Objects;
-
-import org.apache.sshd.common.config.keys.IdentityResourceLoader;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.logging.AbstractLoggingBean;
-
-/**
- * @param <PUB> Generic public key type
- * @param <PRV> Generic private key type
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class AbstractIdentityResourceLoader<PUB extends PublicKey, PRV extends PrivateKey>
-        extends AbstractLoggingBean
-        implements IdentityResourceLoader<PUB, PRV> {
-    private final Class<PUB> pubType;
-    private final Class<PRV> prvType;
-    private final Collection<String> names;
-
-    protected AbstractIdentityResourceLoader(Class<PUB> pubType, Class<PRV> prvType, Collection<String> names) {
-        this.pubType = Objects.requireNonNull(pubType, "No public key type specified");
-        this.prvType = Objects.requireNonNull(prvType, "No private key type specified");
-        this.names = ValidateUtils.checkNotNullAndNotEmpty(names, "No type names provided");
-    }
-
-    @Override
-    public final Class<PUB> getPublicKeyType() {
-        return pubType;
-    }
-
-    @Override
-    public final Class<PRV> getPrivateKeyType() {
-        return prvType;
-    }
-
-    @Override
-    public Collection<String> getSupportedTypeNames() {
-        return names;
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractKeyEntryResolver.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractKeyEntryResolver.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractKeyEntryResolver.java
deleted file mode 100644
index 7afa3a6..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractKeyEntryResolver.java
+++ /dev/null
@@ -1,59 +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.config.keys.impl;
-
-import java.security.GeneralSecurityException;
-import java.security.KeyFactory;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.spec.KeySpec;
-import java.util.Collection;
-
-import org.apache.sshd.common.config.keys.KeyEntryResolver;
-
-/**
- * @param <PUB> Type of {@link PublicKey}
- * @param <PRV> Type of {@link PrivateKey}
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class AbstractKeyEntryResolver<PUB extends PublicKey, PRV extends PrivateKey>
-            extends AbstractIdentityResourceLoader<PUB, PRV>
-            implements KeyEntryResolver<PUB, PRV>  {
-    protected AbstractKeyEntryResolver(Class<PUB> pubType, Class<PRV> prvType, Collection<String> names) {
-        super(pubType, prvType, names);
-    }
-
-    public PUB generatePublicKey(KeySpec keySpec) throws GeneralSecurityException {
-        KeyFactory factory = getKeyFactoryInstance();
-        Class<PUB> keyType = getPublicKeyType();
-        return keyType.cast(factory.generatePublic(keySpec));
-    }
-
-    public PRV generatePrivateKey(KeySpec keySpec) throws GeneralSecurityException {
-        KeyFactory factory = getKeyFactoryInstance();
-        Class<PRV> keyType = getPrivateKeyType();
-        return keyType.cast(factory.generatePrivate(keySpec));
-    }
-
-    @Override
-    public String toString() {
-        return getPublicKeyType().getSimpleName() + ": " + getSupportedTypeNames();
-    }
-}


[39/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common utilities code from sshd-core into sshd-common (new artifact)

Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/SelectorUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/SelectorUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/util/SelectorUtils.java
new file mode 100644
index 0000000..53bca90
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/SelectorUtils.java
@@ -0,0 +1,805 @@
+/*
+ * 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.nio.file.FileSystem;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.StringTokenizer;
+
+/**
+ * <p>This is a utility class used by selectors and DirectoryScanner. The
+ * functionality more properly belongs just to selectors, but unfortunately
+ * DirectoryScanner exposed these as protected methods. Thus we have to
+ * support any subclasses of DirectoryScanner that may access these methods.
+ * </p>
+ * <p>This is a Singleton.</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>
+ * @version $Id$
+ * @since 1.5
+ */
+public final class SelectorUtils {
+
+    public static final String PATTERN_HANDLER_PREFIX = "[";
+
+    public static final String PATTERN_HANDLER_SUFFIX = "]";
+
+    public static final String REGEX_HANDLER_PREFIX = "%regex" + PATTERN_HANDLER_PREFIX;
+
+    public static final String ANT_HANDLER_PREFIX = "%ant" + PATTERN_HANDLER_PREFIX;
+
+    /**
+     * Private Constructor
+     */
+    private SelectorUtils() {
+        throw new UnsupportedOperationException("No instance allowed");
+    }
+
+    /**
+     * <p>Tests whether or not a given path matches the start of a given
+     * pattern up to the first "**".</p>
+     *
+     * <p>This is not a general purpose test and should only be used if you
+     * can live with false positives. For example, <code>pattern=**\a</code>
+     * and <code>str=b</code> will yield <code>true</code>.</p>
+     *
+     * @param pattern The pattern to match against. Must not be
+     *                {@code null}.
+     * @param str     The path to match, as a String. Must not be
+     *                {@code null}.
+     * @return whether or not a given path matches the start of a given
+     * pattern up to the first "**".
+     */
+    public static boolean matchPatternStart(String pattern, String str) {
+        return matchPatternStart(pattern, str, true);
+    }
+
+    /**
+     * <p>Tests whether or not a given path matches the start of a given
+     * pattern up to the first "**".</p>
+     *
+     * <p>This is not a general purpose test and should only be used if you
+     * can live with false positives. For example, <code>pattern=**\a</code>
+     * and <code>str=b</code> will yield <code>true</code>.</p>
+     *
+     * @param pattern         The pattern to match against. Must not be
+     *                        {@code null}.
+     * @param str             The path to match, as a String. Must not be
+     *                        {@code null}.
+     * @param isCaseSensitive Whether or not matching should be performed
+     *                        case sensitively.
+     * @return whether or not a given path matches the start of a given
+     * pattern up to the first "**".
+     */
+    public static boolean matchPatternStart(String pattern, String str,
+                                            boolean isCaseSensitive) {
+        if (pattern.length() > (REGEX_HANDLER_PREFIX.length() + PATTERN_HANDLER_SUFFIX.length() + 1)
+                && pattern.startsWith(REGEX_HANDLER_PREFIX) && pattern.endsWith(PATTERN_HANDLER_SUFFIX)) {
+            // FIXME: ICK! But we can't do partial matches for regex, so we have to reserve judgement until we have
+            // a file to deal with, or we can definitely say this is an exclusion...
+            return true;
+        } else {
+            if (pattern.length() > (ANT_HANDLER_PREFIX.length() + PATTERN_HANDLER_SUFFIX.length() + 1)
+                    && pattern.startsWith(ANT_HANDLER_PREFIX) && pattern.endsWith(PATTERN_HANDLER_SUFFIX)) {
+                pattern =
+                        pattern.substring(ANT_HANDLER_PREFIX.length(), pattern.length() - PATTERN_HANDLER_SUFFIX.length());
+            }
+
+            String altStr = str.replace('\\', '/');
+
+            return matchAntPathPatternStart(pattern, str, File.separator, isCaseSensitive)
+                    || matchAntPathPatternStart(pattern, altStr, "/", isCaseSensitive);
+        }
+    }
+
+    private static boolean matchAntPathPatternStart(String pattern, String str, String separator, boolean isCaseSensitive) {
+        // When str starts with a File.separator, pattern has to start with a
+        // File.separator.
+        // When pattern starts with a File.separator, str has to start with a
+        // File.separator.
+        if (str.startsWith(separator) != pattern.startsWith(separator)) {
+            return false;
+        }
+
+        List<String> patDirs = tokenizePath(pattern, separator);
+        List<String> strDirs = tokenizePath(str, separator);
+
+        int patIdxStart = 0;
+        int patIdxEnd = patDirs.size() - 1;
+        int strIdxStart = 0;
+        int strIdxEnd = strDirs.size() - 1;
+
+        // up to first '**'
+        while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
+            String patDir = patDirs.get(patIdxStart);
+            if (patDir.equals("**")) {
+                break;
+            }
+            if (!match(patDir, strDirs.get(strIdxStart),
+                    isCaseSensitive)) {
+                return false;
+            }
+            patIdxStart++;
+            strIdxStart++;
+        }
+
+        // CHECKSTYLE:OFF
+        if (strIdxStart > strIdxEnd) {
+            // String is exhausted
+            return true;
+        } else {
+            return patIdxStart <= patIdxEnd;
+        }
+        // CHECKSTYLE:ON
+    }
+
+    /**
+     * Tests whether or not a given path matches a given pattern.
+     *
+     * @param pattern The pattern to match against. Must not be
+     *                {@code null}.
+     * @param str     The path to match, as a String. Must not be
+     *                {@code null}.
+     * @return <code>true</code> if the pattern matches against the string,
+     * or <code>false</code> otherwise.
+     */
+    public static boolean matchPath(String pattern, String str) {
+        return matchPath(pattern, str, true);
+    }
+
+    /**
+     * Tests whether or not a given path matches a given pattern.
+     *
+     * @param pattern         The pattern to match against. Must not be
+     *                        {@code null}.
+     * @param str             The path to match, as a String. Must not be
+     *                        {@code null}.
+     * @param isCaseSensitive Whether or not matching should be performed
+     *                        case sensitively.
+     * @return <code>true</code> if the pattern matches against the string,
+     * or <code>false</code> otherwise.
+     */
+    public static boolean matchPath(String pattern, String str, boolean isCaseSensitive) {
+        if (pattern.length() > (REGEX_HANDLER_PREFIX.length() + PATTERN_HANDLER_SUFFIX.length() + 1)
+                && pattern.startsWith(REGEX_HANDLER_PREFIX) && pattern.endsWith(PATTERN_HANDLER_SUFFIX)) {
+            pattern = pattern.substring(REGEX_HANDLER_PREFIX.length(), pattern.length()
+                    - PATTERN_HANDLER_SUFFIX.length());
+
+            return str.matches(pattern);
+        } else {
+            if (pattern.length() > (ANT_HANDLER_PREFIX.length() + PATTERN_HANDLER_SUFFIX.length() + 1)
+                    && pattern.startsWith(ANT_HANDLER_PREFIX) && pattern.endsWith(PATTERN_HANDLER_SUFFIX)) {
+                pattern =
+                        pattern.substring(ANT_HANDLER_PREFIX.length(), pattern.length() - PATTERN_HANDLER_SUFFIX.length());
+            }
+
+            return matchAntPathPattern(pattern, str, isCaseSensitive);
+        }
+    }
+
+    private static boolean matchAntPathPattern(String pattern, String str, boolean isCaseSensitive) {
+        // When str starts with a File.separator, pattern has to start with a
+        // File.separator.
+        // When pattern starts with a File.separator, str has to start with a
+        // File.separator.
+        if (str.startsWith(File.separator) != pattern.startsWith(File.separator)) {
+            return false;
+        }
+
+        List<String> patDirs = tokenizePath(pattern, File.separator);
+        List<String> strDirs = tokenizePath(str, File.separator);
+
+        int patIdxStart = 0;
+        int patIdxEnd = patDirs.size() - 1;
+        int strIdxStart = 0;
+        int strIdxEnd = strDirs.size() - 1;
+
+        // up to first '**'
+        while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
+            String patDir = patDirs.get(patIdxStart);
+            if (patDir.equals("**")) {
+                break;
+            }
+            if (!match(patDir, strDirs.get(strIdxStart),
+                    isCaseSensitive)) {
+                patDirs = null;
+                strDirs = null;
+                return false;
+            }
+            patIdxStart++;
+            strIdxStart++;
+        }
+        if (strIdxStart > strIdxEnd) {
+            // String is exhausted
+            for (int i = patIdxStart; i <= patIdxEnd; i++) {
+                if (!patDirs.get(i).equals("**")) {
+                    patDirs = null;
+                    strDirs = null;
+                    return false;
+                }
+            }
+            return true;
+        } else {
+            if (patIdxStart > patIdxEnd) {
+                // String not exhausted, but pattern is. Failure.
+                patDirs = null;
+                strDirs = null;
+                return false;
+            }
+        }
+
+        // up to last '**'
+        while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
+            String patDir = patDirs.get(patIdxEnd);
+            if (patDir.equals("**")) {
+                break;
+            }
+            if (!match(patDir, strDirs.get(strIdxEnd),
+                    isCaseSensitive)) {
+                patDirs = null;
+                strDirs = null;
+                return false;
+            }
+            patIdxEnd--;
+            strIdxEnd--;
+        }
+        if (strIdxStart > strIdxEnd) {
+            // String is exhausted
+            for (int i = patIdxStart; i <= patIdxEnd; i++) {
+                if (!patDirs.get(i).equals("**")) {
+                    patDirs = null;
+                    strDirs = null;
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) {
+            int patIdxTmp = -1;
+            for (int i = patIdxStart + 1; i <= patIdxEnd; i++) {
+                if (patDirs.get(i).equals("**")) {
+                    patIdxTmp = i;
+                    break;
+                }
+            }
+            if (patIdxTmp == patIdxStart + 1) {
+                // '**/**' situation, so skip one
+                patIdxStart++;
+                continue;
+            }
+            // Find the pattern between padIdxStart & padIdxTmp in str between
+            // strIdxStart & strIdxEnd
+            int patLength = patIdxTmp - patIdxStart - 1;
+            int strLength = strIdxEnd - strIdxStart + 1;
+            int foundIdx = -1;
+            strLoop:
+            for (int i = 0; i <= strLength - patLength; i++) {
+                for (int j = 0; j < patLength; j++) {
+                    String subPat = patDirs.get(patIdxStart + j + 1);
+                    String subStr = strDirs.get(strIdxStart + i + j);
+                    if (!match(subPat, subStr, isCaseSensitive)) {
+                        continue strLoop;
+                    }
+                }
+
+                foundIdx = strIdxStart + i;
+                break;
+            }
+
+            if (foundIdx == -1) {
+                patDirs = null;
+                strDirs = null;
+                return false;
+            }
+
+            patIdxStart = patIdxTmp;
+            strIdxStart = foundIdx + patLength;
+        }
+
+        for (int i = patIdxStart; i <= patIdxEnd; i++) {
+            if (!patDirs.get(i).equals("**")) {
+                patDirs = null;
+                strDirs = null;
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Tests whether or not a string matches against a pattern.
+     * The pattern may contain two special characters:<br>
+     * '*' means zero or more characters<br>
+     * '?' means one and only one character
+     *
+     * @param pattern The pattern to match against.
+     *                Must not be {@code null}.
+     * @param str     The string which must be matched against the pattern.
+     *                Must not be {@code null}.
+     * @return <code>true</code> if the string matches against the pattern,
+     * or <code>false</code> otherwise.
+     */
+    public static boolean match(String pattern, String str) {
+        return match(pattern, str, true);
+    }
+
+    /**
+     * Tests whether or not a string matches against a pattern.
+     * The pattern may contain two special characters:<br>
+     * '*' means zero or more characters<br>
+     * '?' means one and only one character
+     *
+     * @param pattern         The pattern to match against.
+     *                        Must not be {@code null}.
+     * @param str             The string which must be matched against the pattern.
+     *                        Must not be {@code null}.
+     * @param isCaseSensitive Whether or not matching should be performed
+     *                        case sensitively.
+     * @return <code>true</code> if the string matches against the pattern,
+     * or <code>false</code> otherwise.
+     */
+    @SuppressWarnings("PMD.AssignmentInOperand")
+    public static boolean match(String pattern, String str, boolean isCaseSensitive) {
+        char[] patArr = pattern.toCharArray();
+        char[] strArr = str.toCharArray();
+        int patIdxStart = 0;
+        int patIdxEnd = patArr.length - 1;
+        int strIdxStart = 0;
+        int strIdxEnd = strArr.length - 1;
+        char ch;
+
+        boolean containsStar = false;
+        for (char aPatArr : patArr) {
+            if (aPatArr == '*') {
+                containsStar = true;
+                break;
+            }
+        }
+
+        if (!containsStar) {
+            // No '*'s, so we make a shortcut
+            if (patIdxEnd != strIdxEnd) {
+                return false; // Pattern and string do not have the same size
+            }
+            for (int i = 0; i <= patIdxEnd; i++) {
+                ch = patArr[i];
+                if ((ch != '?') && (!equals(ch, strArr[i], isCaseSensitive))) {
+                    return false; // Character mismatch
+                }
+            }
+            return true; // String matches against pattern
+        }
+
+        if (patIdxEnd == 0) {
+            return true; // Pattern contains only '*', which matches anything
+        }
+
+        // Process characters before first star
+        // CHECKSTYLE:OFF
+        while (((ch = patArr[patIdxStart]) != '*') && (strIdxStart <= strIdxEnd)) {
+            if ((ch != '?') && (!equals(ch, strArr[strIdxStart], isCaseSensitive))) {
+                return false; // Character mismatch
+            }
+            patIdxStart++;
+            strIdxStart++;
+        }
+        // CHECKSTYLE:ON
+
+        if (strIdxStart > strIdxEnd) {
+            // All characters in the string are used. Check if only '*'s are
+            // left in the pattern. If so, we succeeded. Otherwise failure.
+            for (int i = patIdxStart; i <= patIdxEnd; i++) {
+                if (patArr[i] != '*') {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        // Process characters after last star
+        // CHECKSTYLE:OFF
+        while (((ch = patArr[patIdxEnd]) != '*') && (strIdxStart <= strIdxEnd)) {
+            if ((ch != '?') && (!equals(ch, strArr[strIdxEnd], isCaseSensitive))) {
+                return false; // Character mismatch
+            }
+            patIdxEnd--;
+            strIdxEnd--;
+        }
+        // CHECKSTYLE:ON
+
+        if (strIdxStart > strIdxEnd) {
+            // All characters in the string are used. Check if only '*'s are
+            // left in the pattern. If so, we succeeded. Otherwise failure.
+            for (int i = patIdxStart; i <= patIdxEnd; i++) {
+                if (patArr[i] != '*') {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        // process pattern between stars. padIdxStart and patIdxEnd point always to a '*'.
+        while ((patIdxStart != patIdxEnd) && (strIdxStart <= strIdxEnd)) {
+            int patIdxTmp = -1;
+            for (int i = patIdxStart + 1; i <= patIdxEnd; i++) {
+                if (patArr[i] == '*') {
+                    patIdxTmp = i;
+                    break;
+                }
+            }
+            if (patIdxTmp == patIdxStart + 1) {
+                // Two stars next to each other, skip the first one.
+                patIdxStart++;
+                continue;
+            }
+            // Find the pattern between padIdxStart & padIdxTmp in str between
+            // strIdxStart & strIdxEnd
+            int patLength = patIdxTmp - patIdxStart - 1;
+            int strLength = strIdxEnd - strIdxStart + 1;
+            int foundIdx = -1;
+            strLoop:
+            for (int i = 0; i <= strLength - patLength; i++) {
+                for (int j = 0; j < patLength; j++) {
+                    ch = patArr[patIdxStart + j + 1];
+                    if (ch != '?' && !equals(ch, strArr[strIdxStart + i + j], isCaseSensitive)) {
+                        continue strLoop;
+                    }
+                }
+
+                foundIdx = strIdxStart + i;
+                break;
+            }
+
+            if (foundIdx == -1) {
+                return false;
+            }
+
+            patIdxStart = patIdxTmp;
+            strIdxStart = foundIdx + patLength;
+        }
+
+        // All characters in the string are used. Check if only '*'s are left
+        // in the pattern. If so, we succeeded. Otherwise failure.
+        for (int i = patIdxStart; i <= patIdxEnd; i++) {
+            if (patArr[i] != '*') {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Tests whether two characters are equal.
+     * @param c1 1st character
+     * @param c2 2nd character
+     * @param isCaseSensitive Whether to compare case sensitive
+     * @return {@code true} if equal characters
+     */
+    public static boolean equals(char c1, char c2, boolean isCaseSensitive) {
+        if (c1 == c2) {
+            return true;
+        }
+        if (!isCaseSensitive) {
+            // NOTE: Try both upper case and lower case as done by String.equalsIgnoreCase()
+            if (Character.toUpperCase(c1) == Character.toUpperCase(c2)
+                    || Character.toLowerCase(c1) == Character.toLowerCase(c2)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Breaks a path up into a Vector of path elements, tokenizing on
+     * <code>File.separator</code>.
+     *
+     * @param path Path to tokenize. Must not be {@code null}.
+     * @return a List of path elements from the tokenized path
+     */
+    public static List<String> tokenizePath(String path) {
+        return tokenizePath(path, File.separator);
+    }
+
+    public static List<String> tokenizePath(String path, String separator) {
+        List<String> ret = new ArrayList<>();
+        StringTokenizer st = new StringTokenizer(path, separator);
+        while (st.hasMoreTokens()) {
+            ret.add(st.nextToken());
+        }
+        return ret;
+    }
+
+    /**   /**
+     * Converts a path to one matching the target file system by applying the
+     * &quot;slashification&quot; rules, converting it to a local path and
+     * then translating its separator to the target file system one (if different
+     * than local one)
+     * @param path          The input path
+     * @param pathSeparator The separator used to build the input path
+     * @param fs            The target {@link FileSystem} - may not be {@code null}
+     * @return The transformed path
+     * @see #translateToLocalFileSystemPath(String, char, String)
+     */
+    public static String translateToLocalFileSystemPath(String path, char pathSeparator, FileSystem fs) {
+        return translateToLocalFileSystemPath(path, pathSeparator,  Objects.requireNonNull(fs, "No target file system").getSeparator());
+    }
+
+    /**
+     * Converts a path to one matching the target file system by applying the
+     * &quot;slashification&quot; rules, converting it to a local path and
+     * then translating its separator to the target file system one (if different
+     * than local one)
+     * @param path          The input path
+     * @param pathSeparator The separator used to build the input path
+     * @param fsSeparator   The target file system separator
+     * @return The transformed path
+     * @see #applySlashifyRules(String, char)
+     * @see #translateToLocalPath(String)
+     * @see #translateToFileSystemPath(String, String, String)
+     */
+    public static String translateToLocalFileSystemPath(String path, char pathSeparator, String fsSeparator) {
+        // In case double slashes and other patterns are used
+        String slashified = applySlashifyRules(path, pathSeparator);
+        // In case we are running on Windows
+        String localPath = translateToLocalPath(slashified);
+        return translateToFileSystemPath(localPath, File.separator, fsSeparator);
+    }
+
+    /**
+     * Applies the &quot;slashification&quot; rules as specified in
+     * <A HREF="http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_266">Single Unix Specification version 3, section 3.266</A>
+     * and <A HREF="http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap04.html#tag_04_11">section 4.11 - Pathname resolution</A>
+     * @param path The original path - ignored if {@code null}/empty or does
+     * not contain any slashes
+     * @param sepChar The &quot;slash&quot; character
+     * @return The effective path - may be same as input if no changes required
+     */
+    public static String applySlashifyRules(String path, char sepChar) {
+        if (GenericUtils.isEmpty(path)) {
+            return path;
+        }
+
+        int curPos = path.indexOf(sepChar);
+        if (curPos < 0) {
+            return path;    // no slashes to handle
+        }
+
+        int lastPos = 0;
+        StringBuilder sb = null;
+        while (curPos < path.length()) {
+            curPos++;   // skip the 1st '/'
+
+            /*
+             * As per Single Unix Specification version 3, section 3.266:
+             *
+             *      Multiple successive slashes are considered to be the
+             *      same as one slash
+             */
+            int nextPos = curPos;
+            while ((nextPos < path.length()) && (path.charAt(nextPos) == sepChar)) {
+                nextPos++;
+            }
+
+            /*
+             * At this stage, nextPos is the first non-slash character after a
+             * possibly 'seqLen' sequence of consecutive slashes.
+             */
+            int seqLen = nextPos - curPos;
+            if (seqLen > 0) {
+                if (sb == null) {
+                    sb = new StringBuilder(path.length() - seqLen);
+                }
+
+                if (lastPos < curPos) {
+                    String clrText = path.substring(lastPos, curPos);
+                    sb.append(clrText);
+                }
+
+                lastPos = nextPos;
+            }
+
+            if (nextPos >= path.length()) {
+                break;  // no more data
+            }
+
+            curPos = path.indexOf(sepChar, nextPos);
+            if (curPos < nextPos) {
+                break;  // no more slashes
+            }
+        }
+
+        // check if any leftovers for the modified path
+        if (sb != null) {
+            if (lastPos < path.length()) {
+                String clrText = path.substring(lastPos);
+                sb.append(clrText);
+            }
+
+            path = sb.toString();
+        }
+
+        /*
+         * At this point we know for sure that 'path' contains only SINGLE
+         * slashes. According to section 4.11 - Pathname resolution
+         *
+         *      A pathname that contains at least one non-slash character
+         *      and that ends with one or more trailing slashes shall be
+         *      resolved as if a single dot character ( '.' ) were appended
+         *      to the pathname.
+         */
+        if ((path.length() > 1) && (path.charAt(path.length() - 1) == sepChar)) {
+            return path + ".";
+        } else {
+            return path;
+        }
+    }
+
+    /**
+     * Converts a possibly '/' separated path to a local path. <B>Note:</B>
+     * takes special care of Windows drive paths - e.g., {@code C:}
+     * by converting them to &quot;C:\&quot;
+     *
+     * @param path The original path - ignored if {@code null}/empty
+     * @return The local path
+     */
+    public static String translateToLocalPath(String path) {
+        if (GenericUtils.isEmpty(path) || (File.separatorChar == '/')) {
+            return path;
+        }
+
+        // This code is reached if we are running on Windows
+        String localPath = path.replace('/', File.separatorChar);
+        // check if '/c:' prefix
+        if ((localPath.charAt(0) == File.separatorChar) && isWindowsDriveSpecified(localPath, 1, localPath.length() - 1)) {
+            localPath = localPath.substring(1);
+        }
+        if (!isWindowsDriveSpecified(localPath)) {
+            return localPath;   // assume a relative path
+        }
+
+        /*
+         * Here we know that we have at least a "C:" string - make sure it
+         * is followed by the local file separator. Note: if all we have is
+         * just the drive, we will create a "C:\" path since this is the
+         * preferred Windows way to refer to root drives in the file system
+         */
+        if (localPath.length() == 2) {
+            return localPath + File.separator;  // all we have is "C:"
+        } else if (localPath.charAt(2) != File.separatorChar) {
+            // be nice and add the missing file separator - C:foo => C:\foo
+            return localPath.substring(0, 2) + File.separator + localPath.substring(2);
+        } else {
+            return localPath;
+        }
+    }
+
+    public static boolean isWindowsDriveSpecified(CharSequence cs) {
+        return isWindowsDriveSpecified(cs, 0, GenericUtils.length(cs));
+    }
+
+    public static boolean isWindowsDriveSpecified(CharSequence cs, int offset, int len) {
+        if ((len < 2) || (cs.charAt(offset + 1) != ':')) {
+            return false;
+        }
+
+        char drive = cs.charAt(offset);
+        return ((drive >= 'a') && (drive <= 'z')) || ((drive >= 'A') && (drive <= 'Z'));
+    }
+
+    /**
+     * Converts a path containing a specific separator to one using the
+     * specified file-system one
+     * @param path          The input path - ignored if {@code null}/empty
+     * @param pathSeparator The separator used to build the input path - may not
+     *                      be {@code null}/empty
+     * @param fs            The target {@link FileSystem} - may not be {@code null}
+     * @return The path where the separator used to build it is replaced by
+     * the file-system one (if different)
+     * @see FileSystem#getSeparator()
+     * @see #translateToFileSystemPath(String, String, String)
+     */
+    public static String translateToFileSystemPath(String path, String pathSeparator, FileSystem fs) {
+        return translateToFileSystemPath(path, pathSeparator, Objects.requireNonNull(fs, "No target file system").getSeparator());
+    }
+
+    /**
+     * Converts a path containing a specific separator to one using the
+     * specified file-system one
+     * @param path          The input path - ignored if {@code null}/empty
+     * @param pathSeparator The separator used to build the input path - may not
+     *                      be {@code null}/empty
+     * @param fsSeparator   The target file system separator - may not be {@code null}/empty
+     * @return The path where the separator used to build it is replaced by
+     * the file-system one (if different)
+     * @throws IllegalArgumentException if path or file-system separator are {@code null}/empty
+     * or if the separators are different and the path contains the target
+     * file-system separator as it would create an ambiguity
+     */
+    public static String translateToFileSystemPath(String path, String pathSeparator, String fsSeparator) {
+        ValidateUtils.checkNotNullAndNotEmpty(pathSeparator, "Missing path separator");
+        ValidateUtils.checkNotNullAndNotEmpty(fsSeparator, "Missing file-system separator");
+
+        if (GenericUtils.isEmpty(path) || Objects.equals(pathSeparator, fsSeparator)) {
+            return path;
+        }
+
+        // make sure path does not contain the target separator
+        if (path.contains(fsSeparator)) {
+            ValidateUtils.throwIllegalArgumentException("File system replacement may yield ambiguous result for %s with separator=%s", path, fsSeparator);
+        }
+
+        // check most likely case
+        if ((pathSeparator.length() == 1) && (fsSeparator.length() == 1)) {
+            return path.replace(pathSeparator.charAt(0), fsSeparator.charAt(0));
+        } else {
+            return path.replace(pathSeparator, fsSeparator);
+        }
+    }
+
+    /**
+     * Returns dependency information on these two files. If src has been
+     * modified later than target, it returns true. If target doesn't exist,
+     * it likewise returns true. Otherwise, target is newer than src and
+     * is not out of date, thus the method returns false. It also returns
+     * false if the src file doesn't even exist, since how could the
+     * target then be out of date.
+     *
+     * @param src         the original file
+     * @param target      the file being compared against
+     * @param granularity the amount in seconds of slack we will give in
+     *                    determining out of dateness
+     * @return whether the target is out of date
+     */
+    public static boolean isOutOfDate(File src, File target, int granularity) {
+        if (!src.exists()) {
+            return false;
+        }
+        if (!target.exists()) {
+            return true;
+        }
+        return (src.lastModified() - granularity) > target.lastModified();
+    }
+
+    /**
+     * "Flattens" a string by removing all whitespace (space, tab, linefeed,
+     * carriage return, and formfeed). This uses StringTokenizer and the
+     * default set of tokens as documented in the single arguement constructor.
+     *
+     * @param input a String to remove all whitespace.
+     * @return a String that has had all whitespace removed.
+     */
+    public static String removeWhitespace(String input) {
+        StringBuilder result = new StringBuilder();
+        if (input != null) {
+            StringTokenizer st = new StringTokenizer(input);
+            while (st.hasMoreTokens()) {
+                result.append(st.nextToken());
+            }
+        }
+        return result.toString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/SshdEventListener.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/SshdEventListener.java b/sshd-common/src/main/java/org/apache/sshd/common/util/SshdEventListener.java
new file mode 100644
index 0000000..ea6a3dd
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/SshdEventListener.java
@@ -0,0 +1,44 @@
+/*
+ * 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.lang.reflect.Proxy;
+import java.util.EventListener;
+import java.util.Objects;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface SshdEventListener extends EventListener {
+
+    /**
+     * Makes sure that the listener is neither {@code null} nor a proxy
+     *
+     * @param <L> Type of {@link SshdEventListener} being validation
+     * @param listener The listener instance
+     * @param prefix Prefix text to be prepended to validation failure messages
+     * @return The validated instance
+     */
+    static <L extends SshdEventListener> L validateListener(L listener, String prefix) {
+        Objects.requireNonNull(listener, prefix + ": no instance");
+        ValidateUtils.checkTrue(!Proxy.isProxyClass(listener.getClass()), prefix + ": proxies N/A");
+        return listener;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/ValidateUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/ValidateUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/util/ValidateUtils.java
new file mode 100644
index 0000000..a55e5d6
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/ValidateUtils.java
@@ -0,0 +1,216 @@
+/*
+ * 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.Collection;
+import java.util.Map;
+import java.util.function.Function;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class ValidateUtils {
+    private ValidateUtils() {
+        throw new UnsupportedOperationException("No instance");
+    }
+
+    public static <T> T checkNotNull(T t, String message) {
+        checkTrue(t != null, message);
+        return t;
+    }
+
+    public static <T> T checkNotNull(T t, String message, Object arg) {
+        checkTrue(t != null, message, arg);
+        return t;
+    }
+
+    public static <T> T checkNotNull(T t, String message, long value) {
+        checkTrue(t != null, message, value);
+        return t;
+    }
+
+    public static <T> T checkNotNull(T t, String message, Object... args) {
+        checkTrue(t != null, message, args);
+        return t;
+    }
+
+    public static String checkNotNullAndNotEmpty(String t, String message) {
+        t = checkNotNull(t, message).trim();
+        checkTrue(GenericUtils.length(t) > 0, message);
+        return t;
+    }
+
+    public static String checkNotNullAndNotEmpty(String t, String message, Object arg) {
+        t = checkNotNull(t, message, arg).trim();
+        checkTrue(GenericUtils.length(t) > 0, message, arg);
+        return t;
+    }
+
+    public static String checkNotNullAndNotEmpty(String t, String message, Object... args) {
+        t = checkNotNull(t, message, args).trim();
+        checkTrue(GenericUtils.length(t) > 0, message, args);
+        return t;
+    }
+
+    public static <K, V, M extends Map<K, V>> M checkNotNullAndNotEmpty(M t, String message, Object... args) {
+        t = checkNotNull(t, message, args);
+        checkTrue(GenericUtils.size(t) > 0, message, args);
+        return t;
+    }
+
+    public static <T, C extends Collection<T>> C checkNotNullAndNotEmpty(C t, String message, Object... args) {
+        t = checkNotNull(t, message, args);
+        checkTrue(GenericUtils.size(t) > 0, message, args);
+        return t;
+    }
+
+    public static <T, C extends Iterable<T>> C checkNotNullAndNotEmpty(C t, String message, Object... args) {
+        t = checkNotNull(t, message, args);
+        checkTrue(GenericUtils.isNotEmpty(t), message, args);
+        return t;
+    }
+
+    public static byte[] checkNotNullAndNotEmpty(byte[] a, String message) {
+        a = checkNotNull(a, 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(NumberUtils.length(a) > 0, message, args);
+        return a;
+    }
+
+    public static char[] checkNotNullAndNotEmpty(char[] a, String message) {
+        a = checkNotNull(a, message);
+        checkTrue(GenericUtils.length(a) > 0, message);
+        return a;
+    }
+
+    public static char[] checkNotNullAndNotEmpty(char[] a, String message, Object... args) {
+        a = checkNotNull(a, message, args);
+        checkTrue(GenericUtils.length(a) > 0, message, args);
+        return a;
+    }
+
+    public static int[] checkNotNullAndNotEmpty(int[] a, String message) {
+        a = checkNotNull(a, 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(NumberUtils.length(a) > 0, message, args);
+        return a;
+    }
+
+    public static <T> T[] checkNotNullAndNotEmpty(T[] t, String message, Object... args) {
+        t = checkNotNull(t, message, args);
+        checkTrue(GenericUtils.length(t) > 0, message, args);
+        return t;
+    }
+
+    public static <T> T checkInstanceOf(Object v, Class<T> expected, String message, long value) {
+        Class<?> actual = checkNotNull(v, message, value).getClass();
+        checkTrue(expected.isAssignableFrom(actual), message, value);
+        return expected.cast(v);
+    }
+
+    public static <T> T checkInstanceOf(Object v, Class<T> expected, String message) {
+        return checkInstanceOf(v, expected, message, GenericUtils.EMPTY_OBJECT_ARRAY);
+    }
+
+    public static <T> T checkInstanceOf(Object v, Class<T> expected, String message, Object arg) {
+        Class<?> actual = checkNotNull(v, message, arg).getClass();
+        checkTrue(expected.isAssignableFrom(actual), message, arg);
+        return expected.cast(v);
+    }
+
+    public static <T> T checkInstanceOf(Object v, Class<T> expected, String message, Object... args) {
+        Class<?> actual = checkNotNull(v, message, args).getClass();
+        checkTrue(expected.isAssignableFrom(actual), message, args);
+        return expected.cast(v);
+    }
+
+    public static void checkTrue(boolean flag, String message) {
+        if (!flag) {
+            throwIllegalArgumentException(message, GenericUtils.EMPTY_OBJECT_ARRAY);
+        }
+    }
+
+    public static void checkTrue(boolean flag, String message, long value) {
+        if (!flag) {
+            throwIllegalArgumentException(message, value);
+        }
+    }
+
+    public static void checkTrue(boolean flag, String message, Object arg) {
+        if (!flag) {
+            throwIllegalArgumentException(message, arg);
+        }
+    }
+
+    public static void checkTrue(boolean flag, String message, Object... args) {
+        if (!flag) {
+            throwIllegalArgumentException(message, args);
+        }
+    }
+
+    public static void throwIllegalArgumentException(String format, Object... args) {
+        throw createFormattedException(IllegalArgumentException::new, format, args);
+    }
+
+    public static void checkState(boolean flag, String message) {
+        if (!flag) {
+            throwIllegalStateException(message, GenericUtils.EMPTY_OBJECT_ARRAY);
+        }
+    }
+
+    public static void checkState(boolean flag, String message, long value) {
+        if (!flag) {
+            throwIllegalStateException(message, value);
+        }
+    }
+
+    public static void checkState(boolean flag, String message, Object arg) {
+        if (!flag) {
+            throwIllegalStateException(message, arg);
+        }
+    }
+
+    public static void checkState(boolean flag, String message, Object... args) {
+        if (!flag) {
+            throwIllegalStateException(message, args);
+        }
+    }
+
+    public static void throwIllegalStateException(String format, Object... args) {
+        throw createFormattedException(IllegalStateException::new, format, args);
+    }
+
+    public static <T extends Throwable> T createFormattedException(
+            Function<? super String, ? extends T> constructor, String format, Object... args) {
+        String message = String.format(format, args);
+        return constructor.apply(message);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/VersionInfo.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/VersionInfo.java b/sshd-common/src/main/java/org/apache/sshd/common/util/VersionInfo.java
new file mode 100644
index 0000000..ed1aab7
--- /dev/null
+++ b/sshd-common/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]);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/Buffer.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/Buffer.java b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/Buffer.java
new file mode 100644
index 0000000..dd61473
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/Buffer.java
@@ -0,0 +1,798 @@
+/*
+ * 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.buffer;
+
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.interfaces.DSAParams;
+import java.security.interfaces.DSAPrivateKey;
+import java.security.interfaces.DSAPublicKey;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.interfaces.RSAPrivateCrtKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.DSAPrivateKeySpec;
+import java.security.spec.DSAPublicKeySpec;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPoint;
+import java.security.spec.ECPrivateKeySpec;
+import java.security.spec.ECPublicKeySpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.RSAPrivateCrtKeySpec;
+import java.security.spec.RSAPublicKeySpec;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Objects;
+import java.util.function.IntUnaryOperator;
+import java.util.logging.Level;
+
+import org.apache.sshd.common.PropertyResolver;
+import org.apache.sshd.common.SshException;
+import org.apache.sshd.common.cipher.ECCurves;
+import org.apache.sshd.common.config.keys.KeyUtils;
+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.Readable;
+import org.apache.sshd.common.util.buffer.keys.BufferPublicKeyParser;
+import org.apache.sshd.common.util.logging.SimplifiedLog;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * Provides an abstract message buffer for encoding SSH messages
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class Buffer implements Readable {
+    protected final byte[] workBuf = new byte[Long.BYTES];
+
+    protected Buffer() {
+        super();
+    }
+
+    public abstract int rpos();
+
+    public abstract void rpos(int rpos);
+
+    public abstract int wpos();
+
+    public abstract void wpos(int wpos);
+
+    public abstract int capacity();
+
+    public abstract byte[] array();
+
+    public abstract void compact();
+
+    public byte[] getCompactData() {
+        int l = available();
+        if (l > 0) {
+            byte[] b = new byte[l];
+            System.arraycopy(array(), rpos(), b, 0, l);
+            return b;
+        } else {
+            return GenericUtils.EMPTY_BYTE_ARRAY;
+        }
+    }
+
+    public void clear() {
+        clear(true);
+    }
+
+    public abstract void clear(boolean wipeData);
+
+    public boolean isValidMessageStructure(Class<?>... fieldTypes) {
+        return isValidMessageStructure(GenericUtils.isEmpty(fieldTypes) ? Collections.emptyList() : Arrays.asList(fieldTypes));
+    }
+
+    public boolean isValidMessageStructure(Collection<Class<?>> fieldTypes) {
+        if (GenericUtils.isEmpty(fieldTypes)) {
+            return true;
+        }
+
+        int remainLen = available();
+        int readOffset = 0;
+        for (Class<?> ft : fieldTypes) {
+            if ((ft == boolean.class) || (ft == Boolean.class)
+                    || (ft == byte.class) || (ft == Byte.class)) {
+                if (remainLen < Byte.BYTES) {
+                    return false;
+                }
+
+                remainLen -= Byte.BYTES;
+                readOffset += Byte.BYTES;
+            } else if ((ft == short.class) || (ft == Short.class)) {
+                if (remainLen < Short.BYTES) {
+                    return false;
+                }
+
+                remainLen -= Short.BYTES;
+                readOffset += Short.BYTES;
+            } else if ((ft == int.class) || (ft == Integer.class)) {
+                if (remainLen < Integer.BYTES) {
+                    return false;
+                }
+
+                remainLen -= Integer.BYTES;
+                readOffset += Integer.BYTES;
+            } else if ((ft == long.class) || (ft == Long.class)) {
+                if (remainLen < Long.BYTES) {
+                    return false;
+                }
+
+                remainLen -= Long.BYTES;
+                readOffset += Long.BYTES;
+            } else if ((ft == byte[].class) || (ft == String.class)) {
+                if (remainLen < Integer.BYTES) {
+                    return false;
+                }
+
+                copyRawBytes(readOffset, workBuf, 0, Integer.BYTES);
+                remainLen -= Integer.BYTES;
+                readOffset += Integer.BYTES;
+
+                long length = BufferUtils.getUInt(workBuf, 0, Integer.BYTES);
+                if (length > remainLen) {
+                    return false;
+                }
+
+                remainLen -= (int) length;
+                readOffset += (int) length;
+            }
+        }
+
+        return true;
+    }
+
+    protected abstract void copyRawBytes(int offset, byte[] buf, int pos, int len);
+
+    public String toHex() {
+        return BufferUtils.toHex(array(), rpos(), available());
+    }
+
+    public void dumpHex(SimplifiedLog logger, String prefix, PropertyResolver resolver) {
+        dumpHex(logger, BufferUtils.DEFAULT_HEXDUMP_LEVEL, prefix, resolver);
+    }
+
+    public void dumpHex(SimplifiedLog logger, Level level, String prefix, PropertyResolver resolver) {
+        BufferUtils.dumpHex(logger, level, prefix, resolver, BufferUtils.DEFAULT_HEX_SEPARATOR, array(), rpos(), available());
+    }
+
+    /*======================
+       Read methods
+     ======================*/
+
+    public int getUByte() {
+        return getByte() & 0xFF;
+    }
+
+    public byte getByte() {
+        ensureAvailable(Byte.BYTES);
+        getRawBytes(workBuf, 0, Byte.BYTES);
+        return workBuf[0];
+    }
+
+    public short getShort() {
+        ensureAvailable(Short.BYTES);
+        getRawBytes(workBuf, 0, Short.BYTES);
+        short v = (short) ((workBuf[1] << Byte.SIZE) & 0xFF00);
+        v |= (short) (workBuf[0] & 0xF);
+        return v;
+    }
+
+    public int getInt() {
+        return (int) getUInt();
+    }
+
+    public long getUInt() {
+        ensureAvailable(Integer.BYTES);
+        getRawBytes(workBuf, 0, Integer.BYTES);
+        return BufferUtils.getUInt(workBuf, 0, Integer.BYTES);
+    }
+
+    public long getLong() {
+        ensureAvailable(Long.BYTES);
+        getRawBytes(workBuf, 0, Long.BYTES);
+        long l = ((long) workBuf[0] << 56) & 0xff00000000000000L;
+        l |= ((long) workBuf[1] << 48) & 0x00ff000000000000L;
+        l |= ((long) workBuf[2] << 40) & 0x0000ff0000000000L;
+        l |= ((long) workBuf[3] << 32) & 0x000000ff00000000L;
+        l |= ((long) workBuf[4] << 24) & 0x00000000ff000000L;
+        l |= ((long) workBuf[5] << 16) & 0x0000000000ff0000L;
+        l |= ((long) workBuf[6] << 8) & 0x000000000000ff00L;
+        l |= (workBuf[7]) & 0x00000000000000ffL;
+        return l;
+    }
+
+    @SuppressWarnings("PMD.BooleanGetMethodName")
+    public boolean getBoolean() {
+        return getByte() != 0;
+    }
+
+    public String getString() {
+        return getString(StandardCharsets.UTF_8);
+    }
+
+    /**
+     * @param usePrependedLength If {@code true} then there is a 32-bit
+     *                           value indicating the number of strings to read. Otherwise, the
+     *                           method will use a &quot;greedy&quot; reading of strings while more
+     *                           data available
+     * @return A {@link Collection} of the read strings
+     * @see #getStringList(boolean, Charset)
+     */
+    public Collection<String> getStringList(boolean usePrependedLength) {
+        return getStringList(usePrependedLength, StandardCharsets.UTF_8);
+    }
+
+    /**
+     * @param usePrependedLength If {@code true} then there is a 32-bit
+     *                           value indicating the number of strings to read. Otherwise, the
+     *                           method will use a &quot;greedy&quot; reading of strings while more
+     *                           data available
+     * @param charset            The {@link Charset} to use for the string
+     * @return A {@link Collection} of the read strings
+     * @see #getStringList(int, Charset)
+     * @see #getAvailableStrings()
+     */
+    public Collection<String> getStringList(boolean usePrependedLength, Charset charset) {
+        if (usePrependedLength) {
+            int count = getInt();
+            return getStringList(count, charset);
+        } else {
+            return getAvailableStrings(charset);
+        }
+    }
+
+    /**
+     * @return The remaining data as a list of strings
+     * @see #getAvailableStrings(Charset)
+     */
+    public Collection<String> getAvailableStrings() {
+        return getAvailableStrings(StandardCharsets.UTF_8);
+    }
+
+    /**
+     * @param charset The {@link Charset} to use for the strings
+     * @return The remaining data as a list of strings
+     * @see #available()
+     * @see #getString(Charset)
+     */
+    public Collection<String> getAvailableStrings(Charset charset) {
+        Collection<String> list = new LinkedList<>();
+        while (available() > 0) {
+            String s = getString(charset);
+            list.add(s);
+        }
+
+        return list;
+    }
+
+    /**
+     * @param count The <U>exact</U> number of strings to read - can be zero
+     * @return A {@link List} with the specified number of strings
+     * @see #getStringList(int, Charset)
+     */
+    public List<String> getStringList(int count) {
+        return getStringList(count, StandardCharsets.UTF_8);
+    }
+
+    /**
+     * @param count   The <U>exact</U> number of strings to read - can be zero
+     * @param charset The {@link Charset} of the strings
+     * @return A {@link List} with the specified number of strings
+     * @see #getString(Charset)
+     */
+    public List<String> getStringList(int count, Charset charset) {
+        if (count == 0) {
+            return Collections.emptyList();
+        }
+
+        List<String> list = new ArrayList<>(count);
+        for (int index = 0; index < count; index++) {
+            String s = getString(charset);
+            list.add(s);
+        }
+
+        return list;
+    }
+
+    public abstract String getString(Charset charset);
+
+    public BigInteger getMPInt() {
+        return new BigInteger(getMPIntAsBytes());
+    }
+
+    public byte[] getMPIntAsBytes() {
+        return getBytes();
+    }
+
+    public byte[] getBytes() {
+        int len = getInt();
+        if (len < 0) {
+            throw new BufferException("Bad item length: " + len);
+        }
+        ensureAvailable(len);
+        byte[] b = new byte[len];
+        getRawBytes(b);
+        return b;
+    }
+
+    public void getRawBytes(byte[] buf) {
+        getRawBytes(buf, 0, buf.length);
+    }
+
+    public PublicKey getPublicKey() throws SshException {
+        return getPublicKey(BufferPublicKeyParser.DEFAULT);
+    }
+
+    /**
+     * @param parser A {@link BufferPublicKeyParser} to extract the key from the buffer
+     * - never {@code null}
+     * @return The extracted {@link PublicKey} - may be {@code null} if the parser so decided
+     * @throws SshException If failed to extract the key
+     * @see #getRawPublicKey(BufferPublicKeyParser)
+     */
+    public PublicKey getPublicKey(BufferPublicKeyParser<? extends PublicKey> parser) throws SshException {
+        int ow = wpos();
+        int len = getInt();
+        wpos(rpos() + len);
+        try {
+            return getRawPublicKey(parser);
+        } finally {
+            wpos(ow);
+        }
+    }
+
+    public PublicKey getRawPublicKey() throws SshException {
+        return getRawPublicKey(BufferPublicKeyParser.DEFAULT);
+    }
+
+    /**
+     * @param parser A {@link BufferPublicKeyParser} to extract the key from the buffer
+     * - never {@code null}
+     * @return The extracted {@link PublicKey} - may be {@code null} if the parser so decided
+     * @throws SshException If failed to extract the key
+     */
+    public PublicKey getRawPublicKey(BufferPublicKeyParser<? extends PublicKey> parser) throws SshException {
+        Objects.requireNonNull(parser, "No key data parser");
+        try {
+            String keyType = getString();
+            if (!parser.isKeyTypeSupported(keyType)) {
+                throw new NoSuchAlgorithmException("Key type=" + keyType + ") not supported by parser=" + parser);
+            }
+
+            return parser.getRawPublicKey(keyType, this);
+        } catch (GeneralSecurityException e) {
+            throw new SshException(e);
+        }
+    }
+
+    public KeyPair getKeyPair() throws SshException {
+        try {
+            final PublicKey pub;
+            final PrivateKey prv;
+            final String keyAlg = getString();
+            if (KeyPairProvider.SSH_RSA.equals(keyAlg)) {
+                BigInteger e = getMPInt();
+                BigInteger n = getMPInt();
+                BigInteger d = getMPInt();
+                BigInteger qInv = getMPInt();
+                BigInteger q = getMPInt();
+                BigInteger p = getMPInt();
+                BigInteger dP = d.remainder(p.subtract(BigInteger.valueOf(1)));
+                BigInteger dQ = d.remainder(q.subtract(BigInteger.valueOf(1)));
+                KeyFactory keyFactory = SecurityUtils.getKeyFactory(KeyUtils.RSA_ALGORITHM);
+                pub = keyFactory.generatePublic(new RSAPublicKeySpec(n, e));
+                prv = keyFactory.generatePrivate(new RSAPrivateCrtKeySpec(n, e, d, p, q, dP, dQ, qInv));
+            } else if (KeyPairProvider.SSH_DSS.equals(keyAlg)) {
+                BigInteger p = getMPInt();
+                BigInteger q = getMPInt();
+                BigInteger g = getMPInt();
+                BigInteger y = getMPInt();
+                BigInteger x = getMPInt();
+                KeyFactory keyFactory = SecurityUtils.getKeyFactory(KeyUtils.DSS_ALGORITHM);
+                pub = keyFactory.generatePublic(new DSAPublicKeySpec(y, p, q, g));
+                prv = keyFactory.generatePrivate(new DSAPrivateKeySpec(x, p, q, g));
+            } else if (KeyPairProvider.SSH_ED25519.equals(keyAlg)) {
+                return SecurityUtils.extractEDDSAKeyPair(this, keyAlg);
+            } else {
+                ECCurves curve = ECCurves.fromKeyType(keyAlg);
+                if (curve == null) {
+                    throw new NoSuchAlgorithmException("Unsupported key pair algorithm: " + keyAlg);
+                }
+                String curveName = curve.getName();
+                ECParameterSpec params = curve.getParameters();
+                return extractEC(curveName, params);
+            }
+
+            return new KeyPair(pub, prv);
+        } catch (GeneralSecurityException e) {
+            throw new SshException(e);
+        }
+    }
+
+    protected KeyPair extractEC(String expectedCurveName, ECParameterSpec spec) throws GeneralSecurityException {
+        String curveName = getString();
+        if (!expectedCurveName.equals(curveName)) {
+            throw new InvalidKeySpecException("extractEC(" + expectedCurveName + ") mismatched curve name: " + curveName);
+        }
+
+        byte[] groupBytes = getBytes();
+        BigInteger exponent = getMPInt();
+
+        if (spec == null) {
+            throw new InvalidKeySpecException("extractEC(" + expectedCurveName + ") missing parameters for curve");
+        }
+
+        ECPoint group;
+        try {
+            group = ECCurves.octetStringToEcPoint(groupBytes);
+        } catch (RuntimeException e) {
+            throw new InvalidKeySpecException("extractEC(" + expectedCurveName + ")"
+                    + " failed (" + e.getClass().getSimpleName() + ")"
+                    + " to decode EC group for curve: " + e.getMessage(),
+                    e);
+        }
+
+        KeyFactory keyFactory = SecurityUtils.getKeyFactory(KeyUtils.EC_ALGORITHM);
+        PublicKey pubKey = keyFactory.generatePublic(new ECPublicKeySpec(group, spec));
+        PrivateKey privKey = keyFactory.generatePrivate(new ECPrivateKeySpec(exponent, spec));
+        return new KeyPair(pubKey, privKey);
+    }
+
+    public void ensureAvailable(int reqLen) throws BufferException {
+        int availLen = available();
+        if (availLen < reqLen) {
+            throw new BufferException("Underflow: requested=" + reqLen + ", available=" + availLen);
+        }
+    }
+
+    /*======================
+       Write methods
+     ======================*/
+
+    public void putByte(byte b) {
+        ensureCapacity(Byte.BYTES);
+        workBuf[0] = b;
+        putRawBytes(workBuf, 0, Byte.BYTES);
+    }
+
+    public void putBuffer(Readable buffer) {
+        putBuffer(buffer, true);
+    }
+
+    public abstract int putBuffer(Readable buffer, boolean expand);
+
+    public abstract void putBuffer(ByteBuffer buffer);
+
+    /**
+     * Writes 16 bits
+     *
+     * @param i The 16-bit value
+     */
+    public void putShort(int i) {
+        ensureCapacity(Short.BYTES);
+        workBuf[0] = (byte) (i >> 8);
+        workBuf[1] = (byte) i;
+        putRawBytes(workBuf, 0, Short.BYTES);
+    }
+
+    /**
+     * Writes 32 bits
+     *
+     * @param i The 32-bit value
+     */
+    public void putInt(long i) {
+        BufferUtils.validateInt32Value(i, "Invalid 32-bit value: %d");
+        ensureCapacity(Integer.BYTES);
+        BufferUtils.putUInt(i, workBuf, 0, Integer.BYTES);
+        putRawBytes(workBuf, 0, Integer.BYTES);
+    }
+
+    /**
+     * Writes 64 bits
+     *
+     * @param i The 64-bit value
+     */
+    public void putLong(long i) {
+        ensureCapacity(Long.BYTES);
+        workBuf[0] = (byte) (i >> 56);
+        workBuf[1] = (byte) (i >> 48);
+        workBuf[2] = (byte) (i >> 40);
+        workBuf[3] = (byte) (i >> 32);
+        workBuf[4] = (byte) (i >> 24);
+        workBuf[5] = (byte) (i >> 16);
+        workBuf[6] = (byte) (i >> 8);
+        workBuf[7] = (byte) i;
+        putRawBytes(workBuf, 0, Long.BYTES);
+    }
+
+    public void putBoolean(boolean b) {
+        putByte(b ? (byte) 1 : (byte) 0);
+    }
+
+    /**
+     * Adds the bytes to the buffer and wipes the data from the
+     * input buffer <U>after</U> having added it - useful for sensitive
+     * information such as password
+     *
+     * @param b The buffer to add - OK if {@code null}
+     */
+    public void putAndWipeBytes(byte[] b) {
+        putAndWipeBytes(b, 0, NumberUtils.length(b));
+    }
+
+    public void putAndWipeBytes(byte[] b, int off, int len) {
+        putBytes(b, off, len);
+
+        for (int pos = off, index = 0; index < len; pos++, index++) {
+            b[pos] = (byte) 0;
+        }
+    }
+
+    public void putBytes(byte[] b) {
+        putBytes(b, 0, NumberUtils.length(b));
+    }
+
+    public void putBytes(byte[] b, int off, int len) {
+        putInt(len);
+        putRawBytes(b, off, len);
+    }
+
+    /**
+     * Encodes the {@link Objects#toString(Object, String) toString} value of each member.
+     *
+     * @param objects       The objects to be encoded in the buffer - OK if
+     *                      {@code null}/empty
+     * @param prependLength If {@code true} then the list is preceded by
+     *                      a 32-bit count of the number of members in the list
+     * @see #putStringList(Collection, Charset, boolean)
+     */
+    public void putStringList(Collection<?> objects, boolean prependLength) {
+        putStringList(objects, StandardCharsets.UTF_8, prependLength);
+    }
+
+    /**
+     * Encodes the {@link Objects#toString(Object, String) toString} value of each member
+     *
+     * @param objects       The objects to be encoded in the buffer - OK if
+     *                      {@code null}/empty
+     * @param charset       The {@link Charset} to use for encoding
+     * @param prependLength If {@code true} then the list is preceded by
+     *                      a 32-bit count of the number of members in the list
+     * @see #putString(String, Charset)
+     */
+    public void putStringList(Collection<?> objects, Charset charset, boolean prependLength) {
+        int numObjects = GenericUtils.size(objects);
+        if (prependLength) {
+            putInt(numObjects);
+        }
+
+        if (numObjects <= 0) {
+            return;
+        }
+
+        objects.forEach(o -> putString(Objects.toString(o, null), charset));
+    }
+
+    public void putString(String string) {
+        putString(string, StandardCharsets.UTF_8);
+    }
+
+    public void putString(String string, Charset charset) {
+        if (GenericUtils.isEmpty(string)) {
+            putBytes(GenericUtils.EMPTY_BYTE_ARRAY);
+        } else {
+            putBytes(string.getBytes(charset));
+        }
+    }
+
+    /**
+     * Zeroes the input array <U>after</U> having put the characters in the
+     * buffer - useful for sensitive information such as passwords
+     *
+     * @param chars The characters to put in the buffer - may be {@code null}/empty
+     * @see #putAndWipeChars(char[], Charset)
+     * @see #putChars(char[], Charset)
+     */
+    public void putAndWipeChars(char[] chars) {
+        putAndWipeChars(chars, 0, GenericUtils.length(chars));
+    }
+
+    public void putAndWipeChars(char[] chars, int offset, int len) {
+        putAndWipeChars(chars, offset, len, StandardCharsets.UTF_8);
+    }
+
+    public void putAndWipeChars(char[] chars, Charset charset) {
+        putAndWipeChars(chars, 0, GenericUtils.length(chars), charset);
+    }
+
+    public void putAndWipeChars(char[] chars, int offset, int len, Charset charset) {
+        putChars(chars, offset, len, charset);
+        for (int pos = offset, index = 0; index < len; index++, pos++) {
+            chars[pos] = '\0';
+        }
+    }
+
+    public void putChars(char[] chars) {
+        putChars(chars, 0, GenericUtils.length(chars));
+    }
+
+    public void putChars(char[] chars, int offset, int len) {
+        putChars(chars, offset, len, StandardCharsets.UTF_8);
+    }
+
+    public void putChars(char[] chars, Charset charset) {
+        putChars(chars, 0, GenericUtils.length(chars), charset);
+    }
+
+    public void putChars(char[] chars, int offset, int len, Charset charset) {
+        if (len <= 0) {
+            putBytes(GenericUtils.EMPTY_BYTE_ARRAY);
+        } else {
+            putBuffer(charset.encode(CharBuffer.wrap(chars, offset, len)));
+        }
+    }
+
+    public void putMPInt(BigInteger bi) {
+        putMPInt(bi.toByteArray());
+    }
+
+    public void putMPInt(byte[] foo) {
+        if ((foo[0] & 0x80) != 0) {
+            putInt(foo.length + 1 /* padding */);
+            putByte((byte) 0);
+        } else {
+            putInt(foo.length);
+        }
+        putRawBytes(foo);
+    }
+
+    public void putRawBytes(byte[] d) {
+        putRawBytes(d, 0, d.length);
+    }
+
+    public abstract void putRawBytes(byte[] d, int off, int len);
+
+    public void putPublicKey(PublicKey key) {
+        int ow = wpos();
+        putInt(0);
+        int ow1 = wpos();
+        putRawPublicKey(key);
+        int ow2 = wpos();
+        wpos(ow);
+        putInt(ow2 - ow1);
+        wpos(ow2);
+    }
+
+    public void putRawPublicKey(PublicKey key) {
+        Objects.requireNonNull(key, "No key");
+        if (key instanceof RSAPublicKey) {
+            RSAPublicKey rsaPub = (RSAPublicKey) key;
+
+            putString(KeyPairProvider.SSH_RSA);
+            putMPInt(rsaPub.getPublicExponent());
+            putMPInt(rsaPub.getModulus());
+        } else if (key instanceof DSAPublicKey) {
+            DSAPublicKey dsaPub = (DSAPublicKey) key;
+            DSAParams dsaParams = dsaPub.getParams();
+
+            putString(KeyPairProvider.SSH_DSS);
+            putMPInt(dsaParams.getP());
+            putMPInt(dsaParams.getQ());
+            putMPInt(dsaParams.getG());
+            putMPInt(dsaPub.getY());
+        } else if (key instanceof ECPublicKey) {
+            ECPublicKey ecKey = (ECPublicKey) key;
+            ECParameterSpec ecParams = ecKey.getParams();
+            ECCurves curve = ECCurves.fromCurveParameters(ecParams);
+            if (curve == null) {
+                throw new BufferException("Unsupported EC curve parameters");
+            }
+
+            putString(curve.getKeyType());
+            putString(curve.getName());
+            putBytes(ECCurves.encodeECPoint(ecKey.getW(), ecParams));
+        } else if (SecurityUtils.EDDSA.equals(key.getAlgorithm())) {
+            SecurityUtils.putRawEDDSAPublicKey(this, key);
+        } else {
+            throw new BufferException("Unsupported raw public key algorithm: " + key.getAlgorithm());
+        }
+    }
+
+    public void putKeyPair(KeyPair kp) {
+        PublicKey pubKey = kp.getPublic();
+        PrivateKey prvKey = kp.getPrivate();
+        if (prvKey instanceof RSAPrivateCrtKey) {
+            RSAPublicKey rsaPub = (RSAPublicKey) pubKey;
+            RSAPrivateCrtKey rsaPrv = (RSAPrivateCrtKey) prvKey;
+
+            putString(KeyPairProvider.SSH_RSA);
+            putMPInt(rsaPub.getPublicExponent());
+            putMPInt(rsaPub.getModulus());
+            putMPInt(rsaPrv.getPrivateExponent());
+            putMPInt(rsaPrv.getCrtCoefficient());
+            putMPInt(rsaPrv.getPrimeQ());
+            putMPInt(rsaPrv.getPrimeP());
+        } else if (pubKey instanceof DSAPublicKey) {
+            DSAPublicKey dsaPub = (DSAPublicKey) pubKey;
+            DSAParams dsaParams = dsaPub.getParams();
+            DSAPrivateKey dsaPrv = (DSAPrivateKey) prvKey;
+
+            putString(KeyPairProvider.SSH_DSS);
+            putMPInt(dsaParams.getP());
+            putMPInt(dsaParams.getQ());
+            putMPInt(dsaParams.getG());
+            putMPInt(dsaPub.getY());
+            putMPInt(dsaPrv.getX());
+        } else if (pubKey instanceof ECPublicKey) {
+            ECPublicKey ecPub = (ECPublicKey) pubKey;
+            ECPrivateKey ecPriv = (ECPrivateKey) prvKey;
+            ECParameterSpec ecParams = ecPub.getParams();
+            ECCurves curve = ECCurves.fromCurveParameters(ecParams);
+            if (curve == null) {
+                throw new BufferException("Unsupported EC curve parameters");
+            }
+
+            putString(curve.getKeyType());
+            putString(curve.getName());
+            putBytes(ECCurves.encodeECPoint(ecPub.getW(), ecParams));
+            putMPInt(ecPriv.getS());
+        } else if (SecurityUtils.EDDSA.equals(pubKey.getAlgorithm())) {
+            SecurityUtils.putEDDSAKeyPair(this, pubKey, prvKey);
+        } else {
+            throw new BufferException("Unsupported key pair algorithm: " + pubKey.getAlgorithm());
+        }
+    }
+
+    protected void ensureCapacity(int capacity) {
+        ensureCapacity(capacity, BufferUtils.DEFAULT_BUFFER_GROWTH_FACTOR);
+    }
+
+    /**
+     * @param capacity     The requires capacity
+     * @param growthFactor An {@link IntUnaryOperator} that is invoked
+     *                     if the current capacity is insufficient. The argument is the minimum
+     *                     required new data length, the function result should be the
+     *                     effective new data length to be allocated - if less than minimum
+     *                     then an exception is thrown
+     */
+    public abstract void ensureCapacity(int capacity, IntUnaryOperator growthFactor);
+
+    protected abstract int size();
+
+    @Override
+    public String toString() {
+        return "Buffer [rpos=" + rpos() + ", wpos=" + wpos() + ", size=" + size() + "]";
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/BufferException.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/BufferException.java b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/BufferException.java
new file mode 100644
index 0000000..93f4e6b
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/BufferException.java
@@ -0,0 +1,30 @@
+/*
+ * 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.buffer;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class BufferException extends RuntimeException {
+    private static final long serialVersionUID = 658645233475011039L;
+
+    public BufferException(String message) {
+        super(message);
+    }
+}
\ No newline at end of file


[20/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common utilities code from sshd-core into sshd-common (new artifact)

Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHECDSAPrivateKeyEntryDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHECDSAPrivateKeyEntryDecoder.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHECDSAPrivateKeyEntryDecoder.java
deleted file mode 100644
index bceca59..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHECDSAPrivateKeyEntryDecoder.java
+++ /dev/null
@@ -1,164 +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.config.keys.loader.openssh;
-
-import java.io.IOException;
-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.KeyPair;
-import java.security.KeyPairGenerator;
-import java.security.NoSuchProviderException;
-import java.security.interfaces.ECPrivateKey;
-import java.security.interfaces.ECPublicKey;
-import java.security.spec.ECParameterSpec;
-import java.security.spec.ECPrivateKeySpec;
-import java.security.spec.ECPublicKeySpec;
-import java.security.spec.InvalidKeySpecException;
-import java.util.Objects;
-
-import org.apache.sshd.common.cipher.ECCurves;
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.config.keys.KeyEntryResolver;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.config.keys.impl.AbstractPrivateKeyEntryDecoder;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class OpenSSHECDSAPrivateKeyEntryDecoder extends AbstractPrivateKeyEntryDecoder<ECPublicKey, ECPrivateKey> {
-    public static final OpenSSHECDSAPrivateKeyEntryDecoder INSTANCE = new OpenSSHECDSAPrivateKeyEntryDecoder();
-
-    public OpenSSHECDSAPrivateKeyEntryDecoder() {
-        super(ECPublicKey.class, ECPrivateKey.class, ECCurves.KEY_TYPES);
-    }
-
-    @Override
-    public ECPrivateKey decodePrivateKey(String keyType, FilePasswordProvider passwordProvider, InputStream keyData)
-            throws IOException, GeneralSecurityException {
-        ECCurves curve = ECCurves.fromKeyType(keyType);
-        if (curve == null) {
-            throw new InvalidKeySpecException("Not an EC curve name: " + keyType);
-        }
-
-        if (!SecurityUtils.isECCSupported()) {
-            throw new NoSuchProviderException("ECC not supported");
-        }
-
-        String keyCurveName = curve.getName();
-        // see rfc5656 section 3.1
-        String encCurveName = KeyEntryResolver.decodeString(keyData);
-        if (!keyCurveName.equals(encCurveName)) {
-            throw new InvalidKeySpecException("Mismatched key curve name (" + keyCurveName + ") vs. encoded one (" + encCurveName + ")");
-        }
-
-        byte[] pubKey = KeyEntryResolver.readRLEBytes(keyData);
-        Objects.requireNonNull(pubKey, "No public point");  // TODO validate it is a valid ECPoint
-        BigInteger s = KeyEntryResolver.decodeBigInt(keyData);
-        ECParameterSpec params = curve.getParameters();
-        return generatePrivateKey(new ECPrivateKeySpec(s, params));
-    }
-
-    @Override
-    public String encodePrivateKey(OutputStream s, ECPrivateKey key) throws IOException {
-        Objects.requireNonNull(key, "No private key provided");
-        return null;
-    }
-
-    @Override
-    public ECPublicKey recoverPublicKey(ECPrivateKey prvKey) throws GeneralSecurityException {
-        ECCurves curve = ECCurves.fromECKey(prvKey);
-        if (curve == null) {
-            throw new InvalidKeyException("Unknown curve");
-        }
-        // TODO see how we can figure out the public value
-        return super.recoverPublicKey(prvKey);
-    }
-
-    @Override
-    public ECPublicKey clonePublicKey(ECPublicKey key) throws GeneralSecurityException {
-        if (!SecurityUtils.isECCSupported()) {
-            throw new NoSuchProviderException("ECC not supported");
-        }
-
-        if (key == null) {
-            return null;
-        }
-
-        ECParameterSpec params = key.getParams();
-        if (params == null) {
-            throw new InvalidKeyException("Missing parameters in key");
-        }
-
-        return generatePublicKey(new ECPublicKeySpec(key.getW(), params));
-    }
-
-    @Override
-    public ECPrivateKey clonePrivateKey(ECPrivateKey key) throws GeneralSecurityException {
-        if (!SecurityUtils.isECCSupported()) {
-            throw new NoSuchProviderException("ECC not supported");
-        }
-
-        if (key == null) {
-            return null;
-        }
-
-        ECParameterSpec params = key.getParams();
-        if (params == null) {
-            throw new InvalidKeyException("Missing parameters in key");
-        }
-
-        return generatePrivateKey(new ECPrivateKeySpec(key.getS(), params));
-    }
-
-    @Override
-    public KeyFactory getKeyFactoryInstance() throws GeneralSecurityException {
-        if (SecurityUtils.isECCSupported()) {
-            return SecurityUtils.getKeyFactory(KeyUtils.EC_ALGORITHM);
-        } else {
-            throw new NoSuchProviderException("ECC not supported");
-        }
-    }
-
-    @Override
-    public KeyPair generateKeyPair(int keySize) throws GeneralSecurityException {
-        ECCurves curve = ECCurves.fromCurveSize(keySize);
-        if (curve == null) {
-            throw new InvalidKeySpecException("Unknown curve for key size=" + keySize);
-        }
-
-        KeyPairGenerator gen = getKeyPairGenerator();
-        gen.initialize(curve.getParameters());
-        return gen.generateKeyPair();
-    }
-
-    @Override
-    public KeyPairGenerator getKeyPairGenerator() throws GeneralSecurityException {
-        if (SecurityUtils.isECCSupported()) {
-            return SecurityUtils.getKeyPairGenerator(KeyUtils.EC_ALGORITHM);
-        } else {
-            throw new NoSuchProviderException("ECC not supported");
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParser.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParser.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParser.java
deleted file mode 100644
index 95db256..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParser.java
+++ /dev/null
@@ -1,358 +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.config.keys.loader.openssh;
-
-import java.io.ByteArrayInputStream;
-import java.io.EOFException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.StreamCorruptedException;
-import java.nio.charset.StandardCharsets;
-import java.security.GeneralSecurityException;
-import java.security.InvalidKeyException;
-import java.security.Key;
-import java.security.KeyPair;
-import java.security.NoSuchAlgorithmException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.util.AbstractMap.SimpleImmutableEntry;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.TreeMap;
-
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.config.keys.KeyEntryResolver;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.config.keys.PrivateKeyEntryDecoder;
-import org.apache.sshd.common.config.keys.PublicKeyEntryDecoder;
-import org.apache.sshd.common.config.keys.loader.AbstractKeyPairResourceParser;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.buffer.BufferUtils;
-import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * Basic support for <A HREF="http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.key?rev=1.1&content-type=text/x-cvsweb-markup">OpenSSH key file(s)</A>
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class OpenSSHKeyPairResourceParser extends AbstractKeyPairResourceParser {
-    public static final String BEGIN_MARKER = "BEGIN OPENSSH PRIVATE KEY";
-    public static final List<String> BEGINNERS =
-            Collections.unmodifiableList(Collections.singletonList(BEGIN_MARKER));
-
-    public static final String END_MARKER = "END OPENSSH PRIVATE KEY";
-    public static final List<String> ENDERS =
-            Collections.unmodifiableList(Collections.singletonList(END_MARKER));
-
-    public static final String AUTH_MAGIC = "openssh-key-v1";
-    public static final OpenSSHKeyPairResourceParser INSTANCE = new OpenSSHKeyPairResourceParser();
-
-    private static final byte[] AUTH_MAGIC_BYTES = AUTH_MAGIC.getBytes(StandardCharsets.UTF_8);
-    private static final Map<String, PrivateKeyEntryDecoder<?, ?>> BY_KEY_TYPE_DECODERS_MAP =
-            new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
-
-    private static final Map<Class<?>, PrivateKeyEntryDecoder<?, ?>> BY_KEY_CLASS_DECODERS_MAP =
-            new HashMap<>();
-
-    static {
-        registerPrivateKeyEntryDecoder(OpenSSHRSAPrivateKeyDecoder.INSTANCE);
-        registerPrivateKeyEntryDecoder(OpenSSHDSSPrivateKeyEntryDecoder.INSTANCE);
-
-        if (SecurityUtils.isECCSupported()) {
-            registerPrivateKeyEntryDecoder(OpenSSHECDSAPrivateKeyEntryDecoder.INSTANCE);
-        }
-        if (SecurityUtils.isEDDSACurveSupported()) {
-            registerPrivateKeyEntryDecoder(SecurityUtils.getOpenSSHEDDSAPrivateKeyEntryDecoder());
-        }
-    }
-
-    public OpenSSHKeyPairResourceParser() {
-        super(BEGINNERS, ENDERS);
-    }
-
-    @Override
-    public Collection<KeyPair> extractKeyPairs(
-            String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, InputStream stream)
-                    throws IOException, GeneralSecurityException {
-        stream = validateStreamMagicMarker(resourceKey, stream);
-
-        String cipher = KeyEntryResolver.decodeString(stream);
-        if (!OpenSSHParserContext.IS_NONE_CIPHER.test(cipher)) {
-            throw new NoSuchAlgorithmException("Unsupported cipher: " + cipher);
-        }
-
-        boolean debugEnabled = log.isDebugEnabled();
-        if (debugEnabled) {
-            log.debug("extractKeyPairs({}) cipher={}", resourceKey, cipher);
-        }
-
-        String kdfName = KeyEntryResolver.decodeString(stream);
-        if (!OpenSSHParserContext.IS_NONE_KDF.test(kdfName)) {
-            throw new NoSuchAlgorithmException("Unsupported KDF: " + kdfName);
-        }
-
-        byte[] kdfOptions = KeyEntryResolver.readRLEBytes(stream);
-        if (debugEnabled) {
-            log.debug("extractKeyPairs({}) KDF={}, options={}",
-                      resourceKey, kdfName, BufferUtils.toHex(':', kdfOptions));
-        }
-
-        int numKeys = KeyEntryResolver.decodeInt(stream);
-        if (numKeys <= 0) {
-            if (debugEnabled) {
-                log.debug("extractKeyPairs({}) no encoded keys", resourceKey);
-            }
-            return Collections.emptyList();
-        }
-
-        List<PublicKey> publicKeys = new ArrayList<>(numKeys);
-        OpenSSHParserContext context = new OpenSSHParserContext(cipher, kdfName, kdfOptions);
-        boolean traceEnabled = log.isTraceEnabled();
-        for (int index = 1; index <= numKeys; index++) {
-            PublicKey pubKey = readPublicKey(resourceKey, context, stream);
-            ValidateUtils.checkNotNull(pubKey, "Empty public key #%d in %s", index, resourceKey);
-            if (traceEnabled) {
-                log.trace("extractKeyPairs({}) read public key #{}: {} {}",
-                          resourceKey, index, KeyUtils.getKeyType(pubKey), KeyUtils.getFingerPrint(pubKey));
-            }
-            publicKeys.add(pubKey);
-        }
-
-        byte[] privateData = KeyEntryResolver.readRLEBytes(stream);
-        try (InputStream bais = new ByteArrayInputStream(privateData)) {
-            return readPrivateKeys(resourceKey, context, publicKeys, passwordProvider, bais);
-        }
-    }
-
-    protected PublicKey readPublicKey(
-            String resourceKey, OpenSSHParserContext context, InputStream stream)
-                    throws IOException, GeneralSecurityException {
-        byte[] keyData = KeyEntryResolver.readRLEBytes(stream);
-        try (InputStream bais = new ByteArrayInputStream(keyData)) {
-            String keyType = KeyEntryResolver.decodeString(bais);
-            PublicKeyEntryDecoder<?, ?> decoder = KeyUtils.getPublicKeyEntryDecoder(keyType);
-            if (decoder == null) {
-                throw new NoSuchAlgorithmException("Unsupported key type (" + keyType + ") in " + resourceKey);
-            }
-
-            return decoder.decodePublicKey(keyType, bais);
-        }
-    }
-
-    protected List<KeyPair> readPrivateKeys(
-            String resourceKey, OpenSSHParserContext context, Collection<? extends PublicKey> publicKeys,
-            FilePasswordProvider passwordProvider, InputStream stream)
-                    throws IOException, GeneralSecurityException {
-        if (GenericUtils.isEmpty(publicKeys)) {
-            return Collections.emptyList();
-        }
-
-        boolean traceEnabled = log.isTraceEnabled();
-        int check1 = KeyEntryResolver.decodeInt(stream);
-        int check2 = KeyEntryResolver.decodeInt(stream);
-        if (traceEnabled) {
-            log.trace("readPrivateKeys({}) check1=0x{}, check2=0x{}",
-                      resourceKey, Integer.toHexString(check1), Integer.toHexString(check2));
-        }
-
-        List<KeyPair> keyPairs = new ArrayList<>(publicKeys.size());
-        for (PublicKey pubKey : publicKeys) {
-            String pubType = KeyUtils.getKeyType(pubKey);
-            int keyIndex = keyPairs.size() + 1;
-            if (traceEnabled) {
-                log.trace("extractKeyPairs({}) read private key #{}: {}",
-                        resourceKey, keyIndex, pubType);
-            }
-
-            Map.Entry<PrivateKey, String> prvData = readPrivateKey(resourceKey, context, pubType, passwordProvider, stream);
-            PrivateKey prvKey = (prvData == null) ? null : prvData.getKey();
-            ValidateUtils.checkNotNull(prvKey, "Empty private key #%d in %s", keyIndex, resourceKey);
-
-            String prvType = KeyUtils.getKeyType(prvKey);
-            ValidateUtils.checkTrue(Objects.equals(pubType, prvType),
-                    "Mismatched public (%s) vs. private (%s) key type #%d in %s",
-                    pubType, prvType, keyIndex, resourceKey);
-
-            if (traceEnabled) {
-                log.trace("extractKeyPairs({}) add private key #{}: {} {}",
-                        resourceKey, keyIndex, prvType, prvData.getValue());
-            }
-            keyPairs.add(new KeyPair(pubKey, prvKey));
-        }
-
-        return keyPairs;
-    }
-
-    protected SimpleImmutableEntry<PrivateKey, String> readPrivateKey(
-            String resourceKey, OpenSSHParserContext context, String keyType, FilePasswordProvider passwordProvider, InputStream stream)
-                    throws IOException, GeneralSecurityException {
-        String prvType = KeyEntryResolver.decodeString(stream);
-        if (!Objects.equals(keyType, prvType)) {
-            throw new StreamCorruptedException("Mismatched private key type: "
-                    + ", expected=" + keyType + ", actual=" + prvType
-                    + " in " + resourceKey);
-        }
-
-        PrivateKeyEntryDecoder<?, ?> decoder = getPrivateKeyEntryDecoder(prvType);
-        if (decoder == null) {
-            throw new NoSuchAlgorithmException("Unsupported key type (" + prvType + ") in " + resourceKey);
-        }
-
-        PrivateKey prvKey = decoder.decodePrivateKey(prvType, passwordProvider, stream);
-        if (prvKey == null) {
-            throw new InvalidKeyException("Cannot parse key type (" + prvType + ") in " + resourceKey);
-        }
-
-        String comment = KeyEntryResolver.decodeString(stream);
-        return new SimpleImmutableEntry<>(prvKey, comment);
-    }
-
-    protected <S extends InputStream> S validateStreamMagicMarker(String resourceKey, S stream) throws IOException {
-        byte[] actual = new byte[AUTH_MAGIC_BYTES.length];
-        IoUtils.readFully(stream, actual);
-        if (!Arrays.equals(AUTH_MAGIC_BYTES, actual)) {
-            throw new StreamCorruptedException(resourceKey + ": Mismatched magic marker value: " + BufferUtils.toHex(':', actual));
-        }
-
-        int eos = stream.read();
-        if (eos == -1) {
-            throw new EOFException(resourceKey + ": Premature EOF after magic marker value");
-        }
-
-        if (eos != 0) {
-            throw new StreamCorruptedException(resourceKey + ": Missing EOS after magic marker value: 0x" + Integer.toHexString(eos));
-        }
-
-        return stream;
-    }
-
-    /**
-     * @param decoder The decoder to register
-     * @throws IllegalArgumentException if no decoder or not key type or no
-     *                                  supported names for the decoder
-     * @see PrivateKeyEntryDecoder#getPublicKeyType()
-     * @see PrivateKeyEntryDecoder#getSupportedTypeNames()
-     */
-    public static void registerPrivateKeyEntryDecoder(PrivateKeyEntryDecoder<?, ?> decoder) {
-        Objects.requireNonNull(decoder, "No decoder specified");
-
-        Class<?> pubType = Objects.requireNonNull(decoder.getPublicKeyType(), "No public key type declared");
-        Class<?> prvType = Objects.requireNonNull(decoder.getPrivateKeyType(), "No private key type declared");
-        synchronized (BY_KEY_CLASS_DECODERS_MAP) {
-            BY_KEY_CLASS_DECODERS_MAP.put(pubType, decoder);
-            BY_KEY_CLASS_DECODERS_MAP.put(prvType, decoder);
-        }
-
-        Collection<String> names = ValidateUtils.checkNotNullAndNotEmpty(decoder.getSupportedTypeNames(), "No supported key type");
-        synchronized (BY_KEY_TYPE_DECODERS_MAP) {
-            for (String n : names) {
-                PrivateKeyEntryDecoder<?, ?> prev = BY_KEY_TYPE_DECODERS_MAP.put(n, decoder);
-                if (prev != null) {
-                    //noinspection UnnecessaryContinue
-                    continue;   // debug breakpoint
-                }
-            }
-        }
-    }
-
-    /**
-     * @param keyType The {@code OpenSSH} key type string -  e.g., {@code ssh-rsa, ssh-dss}
-     *                - ignored if {@code null}/empty
-     * @return The registered {@link PrivateKeyEntryDecoder} or {code null} if not found
-     */
-    public static PrivateKeyEntryDecoder<?, ?> getPrivateKeyEntryDecoder(String keyType) {
-        if (GenericUtils.isEmpty(keyType)) {
-            return null;
-        }
-
-        synchronized (BY_KEY_TYPE_DECODERS_MAP) {
-            return BY_KEY_TYPE_DECODERS_MAP.get(keyType);
-        }
-    }
-
-    /**
-     * @param kp The {@link KeyPair} to examine - ignored if {@code null}
-     * @return The matching {@link PrivateKeyEntryDecoder} provided <U>both</U>
-     * the public and private keys have the same decoder - {@code null} if no
-     * match found
-     * @see #getPrivateKeyEntryDecoder(Key)
-     */
-    public static PrivateKeyEntryDecoder<?, ?> getPrivateKeyEntryDecoder(KeyPair kp) {
-        if (kp == null) {
-            return null;
-        }
-
-        PrivateKeyEntryDecoder<?, ?> d1 = getPrivateKeyEntryDecoder(kp.getPublic());
-        PrivateKeyEntryDecoder<?, ?> d2 = getPrivateKeyEntryDecoder(kp.getPrivate());
-        if (d1 == d2) {
-            return d1;
-        } else {
-            return null;    // some kind of mixed keys...
-        }
-    }
-
-    /**
-     * @param key The {@link Key} (public or private) - ignored if {@code null}
-     * @return The registered {@link PrivateKeyEntryDecoder} for this key or {code null} if no match found
-     * @see #getPrivateKeyEntryDecoder(Class)
-     */
-    public static PrivateKeyEntryDecoder<?, ?> getPrivateKeyEntryDecoder(Key key) {
-        if (key == null) {
-            return null;
-        } else {
-            return getPrivateKeyEntryDecoder(key.getClass());
-        }
-    }
-
-    /**
-     * @param keyType The key {@link Class} - ignored if {@code null} or not a {@link Key}
-     *                compatible type
-     * @return The registered {@link PrivateKeyEntryDecoder} or {code null} if no match found
-     */
-    public static PrivateKeyEntryDecoder<?, ?> getPrivateKeyEntryDecoder(Class<?> keyType) {
-        if ((keyType == null) || (!Key.class.isAssignableFrom(keyType))) {
-            return null;
-        }
-
-        synchronized (BY_KEY_TYPE_DECODERS_MAP) {
-            PrivateKeyEntryDecoder<?, ?> decoder = BY_KEY_CLASS_DECODERS_MAP.get(keyType);
-            if (decoder != null) {
-                return decoder;
-            }
-
-            // in case it is a derived class
-            for (PrivateKeyEntryDecoder<?, ?> dec : BY_KEY_CLASS_DECODERS_MAP.values()) {
-                Class<?> pubType = dec.getPublicKeyType();
-                Class<?> prvType = dec.getPrivateKeyType();
-                if (pubType.isAssignableFrom(keyType) || prvType.isAssignableFrom(keyType)) {
-                    return dec;
-                }
-            }
-        }
-
-        return null;
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHParserContext.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHParserContext.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHParserContext.java
deleted file mode 100644
index 07f2a9a..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHParserContext.java
+++ /dev/null
@@ -1,83 +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.config.keys.loader.openssh;
-
-import java.util.function.Predicate;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.buffer.BufferUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class OpenSSHParserContext {
-    public static final String NONE_CIPHER = "none";
-    public static final Predicate<String> IS_NONE_CIPHER = c -> GenericUtils.isEmpty(c) || NONE_CIPHER.equalsIgnoreCase(c);
-
-    public static final String NONE_KDF = "none";
-    public static final Predicate<String> IS_NONE_KDF = c -> GenericUtils.isEmpty(c) || NONE_KDF.equalsIgnoreCase(c);
-
-    private String cipherName;
-    private String kdfName;
-    private byte[] kdfOptions;
-
-    public OpenSSHParserContext() {
-        super();
-    }
-
-    public OpenSSHParserContext(String cipherName, String kdfName, byte... kdfOptions) {
-        this.cipherName = cipherName;
-        this.kdfName = kdfName;
-        this.kdfOptions = kdfOptions;
-    }
-
-    public String getCipherName() {
-        return cipherName;
-    }
-
-    public void setCipherName(String cipherName) {
-        this.cipherName = cipherName;
-    }
-
-    public String getKdfName() {
-        return kdfName;
-    }
-
-    public void setKdfName(String kdfName) {
-        this.kdfName = kdfName;
-    }
-
-    public byte[] getKdfOptions() {
-        return kdfOptions;
-    }
-
-    public void setKdfOptions(byte[] kdfOptions) {
-        this.kdfOptions = kdfOptions;
-    }
-
-    @Override
-    public String toString() {
-        return getClass().getSimpleName()
-            + "[cipher=" + getCipherName()
-            + ", kdfName=" + getKdfName()
-            + ", kdfOptions=" + BufferUtils.toHex(':', getKdfOptions())
-            + "]";
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHRSAPrivateKeyDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHRSAPrivateKeyDecoder.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHRSAPrivateKeyDecoder.java
deleted file mode 100644
index 72e003f..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHRSAPrivateKeyDecoder.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.common.config.keys.loader.openssh;
-
-import java.io.IOException;
-import java.io.InputStream;
-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.RSAPrivateKeySpec;
-import java.security.spec.RSAPublicKeySpec;
-import java.util.Collections;
-import java.util.Objects;
-
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.config.keys.KeyEntryResolver;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.config.keys.impl.AbstractPrivateKeyEntryDecoder;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class OpenSSHRSAPrivateKeyDecoder extends AbstractPrivateKeyEntryDecoder<RSAPublicKey, RSAPrivateKey> {
-    public static final BigInteger DEFAULT_PUBLIC_EXPONENT = new BigInteger("65537");
-    public static final OpenSSHRSAPrivateKeyDecoder INSTANCE = new OpenSSHRSAPrivateKeyDecoder();
-
-    public OpenSSHRSAPrivateKeyDecoder() {
-        super(RSAPublicKey.class, RSAPrivateKey.class, Collections.unmodifiableList(Collections.singletonList(KeyPairProvider.SSH_RSA)));
-    }
-
-    @Override
-    public RSAPrivateKey decodePrivateKey(String keyType, FilePasswordProvider passwordProvider, InputStream keyData)
-            throws IOException, GeneralSecurityException {
-        if (!KeyPairProvider.SSH_RSA.equals(keyType)) { // just in case we were invoked directly
-            throw new InvalidKeySpecException("Unexpected key type: " + keyType);
-        }
-
-        BigInteger n = KeyEntryResolver.decodeBigInt(keyData);
-        BigInteger e = KeyEntryResolver.decodeBigInt(keyData);
-        if (!Objects.equals(e, DEFAULT_PUBLIC_EXPONENT)) {
-            log.warn("decodePrivateKey({}) non-standard RSA exponent found: {}", keyType, e);
-        }
-
-        BigInteger d = KeyEntryResolver.decodeBigInt(keyData);
-        BigInteger inverseQmodP = KeyEntryResolver.decodeBigInt(keyData);
-        Objects.requireNonNull(inverseQmodP, "Missing iqmodp"); // TODO run some validation on it
-        BigInteger p = KeyEntryResolver.decodeBigInt(keyData);
-        BigInteger q = KeyEntryResolver.decodeBigInt(keyData);
-        BigInteger modulus = p.multiply(q);
-        if (!Objects.equals(n, modulus)) {
-            log.warn("decodePrivateKey({}) mismatched modulus values: encoded={}, calculated={}",
-                     keyType, n, modulus);
-        }
-
-        return generatePrivateKey(new RSAPrivateKeySpec(n, d));
-    }
-
-    @Override
-    public boolean isPublicKeyRecoverySupported() {
-        return true;
-    }
-
-    @Override
-    public RSAPublicKey recoverPublicKey(RSAPrivateKey privateKey) throws GeneralSecurityException {
-        return KeyUtils.recoverRSAPublicKey(privateKey);
-    }
-
-    @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(KeyUtils.RSA_ALGORITHM);
-    }
-
-    @Override
-    public KeyFactory getKeyFactoryInstance() throws GeneralSecurityException {
-        return SecurityUtils.getKeyFactory(KeyUtils.RSA_ALGORITHM);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/AbstractPEMResourceKeyPairParser.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/AbstractPEMResourceKeyPairParser.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/AbstractPEMResourceKeyPairParser.java
deleted file mode 100644
index bee13d6..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/AbstractPEMResourceKeyPairParser.java
+++ /dev/null
@@ -1,167 +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.config.keys.loader.pem;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.StreamCorruptedException;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.security.NoSuchAlgorithmException;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-import javax.security.auth.login.CredentialException;
-import javax.security.auth.login.FailedLoginException;
-
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.config.keys.loader.AbstractKeyPairResourceParser;
-import org.apache.sshd.common.config.keys.loader.KeyPairResourceParser;
-import org.apache.sshd.common.config.keys.loader.PrivateKeyEncryptionContext;
-import org.apache.sshd.common.config.keys.loader.PrivateKeyObfuscator;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.buffer.BufferUtils;
-
-/**
- * Base class for PEM file key-pair loaders
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class AbstractPEMResourceKeyPairParser
-        extends AbstractKeyPairResourceParser
-        implements KeyPairPEMResourceParser {
-    private final String algo;
-    private final String algId;
-
-    protected AbstractPEMResourceKeyPairParser(String algo, String algId, List<String> beginners, List<String> enders) {
-        super(beginners, enders);
-        this.algo = ValidateUtils.checkNotNullAndNotEmpty(algo, "No encryption algorithm provided");
-        this.algId = ValidateUtils.checkNotNullAndNotEmpty(algId, "No algorithm identifier provided");
-    }
-
-    @Override
-    public String getAlgorithm() {
-        return algo;
-    }
-
-    @Override
-    public String getAlgorithmIdentifier() {
-        return algId;
-    }
-
-    @Override
-    public Collection<KeyPair> extractKeyPairs(
-            String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, List<String> lines)
-                    throws IOException, GeneralSecurityException {
-        if (GenericUtils.isEmpty(lines)) {
-            return Collections.emptyList();
-        }
-
-        Boolean encrypted = null;
-        byte[] initVector = null;
-        String algInfo = null;
-        int dataStartIndex = -1;
-        for (int index = 0; index < lines.size(); index++) {
-            String line = GenericUtils.trimToEmpty(lines.get(index));
-            if (GenericUtils.isEmpty(line)) {
-                continue;
-            }
-
-            // check if header line - if not, assume data lines follow
-            int headerPos = line.indexOf(':');
-            if (headerPos < 0) {
-                dataStartIndex = index;
-                break;
-            }
-
-            if (line.startsWith("Proc-Type:")) {
-                if (encrypted != null) {
-                    throw new StreamCorruptedException("Multiple encryption indicators in " + resourceKey);
-                }
-
-                line = line.substring(headerPos + 1).trim();
-                line = line.toUpperCase();
-                encrypted = Boolean.valueOf(line.contains("ENCRYPTED"));
-            } else if (line.startsWith("DEK-Info:")) {
-                if ((initVector != null) || (algInfo != null)) {
-                    throw new StreamCorruptedException("Multiple encryption settings in " + resourceKey);
-                }
-
-                line = line.substring(headerPos + 1).trim();
-                headerPos = line.indexOf(',');
-                if (headerPos < 0) {
-                    throw new StreamCorruptedException(resourceKey + ": Missing encryption data values separator in line '" + line + "'");
-                }
-
-                algInfo = line.substring(0, headerPos).trim();
-
-                String algInitVector = line.substring(headerPos + 1).trim();
-                initVector = BufferUtils.decodeHex(BufferUtils.EMPTY_HEX_SEPARATOR, algInitVector);
-            }
-        }
-
-        if (dataStartIndex < 0) {
-            throw new StreamCorruptedException("No data lines (only headers or empty) found in " + resourceKey);
-        }
-
-        List<String> dataLines = lines.subList(dataStartIndex, lines.size());
-        if ((encrypted != null) || (algInfo != null) || (initVector != null)) {
-            if (passwordProvider == null) {
-                throw new CredentialException("Missing password provider for encrypted resource=" + resourceKey);
-            }
-
-            String password = passwordProvider.getPassword(resourceKey);
-            if (GenericUtils.isEmpty(password)) {
-                throw new FailedLoginException("No password data for encrypted resource=" + resourceKey);
-            }
-
-            PrivateKeyEncryptionContext encContext = new PrivateKeyEncryptionContext(algInfo);
-            encContext.setPassword(password);
-            encContext.setInitVector(initVector);
-            byte[] encryptedData = KeyPairResourceParser.extractDataBytes(dataLines);
-            byte[] decodedData = applyPrivateKeyCipher(encryptedData, encContext, false);
-            try (InputStream bais = new ByteArrayInputStream(decodedData)) {
-                return extractKeyPairs(resourceKey, beginMarker, endMarker, passwordProvider, bais);
-            }
-        }
-
-        return super.extractKeyPairs(resourceKey, beginMarker, endMarker, passwordProvider, dataLines);
-    }
-
-    protected byte[] applyPrivateKeyCipher(byte[] bytes, PrivateKeyEncryptionContext encContext, boolean encryptIt) throws GeneralSecurityException {
-        String cipherName = encContext.getCipherName();
-        PrivateKeyObfuscator o = encContext.resolvePrivateKeyObfuscator();
-        if (o == null) {
-            throw new NoSuchAlgorithmException("decryptPrivateKeyData(" + encContext + ")[encrypt=" + encryptIt + "] unknown cipher: " + cipherName);
-        }
-
-        if (encryptIt) {
-            byte[]  initVector = encContext.getInitVector();
-            if (GenericUtils.isEmpty(initVector)) {
-                initVector = o.generateInitializationVector(encContext);
-                encContext.setInitVector(initVector);
-            }
-        }
-
-        return o.applyPrivateKeyCipher(bytes, encContext, encryptIt);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/DSSPEMResourceKeyPairParser.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/DSSPEMResourceKeyPairParser.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/DSSPEMResourceKeyPairParser.java
deleted file mode 100644
index 0c357af..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/DSSPEMResourceKeyPairParser.java
+++ /dev/null
@@ -1,126 +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.config.keys.loader.pem;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.StreamCorruptedException;
-import java.math.BigInteger;
-import java.security.GeneralSecurityException;
-import java.security.KeyFactory;
-import java.security.KeyPair;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.spec.DSAPrivateKeySpec;
-import java.security.spec.DSAPublicKeySpec;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.util.io.NoCloseInputStream;
-import org.apache.sshd.common.util.io.der.ASN1Object;
-import org.apache.sshd.common.util.io.der.ASN1Type;
-import org.apache.sshd.common.util.io.der.DERParser;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class DSSPEMResourceKeyPairParser extends AbstractPEMResourceKeyPairParser {
-    // Not exactly according to standard but good enough
-    public static final String BEGIN_MARKER = "BEGIN DSA PRIVATE KEY";
-    public static final List<String> BEGINNERS =
-            Collections.unmodifiableList(Collections.singletonList(BEGIN_MARKER));
-
-    public static final String END_MARKER = "END DSA PRIVATE KEY";
-    public static final List<String> ENDERS =
-            Collections.unmodifiableList(Collections.singletonList(END_MARKER));
-
-    /**
-     * @see <A HREF="https://tools.ietf.org/html/rfc3279#section-2.3.2">RFC-3279 section 2.3.2</A>
-     */
-    public static final String DSS_OID = "1.2.840.10040.4.1";
-
-    public static final DSSPEMResourceKeyPairParser INSTANCE = new DSSPEMResourceKeyPairParser();
-
-    public DSSPEMResourceKeyPairParser() {
-        super(KeyUtils.DSS_ALGORITHM, DSS_OID, BEGINNERS, ENDERS);
-    }
-
-    @Override
-    public Collection<KeyPair> extractKeyPairs(
-            String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, InputStream stream)
-                    throws IOException, GeneralSecurityException {
-        KeyPair kp = decodeDSSKeyPair(SecurityUtils.getKeyFactory(KeyUtils.DSS_ALGORITHM), stream, false);
-        return Collections.singletonList(kp);
-    }
-
-    /**
-     * <p>The ASN.1 syntax for the private key:</P>
-     * <pre><code>
-     * DSAPrivateKey ::= SEQUENCE {
-     *      version Version,
-     *      p       INTEGER,
-     *      q       INTEGER,
-     *      g       INTEGER,
-     *      y       INTEGER,
-     *      x       INTEGER
-     * }
-     * </code></pre>
-     * @param kf The {@link KeyFactory} To use to generate the keys
-     * @param s The {@link InputStream} containing the encoded bytes
-     * @param okToClose <code>true</code> if the method may close the input
-     * stream regardless of success or failure
-     * @return The recovered {@link KeyPair}
-     * @throws IOException If failed to read or decode the bytes
-     * @throws GeneralSecurityException If failed to generate the keys
-     */
-    public static KeyPair decodeDSSKeyPair(KeyFactory kf, InputStream s, boolean okToClose)
-            throws IOException, GeneralSecurityException {
-        ASN1Object sequence;
-        try (DERParser parser = new DERParser(NoCloseInputStream.resolveInputStream(s, okToClose))) {
-            sequence = parser.readObject();
-        }
-
-        if (!ASN1Type.SEQUENCE.equals(sequence.getObjType())) {
-            throw new IOException("Invalid DER: not a sequence: " + sequence.getObjType());
-        }
-
-        // Parse inside the sequence
-        try (DERParser parser = sequence.createParser()) {
-            // Skip version
-            ASN1Object version = parser.readObject();
-            if (version == null) {
-                throw new StreamCorruptedException("No version");
-            }
-
-            BigInteger p = parser.readObject().asInteger();
-            BigInteger q = parser.readObject().asInteger();
-            BigInteger g = parser.readObject().asInteger();
-            BigInteger y = parser.readObject().asInteger();
-            BigInteger x = parser.readObject().asInteger();
-            PublicKey pubKey = kf.generatePublic(new DSAPublicKeySpec(y, p, q, g));
-            PrivateKey prvKey = kf.generatePrivate(new DSAPrivateKeySpec(x, p, q, g));
-            return new KeyPair(pubKey, prvKey);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/ECDSAPEMResourceKeyPairParser.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/ECDSAPEMResourceKeyPairParser.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/ECDSAPEMResourceKeyPairParser.java
deleted file mode 100644
index dd8d2ea..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/ECDSAPEMResourceKeyPairParser.java
+++ /dev/null
@@ -1,220 +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.config.keys.loader.pem;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.StreamCorruptedException;
-import java.math.BigInteger;
-import java.security.GeneralSecurityException;
-import java.security.KeyFactory;
-import java.security.KeyPair;
-import java.security.NoSuchProviderException;
-import java.security.interfaces.ECPrivateKey;
-import java.security.interfaces.ECPublicKey;
-import java.security.spec.ECPoint;
-import java.security.spec.ECPrivateKeySpec;
-import java.security.spec.ECPublicKeySpec;
-import java.util.AbstractMap.SimpleImmutableEntry;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.sshd.common.cipher.ECCurves;
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.util.io.NoCloseInputStream;
-import org.apache.sshd.common.util.io.der.ASN1Object;
-import org.apache.sshd.common.util.io.der.ASN1Type;
-import org.apache.sshd.common.util.io.der.DERParser;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class ECDSAPEMResourceKeyPairParser extends AbstractPEMResourceKeyPairParser {
-    public static final String BEGIN_MARKER = "BEGIN EC PRIVATE KEY";
-    public static final List<String> BEGINNERS =
-            Collections.unmodifiableList(Collections.singletonList(BEGIN_MARKER));
-
-    public static final String END_MARKER = "END EC PRIVATE KEY";
-    public static final List<String> ENDERS =
-            Collections.unmodifiableList(Collections.singletonList(END_MARKER));
-
-    /**
-     * @see <A HREF="https://tools.ietf.org/html/rfc3279#section-2.3.5">RFC-3279 section 2.3.5</A>
-     */
-    public static final String ECDSA_OID = "1.2.840.10045.2.1";
-
-    public static final ECDSAPEMResourceKeyPairParser INSTANCE = new ECDSAPEMResourceKeyPairParser();
-
-    public ECDSAPEMResourceKeyPairParser() {
-        super(KeyUtils.EC_ALGORITHM, ECDSA_OID, BEGINNERS, ENDERS);
-    }
-
-    @Override
-    public Collection<KeyPair> extractKeyPairs(
-            String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, InputStream stream)
-                    throws IOException, GeneralSecurityException {
-        Map.Entry<ECPublicKeySpec, ECPrivateKeySpec> spec = decodeECPrivateKeySpec(stream, false);
-        if (!SecurityUtils.isECCSupported()) {
-            throw new NoSuchProviderException("ECC not supported");
-        }
-
-        KeyFactory kf = SecurityUtils.getKeyFactory(KeyUtils.EC_ALGORITHM);
-        ECPublicKey pubKey = (ECPublicKey) kf.generatePublic(spec.getKey());
-        ECPrivateKey prvKey = (ECPrivateKey) kf.generatePrivate(spec.getValue());
-        KeyPair kp = new KeyPair(pubKey, prvKey);
-        return Collections.singletonList(kp);
-    }
-
-    /**
-     * <P>ASN.1 syntax according to rfc5915 is:</P></BR>
-     * <PRE><CODE>
-     * ECPrivateKey ::= SEQUENCE {
-     *      version        INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1),
-     *      privateKey     OCTET STRING,
-     *      parameters [0] ECParameters {{ NamedCurve }} OPTIONAL,
-     *      publicKey  [1] BIT STRING OPTIONAL
-     * }
-     * </CODE></PRE>
-     * <P><I>ECParameters</I> syntax according to RFC5480:</P></BR>
-     * <PRE><CODE>
-     * ECParameters ::= CHOICE {
-     *      namedCurve         OBJECT IDENTIFIER
-     *      -- implicitCurve   NULL
-     *      -- specifiedCurve  SpecifiedECDomain
-     * }
-     * </CODE></PRE>
-     * @param inputStream The {@link InputStream} containing the DER encoded data
-     * @param okToClose {@code true} if OK to close the DER stream once parsing complete
-     * @return The decoded {@link SimpleImmutableEntry} of {@link ECPublicKeySpec} and {@link ECPrivateKeySpec}
-     * @throws IOException If failed to to decode the DER stream
-     */
-    public static SimpleImmutableEntry<ECPublicKeySpec, ECPrivateKeySpec> decodeECPrivateKeySpec(InputStream inputStream, boolean okToClose) throws IOException {
-        ASN1Object sequence;
-        try (DERParser parser = new DERParser(NoCloseInputStream.resolveInputStream(inputStream, okToClose))) {
-            sequence = parser.readObject();
-        }
-
-        if (!ASN1Type.SEQUENCE.equals(sequence.getObjType())) {
-            throw new IOException("Invalid DER: not a sequence: " + sequence.getObjType());
-        }
-
-        // Parse inside the sequence
-        try (DERParser parser = sequence.createParser()) {
-            ECPrivateKeySpec prvSpec = decodeECPrivateKeySpec(parser);
-            ECCurves curve = ECCurves.fromCurveParameters(prvSpec.getParams());
-            if (curve == null) {
-                throw new StreamCorruptedException("Unknown curve");
-            }
-
-            ECPoint w = decodeECPublicKeyValue(curve, parser);
-            ECPublicKeySpec pubSpec = new ECPublicKeySpec(w, prvSpec.getParams());
-            return new SimpleImmutableEntry<>(pubSpec, prvSpec);
-        }
-    }
-
-    public static final ECPrivateKeySpec decodeECPrivateKeySpec(DERParser parser) throws IOException {
-        // see openssl asn1parse -inform PEM -in ...file... -dump
-        ASN1Object versionObject = parser.readObject(); // Skip version
-        if (versionObject == null) {
-            throw new StreamCorruptedException("No version");
-        }
-
-        // as per RFC-5915 section 3
-        BigInteger version = versionObject.asInteger();
-        if (!BigInteger.ONE.equals(version)) {
-            throw new StreamCorruptedException("Bad version value: " + version);
-        }
-
-        ASN1Object keyObject = parser.readObject();
-        if (keyObject == null) {
-            throw new StreamCorruptedException("No private key value");
-        }
-
-        ASN1Type objType = keyObject.getObjType();
-        if (!ASN1Type.OCTET_STRING.equals(objType)) {
-            throw new StreamCorruptedException("Non-matching private key object type: " + objType);
-        }
-
-        ASN1Object paramsObject = parser.readObject();
-        if (paramsObject == null) {
-            throw new StreamCorruptedException("No parameters value");
-        }
-
-        // TODO make sure params object tag is 0xA0
-
-        final List<Integer> curveOID;
-        try (DERParser paramsParser = paramsObject.createParser()) {
-            ASN1Object namedCurve = paramsParser.readObject();
-            if (namedCurve == null) {
-                throw new StreamCorruptedException("Missing named curve parameter");
-            }
-
-            curveOID = namedCurve.asOID();
-        }
-
-        ECCurves curve = ECCurves.fromOIDValue(curveOID);
-        if (curve == null) {
-            throw new StreamCorruptedException("Unknown curve OID: " + curveOID);
-        }
-
-        BigInteger s = ECCurves.octetStringToInteger(keyObject.getPureValueBytes());
-        return new ECPrivateKeySpec(s, curve.getParameters());
-    }
-
-    /**
-     * <P>ASN.1 syntax according to rfc5915 is:</P></BR>
-     * <PRE>
-     *      publicKey  [1] BIT STRING OPTIONAL
-     * </PRE>
-     * @param curve The {@link ECCurves} curve
-     * @param parser The {@link DERParser} assumed to be positioned at the
-     * start of the data
-     * @return The encoded {@link ECPoint}
-     * @throws IOException If failed to create the point
-     */
-    public static final ECPoint decodeECPublicKeyValue(ECCurves curve, DERParser parser) throws IOException {
-        // see openssl asn1parse -inform PEM -in ...file... -dump
-        ASN1Object dataObject = parser.readObject();
-        if (dataObject == null) {
-            throw new StreamCorruptedException("No public key data bytes");
-        }
-
-        try (DERParser dataParser = dataObject.createParser()) {
-            ASN1Object pointData = dataParser.readObject();
-            if (pointData == null) {
-                throw new StreamCorruptedException("Missing public key data parameter");
-            }
-
-            ASN1Type objType = pointData.getObjType();
-            if (!ASN1Type.BIT_STRING.equals(objType)) {
-                throw new StreamCorruptedException("Non-matching public key object type: " + objType);
-            }
-
-            // see https://tools.ietf.org/html/rfc5480#section-2.2
-            byte[] octets = pointData.getValue();
-            return ECCurves.octetStringToEcPoint(octets);
-        }
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/KeyPairPEMResourceParser.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/KeyPairPEMResourceParser.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/KeyPairPEMResourceParser.java
deleted file mode 100644
index 07c7e44..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/KeyPairPEMResourceParser.java
+++ /dev/null
@@ -1,37 +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.config.keys.loader.pem;
-
-import org.apache.sshd.common.config.keys.loader.KeyPairResourceParser;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface KeyPairPEMResourceParser extends KeyPairResourceParser {
-    /**
-     * @return The encryption algorithm name - e.g., &quot;RSA&quot;, &quot;DSA&quot;
-     */
-    String getAlgorithm();
-
-    /**
-     * @return The OID used to identify this algorithm in DER encodings - e.g., RSA=1.2.840.113549.1.1.1
-     */
-    String getAlgorithmIdentifier();
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PEMResourceParserUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PEMResourceParserUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PEMResourceParserUtils.java
deleted file mode 100644
index b6749da..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PEMResourceParserUtils.java
+++ /dev/null
@@ -1,109 +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.config.keys.loader.pem;
-
-import java.io.IOException;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.TreeMap;
-import java.util.concurrent.atomic.AtomicReference;
-
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.config.keys.loader.KeyPairResourceParser;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public final class PEMResourceParserUtils {
-    public static final KeyPairResourceParser PROXY = new KeyPairResourceParser() {
-        @Override
-        public Collection<KeyPair> loadKeyPairs(
-                String resourceKey, FilePasswordProvider passwordProvider, List<String> lines)
-                        throws IOException, GeneralSecurityException {
-            @SuppressWarnings("synthetic-access")
-            KeyPairResourceParser proxy = PROXY_HOLDER.get();
-            return (proxy == null) ? Collections.<KeyPair>emptyList() : proxy.loadKeyPairs(resourceKey, passwordProvider, lines);
-        }
-
-        @Override
-        public boolean canExtractKeyPairs(String resourceKey, List<String> lines)
-                throws IOException, GeneralSecurityException {
-            @SuppressWarnings("synthetic-access")
-            KeyPairResourceParser proxy = PROXY_HOLDER.get();
-            return (proxy != null) && proxy.canExtractKeyPairs(resourceKey, lines);
-        }
-    };
-
-    private static final Map<String, KeyPairPEMResourceParser> BY_OID_MAP = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
-    private static final Map<String, KeyPairPEMResourceParser> BY_ALGORITHM_MAP = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
-    private static final AtomicReference<KeyPairResourceParser> PROXY_HOLDER = new AtomicReference<>(KeyPairResourceParser.EMPTY);
-
-    static {
-        registerPEMResourceParser(RSAPEMResourceKeyPairParser.INSTANCE);
-        registerPEMResourceParser(DSSPEMResourceKeyPairParser.INSTANCE);
-        registerPEMResourceParser(ECDSAPEMResourceKeyPairParser.INSTANCE);
-        registerPEMResourceParser(PKCS8PEMResourceKeyPairParser.INSTANCE);
-    }
-
-    private PEMResourceParserUtils() {
-        throw new UnsupportedOperationException("No instance");
-    }
-
-    public static void registerPEMResourceParser(KeyPairPEMResourceParser parser) {
-        Objects.requireNonNull(parser, "No parser to register");
-        synchronized (BY_OID_MAP) {
-            BY_OID_MAP.put(ValidateUtils.checkNotNullAndNotEmpty(parser.getAlgorithmIdentifier(), "No OID value"), parser);
-        }
-
-        synchronized (BY_ALGORITHM_MAP) {
-            BY_ALGORITHM_MAP.put(ValidateUtils.checkNotNullAndNotEmpty(parser.getAlgorithm(), "No algorithm value"), parser);
-            // Use a copy in order to avoid concurrent modifications
-            PROXY_HOLDER.set(KeyPairResourceParser.aggregate(new ArrayList<>(BY_ALGORITHM_MAP.values())));
-        }
-    }
-
-    public static KeyPairPEMResourceParser getPEMResourceParserByOid(String oid) {
-        if (GenericUtils.isEmpty(oid)) {
-            return null;
-        }
-
-        synchronized (BY_OID_MAP) {
-            return BY_OID_MAP.get(oid);
-        }
-    }
-
-    public static KeyPairPEMResourceParser getPEMResourceParserByAlgorithm(String algorithm) {
-        if (GenericUtils.isEmpty(algorithm)) {
-            return null;
-        }
-
-        synchronized (BY_ALGORITHM_MAP) {
-            return BY_ALGORITHM_MAP.get(algorithm);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParser.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParser.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParser.java
deleted file mode 100644
index b333f23..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParser.java
+++ /dev/null
@@ -1,156 +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.config.keys.loader.pem;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.StreamCorruptedException;
-import java.security.GeneralSecurityException;
-import java.security.KeyFactory;
-import java.security.KeyPair;
-import java.security.NoSuchAlgorithmException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.spec.PKCS8EncodedKeySpec;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.common.util.io.der.ASN1Object;
-import org.apache.sshd.common.util.io.der.DERParser;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class PKCS8PEMResourceKeyPairParser extends AbstractPEMResourceKeyPairParser {
-    // Not exactly according to standard but good enough
-    public static final String BEGIN_MARKER = "BEGIN PRIVATE KEY";
-    public static final List<String> BEGINNERS =
-            Collections.unmodifiableList(Collections.singletonList(BEGIN_MARKER));
-
-    public static final String END_MARKER = "END PRIVATE KEY";
-    public static final List<String> ENDERS =
-            Collections.unmodifiableList(Collections.singletonList(END_MARKER));
-
-    public static final String PKCS8_FORMAT = "PKCS#8";
-
-    public static final PKCS8PEMResourceKeyPairParser INSTANCE = new PKCS8PEMResourceKeyPairParser();
-
-    public PKCS8PEMResourceKeyPairParser() {
-        super(PKCS8_FORMAT, PKCS8_FORMAT, BEGINNERS, ENDERS);
-    }
-
-    @Override
-    public Collection<KeyPair> extractKeyPairs(
-            String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, InputStream stream)
-                    throws IOException, GeneralSecurityException {
-        // Save the data before getting the algorithm OID since we will need it
-        byte[] encBytes = IoUtils.toByteArray(stream);
-        List<Integer> oidAlgorithm = getPKCS8AlgorithmIdentifier(encBytes);
-        PrivateKey prvKey = decodePEMPrivateKeyPKCS8(oidAlgorithm, encBytes, passwordProvider);
-        PublicKey pubKey = ValidateUtils.checkNotNull(KeyUtils.recoverPublicKey(prvKey),
-                "Failed to recover public key of OID=%s", oidAlgorithm);
-        KeyPair kp = new KeyPair(pubKey, prvKey);
-        return Collections.singletonList(kp);
-    }
-
-    public static PrivateKey decodePEMPrivateKeyPKCS8(
-            List<Integer> oidAlgorithm, byte[] keyBytes, FilePasswordProvider passwordProvider)
-                    throws GeneralSecurityException {
-        ValidateUtils.checkNotNullAndNotEmpty(oidAlgorithm, "No PKCS8 algorithm OID");
-        return decodePEMPrivateKeyPKCS8(GenericUtils.join(oidAlgorithm, '.'), keyBytes, passwordProvider);
-    }
-
-    public static PrivateKey decodePEMPrivateKeyPKCS8(
-            String oid, byte[] keyBytes, FilePasswordProvider passwordProvider)
-                    throws GeneralSecurityException {
-        KeyPairPEMResourceParser parser =
-            PEMResourceParserUtils.getPEMResourceParserByOid(
-                ValidateUtils.checkNotNullAndNotEmpty(oid, "No PKCS8 algorithm OID"));
-        if (parser == null) {
-            throw new NoSuchAlgorithmException("decodePEMPrivateKeyPKCS8(" + oid + ") unknown algorithm identifier");
-        }
-
-        String algorithm = ValidateUtils.checkNotNullAndNotEmpty(parser.getAlgorithm(), "No parser algorithm");
-        KeyFactory factory = SecurityUtils.getKeyFactory(algorithm);
-        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
-        return factory.generatePrivate(keySpec);
-    }
-
-    public static List<Integer> getPKCS8AlgorithmIdentifier(byte[] input) throws IOException {
-        try (DERParser parser = new DERParser(input)) {
-            return getPKCS8AlgorithmIdentifier(parser);
-        }
-    }
-
-    /**
-     * According to the standard:
-     * <PRE><CODE>
-     * PrivateKeyInfo ::= SEQUENCE {
-     *          version Version,
-     *          privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
-     *          privateKey PrivateKey,
-     *          attributes [0] IMPLICIT Attributes OPTIONAL
-     *  }
-     *
-     * Version ::= INTEGER
-     * PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
-     * PrivateKey ::= OCTET STRING
-     * Attributes ::= SET OF Attribute
-     * AlgorithmIdentifier ::= SEQUENCE {
-     *      algorithm       OBJECT IDENTIFIER,
-     *      parameters      ANY DEFINED BY algorithm OPTIONAL
-     * }
-     * </CODE></PRE>
-     * @param parser The {@link DERParser} to use
-     * @return The PKCS8 algorithm OID
-     * @throws IOException If malformed data
-     * @see #getPKCS8AlgorithmIdentifier(ASN1Object)
-     */
-    public static List<Integer> getPKCS8AlgorithmIdentifier(DERParser parser) throws IOException {
-        return getPKCS8AlgorithmIdentifier(parser.readObject());
-    }
-
-    public static List<Integer> getPKCS8AlgorithmIdentifier(ASN1Object privateKeyInfo) throws IOException {
-        try (DERParser parser = privateKeyInfo.createParser()) {
-            // Skip version
-            ASN1Object versionObject = parser.readObject();
-            if (versionObject == null) {
-                throw new StreamCorruptedException("No version");
-            }
-
-            ASN1Object privateKeyAlgorithm = parser.readObject();
-            if (privateKeyAlgorithm == null) {
-                throw new StreamCorruptedException("No private key algorithm");
-            }
-
-            try (DERParser oidParser = privateKeyAlgorithm.createParser()) {
-                ASN1Object oid = oidParser.readObject();
-                return oid.asOID();
-            }
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/RSAPEMResourceKeyPairParser.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/RSAPEMResourceKeyPairParser.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/RSAPEMResourceKeyPairParser.java
deleted file mode 100644
index d760aaf..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/RSAPEMResourceKeyPairParser.java
+++ /dev/null
@@ -1,142 +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.config.keys.loader.pem;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.StreamCorruptedException;
-import java.math.BigInteger;
-import java.security.GeneralSecurityException;
-import java.security.KeyFactory;
-import java.security.KeyPair;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.spec.RSAPrivateCrtKeySpec;
-import java.security.spec.RSAPrivateKeySpec;
-import java.security.spec.RSAPublicKeySpec;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.util.io.NoCloseInputStream;
-import org.apache.sshd.common.util.io.der.ASN1Object;
-import org.apache.sshd.common.util.io.der.ASN1Type;
-import org.apache.sshd.common.util.io.der.DERParser;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class RSAPEMResourceKeyPairParser extends AbstractPEMResourceKeyPairParser {
-    // Not exactly according to standard but good enough
-    public static final String BEGIN_MARKER = "BEGIN RSA PRIVATE KEY";
-    public static final List<String> BEGINNERS =
-            Collections.unmodifiableList(Collections.singletonList(BEGIN_MARKER));
-
-    public static final String END_MARKER = "END RSA PRIVATE KEY";
-    public static final List<String> ENDERS =
-            Collections.unmodifiableList(Collections.singletonList(END_MARKER));
-
-    /**
-     * @see <A HREF="https://tools.ietf.org/html/rfc3279#section-2.3.1">RFC-3279 section 2.3.1</A>
-     */
-    public static final String RSA_OID = "1.2.840.113549.1.1.1";
-
-    public static final RSAPEMResourceKeyPairParser INSTANCE = new RSAPEMResourceKeyPairParser();
-
-    public RSAPEMResourceKeyPairParser() {
-        super(KeyUtils.RSA_ALGORITHM, RSA_OID, BEGINNERS, ENDERS);
-    }
-
-    @Override
-    public Collection<KeyPair> extractKeyPairs(
-            String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, InputStream stream)
-                    throws IOException, GeneralSecurityException {
-        KeyPair kp = decodeRSAKeyPair(SecurityUtils.getKeyFactory(KeyUtils.RSA_ALGORITHM), stream, false);
-        return Collections.singletonList(kp);
-    }
-
-    /**
-     * <p>The ASN.1 syntax for the private key as per RFC-3447 section A.1.2:</P>
-     * <pre><code>
-     * RSAPrivateKey ::= SEQUENCE {
-     *   version           Version,
-     *   modulus           INTEGER,  -- n
-     *   publicExponent    INTEGER,  -- e
-     *   privateExponent   INTEGER,  -- d
-     *   prime1            INTEGER,  -- p
-     *   prime2            INTEGER,  -- q
-     *   exponent1         INTEGER,  -- d mod (p-1)
-     *   exponent2         INTEGER,  -- d mod (q-1)
-     *   coefficient       INTEGER,  -- (inverse of q) mod p
-     *   otherPrimeInfos   OtherPrimeInfos OPTIONAL
-     * }
-     * </code></pre>
-     * @param kf The {@link KeyFactory} To use to generate the keys
-     * @param s The {@link InputStream} containing the encoded bytes
-     * @param okToClose <code>true</code> if the method may close the input
-     * stream regardless of success or failure
-     * @return The recovered {@link KeyPair}
-     * @throws IOException If failed to read or decode the bytes
-     * @throws GeneralSecurityException If failed to generate the keys
-     */
-    public static KeyPair decodeRSAKeyPair(KeyFactory kf, InputStream s, boolean okToClose)
-            throws IOException, GeneralSecurityException {
-        ASN1Object sequence;
-        try (DERParser parser = new DERParser(NoCloseInputStream.resolveInputStream(s, okToClose))) {
-            sequence = parser.readObject();
-        }
-
-        if (!ASN1Type.SEQUENCE.equals(sequence.getObjType())) {
-            throw new IOException("Invalid DER: not a sequence: " + sequence.getObjType());
-        }
-
-        try (DERParser parser = sequence.createParser()) {
-            // Skip version
-            ASN1Object versionObject = parser.readObject();
-            if (versionObject == null) {
-                throw new StreamCorruptedException("No version");
-            }
-
-            // as per RFC-3447 section A.1.2
-            BigInteger version = versionObject.asInteger();
-            if (!BigInteger.ZERO.equals(version)) {
-                throw new StreamCorruptedException("Multi-primes N/A");
-            }
-
-            BigInteger modulus = parser.readObject().asInteger();
-            BigInteger publicExp = parser.readObject().asInteger();
-            PublicKey pubKey = kf.generatePublic(new RSAPublicKeySpec(modulus, publicExp));
-
-            BigInteger privateExp = parser.readObject().asInteger();
-            BigInteger primeP = parser.readObject().asInteger();
-            BigInteger primeQ = parser.readObject().asInteger();
-            BigInteger primeExponentP = parser.readObject().asInteger();
-            BigInteger primeExponentQ = parser.readObject().asInteger();
-            BigInteger crtCoef = parser.readObject().asInteger();
-            RSAPrivateKeySpec prvSpec = new RSAPrivateCrtKeySpec(
-                    modulus, publicExp, privateExp, primeP, primeQ, primeExponentP, primeExponentQ, crtCoef);
-            PrivateKey prvKey = kf.generatePrivate(prvSpec);
-            return new KeyPair(pubKey, prvKey);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/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
deleted file mode 100644
index a5ef6f9..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/digest/BaseDigest.java
+++ /dev/null
@@ -1,156 +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.digest;
-
-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.ValidateUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * Base class for Digest algorithms based on the JCE provider.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class BaseDigest implements Digest {
-
-    private final String algorithm;
-    private final int bsize;
-    private int h;
-    private String s;
-    private MessageDigest md;
-
-    /**
-     * Create a new digest using the given algorithm and block size.
-     * The initialization and creation of the underlying {@link MessageDigest}
-     * object will be done in the {@link #init()} method.
-     *
-     * @param algorithm the JCE algorithm to use for this digest
-     * @param bsize     the block size of this digest
-     */
-    public BaseDigest(String algorithm, int bsize) {
-        this.algorithm = ValidateUtils.checkNotNullAndNotEmpty(algorithm, "No algorithm");
-        ValidateUtils.checkTrue(bsize > 0, "Invalid block size: %d", bsize);
-        this.bsize = bsize;
-    }
-
-    @Override
-    public final String getAlgorithm() {
-        return algorithm;
-    }
-
-    @Override
-    public int getBlockSize() {
-        return bsize;
-    }
-
-    @Override
-    public void init() throws Exception {
-        this.md = SecurityUtils.getMessageDigest(getAlgorithm());
-    }
-
-    @Override
-    public void update(byte[] data) throws Exception {
-        update(data, 0, NumberUtils.length(data));
-    }
-
-    @Override
-    public void update(byte[] data, int start, int len) throws Exception {
-        Objects.requireNonNull(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 Objects.requireNonNull(md, "Digest not initialized").digest();
-    }
-
-    @Override
-    public int hashCode() {
-        synchronized (this) {
-            if (h == 0) {
-                h = Objects.hashCode(getAlgorithm()) + getBlockSize();
-                if (h == 0) {
-                    h = 1;
-                }
-            }
-        }
-
-        return h;
-    }
-
-    @Override
-    public int compareTo(Digest that) {
-        if (that == null) {
-            return -1;    // push null(s) to end
-        } else if (this == that) {
-            return 0;
-        }
-
-        String thisAlg = getAlgorithm();
-        String thatAlg = that.getAlgorithm();
-        int nRes = GenericUtils.safeCompare(thisAlg, thatAlg, false);
-        if (nRes != 0) {
-            return nRes;    // debug breakpoint
-        }
-
-        nRes = Integer.compare(this.getBlockSize(), that.getBlockSize());
-        if (nRes != 0) {
-            return nRes;    // debug breakpoint
-        }
-
-        return 0;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (obj == null) {
-            return false;
-        }
-        if (obj == this) {
-            return true;
-        }
-        if (getClass() != obj.getClass()) {
-            return false;
-        }
-
-        int nRes = compareTo((Digest) obj);
-        return nRes == 0;
-    }
-
-    @Override
-    public String toString() {
-        synchronized (this) {
-            if (s == null) {
-                s = getClass().getSimpleName() + "[" + getAlgorithm() + ":" + getBlockSize() + "]";
-            }
-        }
-
-        return s;
-    }
-}


[17/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common utilities code from sshd-core into sshd-common (new artifact)

Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureFactoriesManager.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureFactoriesManager.java b/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureFactoriesManager.java
deleted file mode 100644
index c9d876a..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureFactoriesManager.java
+++ /dev/null
@@ -1,94 +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.signature;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-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.ValidateUtils;
-
-/**
- * Manage the list of named factories for <code>Signature</code>.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface SignatureFactoriesManager {
-    /**
-     * @return The list of named <code>Signature</code> factories
-     */
-    List<NamedFactory<Signature>> getSignatureFactories();
-
-    default String getSignatureFactoriesNameList() {
-        return NamedResource.getNames(getSignatureFactories());
-    }
-
-    default List<String> getSignatureFactoriesNames() {
-        return NamedResource.getNameList(getSignatureFactories());
-    }
-
-    void setSignatureFactories(List<NamedFactory<Signature>> factories);
-
-    default void setSignatureFactoriesNameList(String names) {
-        setSignatureFactoriesNames(GenericUtils.split(names, ','));
-    }
-
-    default void setSignatureFactoriesNames(String... names) {
-        setSignatureFactoriesNames(GenericUtils.isEmpty((Object[]) names) ? Collections.emptyList() : Arrays.asList(names));
-    }
-
-    default void setSignatureFactoriesNames(Collection<String> names) {
-        BuiltinSignatures.ParseResult result = BuiltinSignatures.parseSignatureList(names);
-        @SuppressWarnings({ "rawtypes", "unchecked" })
-        List<NamedFactory<Signature>> factories =
-                (List) ValidateUtils.checkNotNullAndNotEmpty(result.getParsedFactories(), "No supported signature factories: %s", names);
-        Collection<String> unsupported = result.getUnsupportedFactories();
-        ValidateUtils.checkTrue(GenericUtils.isEmpty(unsupported), "Unsupported signature factories found: %s", unsupported);
-        setSignatureFactories(factories);
-    }
-
-    /**
-     * Attempts to use the primary manager's signature factories if not {@code null}/empty,
-     * otherwise uses the secondary ones (regardless of whether there are any...)
-     *
-     * @param primary The primary {@link SignatureFactoriesManager}
-     * @param secondary The secondary {@link SignatureFactoriesManager}
-     * @return The resolved signature factories - may be {@code null}/empty
-     * @see #getSignatureFactories(SignatureFactoriesManager)
-     */
-    static List<NamedFactory<Signature>> resolveSignatureFactories(
-            SignatureFactoriesManager primary, SignatureFactoriesManager secondary) {
-        List<NamedFactory<Signature>> factories = getSignatureFactories(primary);
-        return GenericUtils.isEmpty(factories) ? getSignatureFactories(secondary) : factories;
-    }
-
-    /**
-     * @param manager The {@link SignatureFactoriesManager} instance - ignored if {@code null}
-     * @return The associated list of named <code>Signature</code> factories or {@code null} if
-     * no manager instance
-     */
-    static List<NamedFactory<Signature>> getSignatureFactories(SignatureFactoriesManager manager) {
-        return (manager == null) ? null : manager.getSignatureFactories();
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/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
deleted file mode 100644
index 0881714..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureFactory.java
+++ /dev/null
@@ -1,31 +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.signature;
-
-import org.apache.sshd.common.BuiltinFactory;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-// CHECKSTYLE:OFF
-public interface SignatureFactory extends BuiltinFactory<Signature> {
-    // nothing extra
-}
-//CHECKSTYLE:ON

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureRSA.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureRSA.java b/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureRSA.java
deleted file mode 100644
index ad1d37e..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureRSA.java
+++ /dev/null
@@ -1,89 +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.signature;
-
-import java.math.BigInteger;
-import java.security.PublicKey;
-import java.security.interfaces.RSAKey;
-import java.util.Map;
-
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.ValidateUtils;
-
-/**
- * RSA <code>Signature</code>
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- * @see <A HREF="https://tools.ietf.org/html/rfc4253#section-6.6">RFC4253 section 6.6</A>
- */
-public class SignatureRSA extends AbstractSignature {
-    public static final String DEFAULT_ALGORITHM = "SHA1withRSA";
-
-    private int verifierSignatureSize = -1;
-
-    public SignatureRSA() {
-        super(DEFAULT_ALGORITHM);
-    }
-
-    protected SignatureRSA(String algorithm) {
-        super(algorithm);
-    }
-
-    /**
-     * @return The expected number of bytes in the signature - non-positive
-     * if not initialized or not intended to be used for verification
-     */
-    protected int getVerifierSignatureSize() {
-        return verifierSignatureSize;
-    }
-
-    @Override
-    public void initVerifier(PublicKey key) throws Exception {
-        super.initVerifier(key);
-        RSAKey rsaKey = ValidateUtils.checkInstanceOf(key, RSAKey.class, "Not an RSA key");
-        verifierSignatureSize = getVerifierSignatureSize(rsaKey);
-    }
-
-    public static int getVerifierSignatureSize(RSAKey key) {
-        BigInteger modulus = key.getModulus();
-        return (modulus.bitLength() + Byte.SIZE - 1) / Byte.SIZE;
-    }
-
-    @Override
-    public boolean verify(byte[] sig) throws Exception {
-        byte[] data = sig;
-        Map.Entry<String, byte[]> encoding = extractEncodedSignature(data);
-        if (encoding != null) {
-            String keyType = encoding.getKey();
-            ValidateUtils.checkTrue(KeyPairProvider.SSH_RSA.equals(keyType), "Mismatched key type: %s", keyType);
-            data = encoding.getValue();
-        }
-
-        int expectedSize = getVerifierSignatureSize();
-        ValidateUtils.checkTrue(expectedSize > 0, "Signature verification size has not been initialized");
-        // Pad with zero if value is trimmed
-        if (data.length < expectedSize) {
-            byte[] pad = new byte[expectedSize];
-            System.arraycopy(data, 0, pad, pad.length - data.length, data.length);
-            data = pad;
-        }
-
-        return doVerify(data);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/signature/package.html
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/signature/package.html b/sshd-core/src/main/java/org/apache/sshd/common/signature/package.html
deleted file mode 100644
index d1b16e4..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/signature/package.html
+++ /dev/null
@@ -1,25 +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.
--->
-<html>
-<head>
-</head>
-<body>
-
-<a href="{@docRoot}/org/apache/sshd/common/signature/Signature.html"><code>Signature</code></a> implementations.
-
-</body>
-</html>

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/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
deleted file mode 100644
index 1620bad..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/EventListenerUtils.java
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sshd.common.util;
-
-import java.lang.reflect.Proxy;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.EventListener;
-import java.util.Objects;
-import java.util.Set;
-import java.util.TreeSet;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public final class EventListenerUtils {
-    /**
-     * A special &quot;comparator&quot; whose only purpose is to ensure
-     * there are no same references in a listener's set - to be used
-     * in conjunction with a {@code TreeSet} as its comparator
-     */
-    @SuppressWarnings("checkstyle:anoninnerlength")
-    public static final Comparator<EventListener> LISTENER_INSTANCE_COMPARATOR = (l1, l2) -> {
-        if (l1 == l2) {
-            return 0;
-        } else if (l1 == null) {
-            return 1;
-        } else if (l2 == null) {
-            return -1;
-        }
-
-        Class<?> c1 = l1.getClass();
-        Class<?> c2 = l2.getClass();
-        boolean checkHashCodes = true;
-        if (Proxy.isProxyClass(c1)) {
-            if (Proxy.isProxyClass(c2)) {
-                checkHashCodes = false; // cannot call hashCode on a proxy
-            } else {
-                return 1;
-            }
-        } else if (Proxy.isProxyClass(c2)) {
-            return -1;
-        }
-
-        if (checkHashCodes) {
-            int nRes = Integer.compare(l1.hashCode(), l2.hashCode());
-            if (nRes != 0) {
-                return nRes;
-            }
-        }
-
-        int nRes = Integer.compare(System.identityHashCode(l1), System.identityHashCode(l2));
-        if (nRes != 0) {
-            return nRes;
-        }
-
-        if (c1 != c2) {
-            return c1.getName().compareTo(c2.getName());
-        }
-
-        String s1 = Objects.toString(l1.toString(), "");
-        String s2 = Objects.toString(l2.toString(), "");
-        nRes = s1.compareTo(s2);
-        if (nRes != 0) {
-            return nRes;
-        }
-        throw new UnsupportedOperationException("Ran out of options to compare instance of " + s1 + " vs. " + s2);
-    };
-
-    private EventListenerUtils() {
-        throw new UnsupportedOperationException("No instance");
-    }
-
-    /**
-     * @param <L> Type of {@link SshdEventListener} contained in the set
-     * @param listeners The listeners to pre-add to the create set - ignored
-     * if (@code null}/empty
-     * @return A (synchronized) {@link Set} for containing the listeners ensuring
-     * that if same listener instance is added repeatedly only <U>one</U>
-     * instance is actually contained
-     */
-    public static <L extends SshdEventListener> Set<L> synchronizedListenersSet(Collection<? extends L> listeners) {
-        Set<L> s = EventListenerUtils.synchronizedListenersSet();
-        if (GenericUtils.size(listeners) > 0) {
-            s.addAll(listeners);
-        }
-
-        return s;
-    }
-
-    /**
-     * @param <L> Type of {@link SshdEventListener} contained in the set
-     * @return A (synchronized) {@link Set} for containing the listeners ensuring
-     * that if same listener instance is added repeatedly only <U>one</U>
-     * instance is actually contained
-     * @see #LISTENER_INSTANCE_COMPARATOR
-     */
-    public static <L extends SshdEventListener> Set<L> synchronizedListenersSet() {
-        return Collections.synchronizedSet(new TreeSet<L>(LISTENER_INSTANCE_COMPARATOR));
-    }
-
-    /**
-     * Provides proxy wrapper around an {@link Iterable} container of listener
-     * interface implementation. <b>Note:</b> a listener interface is one whose
-     * invoked methods return <u>only</u> {@code void}.
-     *
-     * @param <T>          Generic listener type
-     * @param listenerType The expected listener <u>interface</u>
-     * @param listeners    An {@link Iterable} container of listeners to be invoked.
-     *                     <p>
-     *                     <b>Note(s):</b>
-     *                     </p>
-     *                     <ul>
-     *                     <li><p>
-     *                     The invocation order is same as the {@link Iterable} container
-     *                     </p></li>
-     *
-     *                     <li><p>
-     *                     If any of the invoked listener methods throws an exception, the
-     *                     rest of the listener are <u>not</u> invoked and the exception is
-     *                     propagated to the caller
-     *                     </p></li>
-     *
-     *                     <li><p>
-     *                     It is up to the <u>caller</u> to ensure that the container does
-     *                     not change while the proxy is invoked
-     *                     </p></li>
-     *                     </ul>
-     * @return A proxy wrapper implementing the same interface, but delegating
-     * the calls to the container
-     * @see #proxyWrapper(Class, ClassLoader, Iterable)
-     */
-    public static <T extends SshdEventListener> T proxyWrapper(Class<T> listenerType, Iterable<? extends T> listeners) {
-        return proxyWrapper(listenerType, listenerType.getClassLoader(), listeners);
-    }
-
-    /**
-     * Provides proxy wrapper around an {@link Iterable} container of listener
-     * interface implementation. <b>Note:</b> a listener interface is one whose
-     * invoked methods return <u>only</u> {@code void}.
-     *
-     * @param <T>          Generic {@link SshdEventListener} type
-     * @param listenerType The expected listener <u>interface</u>
-     * @param loader       The {@link ClassLoader} to use for the proxy
-     * @param listeners    An {@link Iterable} container of listeners to be invoked.
-     *                     <p>
-     *                     <b>Note(s):</b>
-     *                     </p>
-     *                     <ul>
-     *                     <li><p>
-     *                     The invocation order is same as the {@link Iterable} container
-     *                     </p></li>
-     *
-     *                     <li><p>
-     *                     If any of the invoked listener methods throws an exception, the
-     *                     rest of the listener are <u>not</u> invoked and the exception is
-     *                     propagated to the caller
-     *                     </p></li>
-     *
-     *                     <li><p>
-     *                     It is up to the <u>caller</u> to ensure that the container does
-     *                     not change while the proxy is invoked
-     *                     </p></li>
-     *                     </ul>
-     * @return A proxy wrapper implementing the same interface, but delegating
-     * the calls to the container
-     * @throws IllegalArgumentException if <tt>listenerType</tt> is not an interface
-     *                                  or a {@code null} container has been provided
-     * @see #proxyWrapper(Class, ClassLoader, Iterable)
-     */
-    public static <T extends SshdEventListener> T proxyWrapper(Class<T> listenerType, ClassLoader loader, final Iterable<? extends T> listeners) {
-        Objects.requireNonNull(listenerType, "No listener type specified");
-        ValidateUtils.checkTrue(listenerType.isInterface(), "Target proxy is not an interface: %s", listenerType.getSimpleName());
-        Objects.requireNonNull(listeners, "No listeners container provided");
-
-        Object wrapper = Proxy.newProxyInstance(loader, new Class<?>[]{listenerType}, (proxy, method, args) -> {
-            Throwable err = null;
-            for (T l : listeners) {
-                try {
-                    method.invoke(l, args);
-                } catch (Throwable t) {
-                    Throwable e = GenericUtils.peelException(t);
-                    err = GenericUtils.accumulateException(err, e);
-                }
-            }
-
-            if (err != null) {
-                throw err;
-            }
-
-            return null;    // we assume always void return value...
-        });
-        return listenerType.cast(wrapper);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/EventNotifier.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/EventNotifier.java b/sshd-core/src/main/java/org/apache/sshd/common/util/EventNotifier.java
deleted file mode 100644
index c041f1b..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/EventNotifier.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.common.util;
-
-/**
- * Notify about the occurrence of an event
- *
- * @param <E> type of event being notified
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FunctionalInterface
-public interface EventNotifier<E> {
-    /**
-     * @param event The event
-     * @throws Exception If failed to process the event notification
-     */
-    void notifyEvent(E event) throws Exception;
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/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
deleted file mode 100644
index c924d1e..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/GenericUtils.java
+++ /dev/null
@@ -1,915 +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.IOException;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.UndeclaredThrowableException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.EnumSet;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.NavigableMap;
-import java.util.NavigableSet;
-import java.util.Objects;
-import java.util.Set;
-import java.util.TreeMap;
-import java.util.TreeSet;
-import java.util.concurrent.ExecutionException;
-import java.util.function.BinaryOperator;
-import java.util.function.Consumer;
-import java.util.function.Function;
-import java.util.function.Predicate;
-import java.util.function.Supplier;
-import java.util.stream.Collector;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-import java.util.stream.StreamSupport;
-
-import javax.management.MBeanException;
-import javax.management.ReflectionException;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public final class GenericUtils {
-
-    public static final byte[] EMPTY_BYTE_ARRAY = {};
-    public static final char[] EMPTY_CHAR_ARRAY = {};
-    public static final String[] EMPTY_STRING_ARRAY = {};
-    public static final Object[] EMPTY_OBJECT_ARRAY = {};
-
-    /**
-     * A value indicating a {@code null} value - to be used as a placeholder
-     * where {@code null}s are not allowed
-     */
-    public static final Object NULL = new Object();
-
-    /**
-     * The complement of {@link String#CASE_INSENSITIVE_ORDER}
-     */
-    public static final Comparator<String> CASE_SENSITIVE_ORDER = (s1, s2) -> {
-        if (s1 == s2) {
-            return 0;
-        } else {
-            return s1.compareTo(s2);
-        }
-    };
-
-    public static final String QUOTES = "\"'";
-
-    @SuppressWarnings("rawtypes")
-    private static final Supplier CASE_INSENSITIVE_MAP_FACTORY = () -> new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
-
-    private GenericUtils() {
-        throw new UnsupportedOperationException("No instance");
-    }
-
-    public static String trimToEmpty(String s) {
-        if (s == null) {
-            return "";
-        } else {
-            return s.trim();
-        }
-    }
-
-    public static String replaceWhitespaceAndTrim(String s) {
-        if (s != null) {
-            s = s.replace('\t', ' ');
-        }
-
-        return trimToEmpty(s);
-    }
-
-    /**
-     * @param s The {@link String} value to calculate the hash code on - may
-     * be {@code null}/empty in which case a value of zero is returned
-     * @return The calculated hash code
-     * @see #hashCode(String, Boolean)
-     */
-    public static int hashCode(String s) {
-        return hashCode(s, null);
-    }
-
-    /**
-     * @param s The {@link String} value to calculate the hash code on - may
-     * be {@code null}/empty in which case a value of zero is returned
-     * @param useUppercase Whether to convert the string to uppercase, lowercase
-     * or not at all:
-     * <UL>
-     *      <LI>{@code null} - no conversion</LI>
-     *      <LI>{@link Boolean#TRUE} - get hash code of uppercase</LI>
-     *      <LI>{@link Boolean#FALSE} - get hash code of lowercase</LI>
-     * </UL>
-     * @return The calculated hash code
-     */
-    public static int hashCode(String s, Boolean useUppercase) {
-        if (isEmpty(s)) {
-            return 0;
-        } else if (useUppercase == null) {
-            return s.hashCode();
-        } else if (useUppercase.booleanValue()) {
-            return s.toUpperCase().hashCode();
-        } else {
-            return s.toLowerCase().hashCode();
-        }
-    }
-
-    public static int safeCompare(String s1, String s2, boolean caseSensitive) {
-        if (isSameReference(s1, s2)) {
-            return 0;
-        } else if (s1 == null) {
-            return +1;    // push null(s) to end
-        } else if (s2 == null) {
-            return -1;    // push null(s) to end
-        } else if (caseSensitive) {
-            return s1.compareTo(s2);
-        } else {
-            return s1.compareToIgnoreCase(s2);
-        }
-    }
-
-    public static <T> boolean isSameReference(T o1, T o2) {
-        return o1 == o2;
-    }
-
-    public static int length(CharSequence cs) {
-        return cs == null ? 0 : cs.length();
-    }
-
-    public static boolean isEmpty(CharSequence cs) {
-        return length(cs) <= 0;
-    }
-
-    public static boolean isNotEmpty(CharSequence cs) {
-        return !isEmpty(cs);
-    }
-
-    public static int indexOf(CharSequence cs, char c) {
-        int len = length(cs);
-        for (int pos = 0; pos < len; pos++) {
-            char ch = cs.charAt(pos);
-            if (ch == c) {
-                return pos;
-            }
-        }
-
-        return -1;
-    }
-
-    public static int lastIndexOf(CharSequence cs, char c) {
-        int len = length(cs);
-        for (int pos = len - 1; pos >= 0; pos--) {
-            char ch = cs.charAt(pos);
-            if (ch == c) {
-                return pos;
-            }
-        }
-
-        return -1;
-    }
-
-    // a List would be better, but we want to be compatible with String.split(...)
-    public static String[] split(String s, char ch) {
-        if (isEmpty(s)) {
-            return EMPTY_STRING_ARRAY;
-        }
-
-        int lastPos = 0;
-        int curPos = s.indexOf(ch);
-        if (curPos < 0) {
-            return new String[]{s};
-        }
-
-        Collection<String> values = new LinkedList<>();
-        do {
-            String v = s.substring(lastPos, curPos);
-            values.add(v);
-
-            // skip separator
-            lastPos = curPos + 1;
-            if (lastPos >= s.length()) {
-                break;
-            }
-
-            curPos = s.indexOf(ch, lastPos);
-            if (curPos < lastPos) {
-                break;  // no more separators
-            }
-        } while (curPos < s.length());
-
-        // check if any leftovers
-        if (lastPos < s.length()) {
-            String v = s.substring(lastPos);
-            values.add(v);
-        }
-
-        return values.toArray(new String[values.size()]);
-    }
-
-    public static <T> String join(T[] values, char ch) {
-        return join(isEmpty(values) ? Collections.<T>emptyList() : Arrays.asList(values), ch);
-    }
-
-    public static String join(Iterable<?> iter, char ch) {
-        return join((iter == null) ? null : iter.iterator(), ch);
-    }
-
-    public static String join(Iterator<?> iter, char ch) {
-        if ((iter == null) || (!iter.hasNext())) {
-            return "";
-        }
-
-        StringBuilder sb = new StringBuilder();
-        do {    // we already asked hasNext...
-            Object o = iter.next();
-            if (sb.length() > 0) {
-                sb.append(ch);
-            }
-            sb.append(Objects.toString(o));
-        } while (iter.hasNext());
-
-        return sb.toString();
-    }
-
-    public static <T> String join(T[] values, CharSequence sep) {
-        return join(isEmpty(values) ? Collections.<T>emptyList() : Arrays.asList(values), sep);
-    }
-
-    public static String join(Iterable<?> iter, CharSequence sep) {
-        return join((iter == null) ? null : iter.iterator(), sep);
-    }
-
-    public static String join(Iterator<?> iter, CharSequence sep) {
-        if ((iter == null) || (!iter.hasNext())) {
-            return "";
-        }
-
-        StringBuilder sb = new StringBuilder();
-        do {    // we already asked hasNext...
-            Object o = iter.next();
-            if (sb.length() > 0) {
-                sb.append(sep);
-            }
-            sb.append(Objects.toString(o));
-        } while (iter.hasNext());
-
-        return sb.toString();
-    }
-
-    public static int size(Collection<?> c) {
-        return c == null ? 0 : c.size();
-    }
-
-    public static boolean isEmpty(Collection<?> c) {
-        return (c == null) || c.isEmpty();
-    }
-
-    public static boolean isNotEmpty(Collection<?> c) {
-        return !isEmpty(c);
-    }
-
-    public static int size(Map<?, ?> m) {
-        return m == null ? 0 : m.size();
-    }
-
-    public static boolean isEmpty(Map<?, ?> m) {
-        return (m == null) || m.isEmpty();
-    }
-
-    public static boolean isNotEmpty(Map<?, ?> m) {
-        return !isEmpty(m);
-    }
-
-    @SafeVarargs
-    public static <T> int length(T... a) {
-        return a == null ? 0 : a.length;
-    }
-
-    public static <T> boolean isEmpty(Iterable<? extends T> iter) {
-        if (iter == null) {
-            return true;
-        } else if (iter instanceof Collection<?>) {
-            return isEmpty((Collection<?>) iter);
-        } else {
-            return isEmpty(iter.iterator());
-        }
-    }
-
-    public static <T> boolean isNotEmpty(Iterable<? extends T> iter) {
-        return !isEmpty(iter);
-    }
-
-    public static <T> boolean isEmpty(Iterator<? extends T> iter) {
-        return iter == null || !iter.hasNext();
-    }
-
-    public static <T> boolean isNotEmpty(Iterator<? extends T> iter) {
-        return !isEmpty(iter);
-    }
-
-    @SafeVarargs
-    public static <T> boolean isEmpty(T... a) {
-        return length(a) <= 0;
-    }
-
-    public static int length(char[] chars) {
-        return (chars == null) ? 0 : chars.length;
-    }
-
-    public static boolean isEmpty(char[] chars) {
-        return length(chars) <= 0;
-    }
-
-    /**
-     * Compares 2 character arrays - <B>Note:</B> {@code null} and empty
-     * are considered <U>equal</U>
-     *
-     * @param c1 1st array
-     * @param c2 2nd array
-     * @return Negative is 1st array comes first in lexicographical order,
-     * positive if 2nd array comes first and zero if equal
-     */
-    public static int compare(char[] c1, char[] c2) {
-        int l1 = length(c1);
-        int l2 = length(c2);
-        int cmpLen = Math.min(l1, l2);
-        for (int index = 0; index < cmpLen; index++) {
-            char c11 = c1[index];
-            char c22 = c2[index];
-            int nRes = Character.compare(c11, c22);
-            if (nRes != 0) {
-                return nRes;
-            }
-        }
-
-        int nRes = Integer.compare(l1, l2);
-        if (nRes != 0) {
-            return nRes;
-        }
-
-        return 0;
-    }
-
-    @SafeVarargs    // there is no EnumSet.of(...) so we have to provide our own
-    public static <E extends Enum<E>> Set<E> of(E... values) {
-        return of(isEmpty(values) ? Collections.emptySet() : Arrays.asList(values));
-    }
-
-    public static <E extends Enum<E>> Set<E> of(Collection<? extends E> values) {
-        if (isEmpty(values)) {
-            return Collections.emptySet();
-        }
-
-        Set<E> result = null;
-        for (E v : values) {
-            /*
-             * A trick to compensate for the fact that we do not have
-             * the enum Class to invoke EnumSet.noneOf
-             */
-            if (result == null) {
-                result = EnumSet.of(v);
-            } else {
-                result.add(v);
-            }
-        }
-
-        return result;
-    }
-
-    public static <T> boolean containsAny(Collection<? extends T> coll, Iterable<? extends T> values) {
-        if (isEmpty(coll)) {
-            return false;
-        }
-
-        for (T v : values) {
-            if (coll.contains(v)) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-    public static <T> void forEach(Iterable<T> values, Consumer<T> consumer) {
-        if (isNotEmpty(values)) {
-            values.forEach(consumer);
-        }
-    }
-
-    public static <T, U> List<U> map(Collection<T> values, Function<? super T, ? extends U> mapper) {
-        return stream(values).map(mapper).collect(Collectors.toList());
-    }
-
-    public static <T, U> NavigableSet<U> mapSort(
-            Collection<T> values, Function<? super T, ? extends U> mapper, Comparator<U> comparator) {
-        return stream(values).map(mapper).collect(toSortedSet(comparator));
-    }
-
-    public static <T, K, U> NavigableMap<K, U> toSortedMap(
-            Iterable<T> values, Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper, Comparator<K> comparator) {
-        return stream(values).collect(toSortedMap(keyMapper, valueMapper, comparator));
-    }
-
-    public static <T, K, U> Collector<T, ?, NavigableMap<K, U>> toSortedMap(
-            Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper, Comparator<K> comparator) {
-        return Collectors.toMap(keyMapper, valueMapper, throwingMerger(), () -> new TreeMap<>(comparator));
-    }
-
-    private static <T> BinaryOperator<T> throwingMerger() {
-        return (u, v) -> {
-            throw new IllegalStateException(String.format("Duplicate key %s", u));
-        };
-    }
-
-    public static <T> Collector<T, ?, NavigableSet<T>> toSortedSet(Comparator<T> comparator) {
-        return Collectors.toCollection(() -> new TreeSet<>(comparator));
-    }
-
-    public static <T> Stream<T> stream(Iterable<T> values) {
-        if (isEmpty(values)) {
-            return Stream.empty();
-        } else if (values instanceof Collection<?>) {
-            return ((Collection<T>) values).stream();
-        } else {
-            return StreamSupport.stream(values.spliterator(), false);
-        }
-    }
-
-    @SafeVarargs
-    public static <T> List<T> unmodifiableList(T... values) {
-        return unmodifiableList(asList(values));
-    }
-
-    public static <T> List<T> unmodifiableList(Collection<? extends T> values) {
-        if (isEmpty(values)) {
-            return Collections.emptyList();
-        } else {
-            return Collections.unmodifiableList(new ArrayList<>(values));
-        }
-    }
-
-    public static <T> List<T> unmodifiableList(Stream<T> values) {
-        return unmodifiableList(values.collect(Collectors.toList()));
-    }
-
-    @SafeVarargs
-    public static <T> List<T> asList(T... values) {
-        return isEmpty(values) ? Collections.emptyList() : Arrays.asList(values);
-    }
-
-    @SafeVarargs
-    public static <T> Set<T> asSet(T... values) {
-        return new HashSet<>(asList(values));
-    }
-
-    @SafeVarargs
-    public static <V extends Comparable<V>> NavigableSet<V> asSortedSet(V... values) {
-        return asSortedSet(Comparator.naturalOrder(), values);
-    }
-
-    public static <V extends Comparable<V>> NavigableSet<V> asSortedSet(Collection<? extends V> values) {
-        return asSortedSet(Comparator.naturalOrder(), values);
-    }
-
-    /**
-     * @param <V>    The element type
-     * @param comp   The (non-{@code null}) {@link Comparator} to use
-     * @param values The values to be added (ignored if {@code null})
-     * @return A {@link NavigableSet} containing the values (if any) sorted
-     * using the provided comparator
-     */
-    @SafeVarargs
-    public static <V> NavigableSet<V> asSortedSet(Comparator<? super V> comp, V... values) {
-        return asSortedSet(comp, isEmpty(values) ? Collections.emptyList() : Arrays.asList(values));
-    }
-
-    /**
-     * @param <V>    The element type
-     * @param comp   The (non-{@code null}) {@link Comparator} to use
-     * @param values The values to be added (ignored if {@code null}/empty)
-     * @return A {@link NavigableSet} containing the values (if any) sorted
-     * using the provided comparator
-     */
-    public static <V> NavigableSet<V> asSortedSet(Comparator<? super V> comp, Collection<? extends V> values) {
-        NavigableSet<V> set = new TreeSet<>(Objects.requireNonNull(comp, "No comparator"));
-        if (size(values) > 0) {
-            set.addAll(values);
-        }
-        return set;
-    }
-
-    /**
-     * @param <V> Type of mapped value
-     * @return A {@link Supplier} that returns a <U>new</U> {@link NavigableMap}
-     * whenever its {@code get()} method is invoked
-     */
-    @SuppressWarnings("unchecked")
-    public static <V> Supplier<NavigableMap<String, V>> caseInsensitiveMap() {
-        return CASE_INSENSITIVE_MAP_FACTORY;
-    }
-
-    /**
-     * Flips between keys and values of an input map
-     *
-     * @param <K> Original map key type
-     * @param <V> Original map value type
-     * @param <M> Flipped map type
-     * @param map The original map to flip
-     * @param mapCreator The creator of the target map
-     * @param allowDuplicates Whether to ignore duplicates on flip
-     * @return The flipped map result
-     * @throws IllegalArgumentException if <tt>allowDuplicates</tt> is {@code false}
-     * and a duplicate value found in the original map.
-     */
-    public static <K, V, M extends Map<V, K>> M flipMap(
-            Map<? extends K, ? extends V> map, Supplier<? extends M> mapCreator, boolean allowDuplicates) {
-        M result = Objects.requireNonNull(mapCreator.get(), "No map created");
-        map.forEach((key, value) -> {
-            K prev = result.put(value, key);
-            if ((prev != null) && (!allowDuplicates)) {
-                ValidateUtils.throwIllegalArgumentException("Multiple values for key=%s: current=%s, previous=%s", value, key, prev);
-            }
-        });
-
-        return result;
-    }
-
-    @SafeVarargs
-    public static <K, V, M extends Map<K, V>> M mapValues(
-            Function<? super V, ? extends K> keyMapper, Supplier<? extends M> mapCreator, V... values) {
-        return mapValues(keyMapper, mapCreator, isEmpty(values) ? Collections.emptyList() : Arrays.asList(values));
-    }
-
-    /**
-     * Creates a map out of a group of values
-     *
-     * @param <K> The key type
-     * @param <V> The value type
-     * @param <M> The result {@link Map} type
-     * @param keyMapper The {@link Function} that generates a key for a given value.
-     * If the returned key is {@code null} then the value is not mapped
-     * @param mapCreator The {@link Supplier} used to create/retrieve the result map - provided
-     * non-empty group of values
-     * @param values The values to be mapped
-     * @return The resulting {@link Map} - <B>Note:</B> no validation is made to ensure
-     * that 2 (or more) values are not mapped to the same key
-     */
-    public static <K, V, M extends Map<K, V>> M mapValues(
-            Function<? super V, ? extends K> keyMapper, Supplier<? extends M> mapCreator, Collection<? extends V> values) {
-        M map = mapCreator.get();
-        for (V v : values) {
-            K k = keyMapper.apply(v);
-            if (k == null) {
-                continue;   // debug breakpoint
-            }
-            map.put(k, v);
-        }
-
-        return map;
-    }
-
-    @SafeVarargs
-    public static <T> T findFirstMatchingMember(Predicate<? super T> acceptor, T... values) {
-        return findFirstMatchingMember(acceptor, isEmpty(values) ? Collections.emptyList() : Arrays.asList(values));
-    }
-
-    public static <T> T findFirstMatchingMember(Predicate<? super T> acceptor, Collection<? extends T> values) {
-        List<T> matches = selectMatchingMembers(acceptor, values);
-        return GenericUtils.isEmpty(matches) ? null : matches.get(0);
-    }
-
-    /**
-     * Returns a list of all the values that were accepted by a predicate
-     *
-     * @param <T> The type of value being evaluated
-     * @param acceptor The {@link Predicate} to consult whether a member is selected
-     * @param values The values to be scanned
-     * @return A {@link List} of all the values that were accepted by the predicate
-     */
-    @SafeVarargs
-    public static <T> List<T> selectMatchingMembers(Predicate<? super T> acceptor, T... values) {
-        return selectMatchingMembers(acceptor, isEmpty(values) ? Collections.emptyList() : Arrays.asList(values));
-    }
-
-    /**
-     * Returns a list of all the values that were accepted by a predicate
-     *
-     * @param <T> The type of value being evaluated
-     * @param acceptor The {@link Predicate} to consult whether a member is selected
-     * @param values The values to be scanned
-     * @return A {@link List} of all the values that were accepted by the predicate
-     */
-    public static <T> List<T> selectMatchingMembers(Predicate<? super T> acceptor, Collection<? extends T> values) {
-        return GenericUtils.stream(values)
-                .filter(acceptor)
-                .collect(Collectors.toList());
-    }
-
-    /**
-     * @param s The {@link CharSequence} to be checked
-     * @return If the sequence contains any of the {@link #QUOTES}
-     * on <U>both</U> ends, then they are stripped, otherwise
-     * nothing is done
-     * @see #stripDelimiters(CharSequence, char)
-     */
-    public static CharSequence stripQuotes(CharSequence s) {
-        if (isEmpty(s)) {
-            return s;
-        }
-
-        for (int index = 0; index < QUOTES.length(); index++) {
-            char delim = QUOTES.charAt(index);
-            CharSequence v = stripDelimiters(s, delim);
-            if (v != s) {   // if stripped one don't continue
-                return v;
-            }
-        }
-
-        return s;
-    }
-
-    /**
-     * @param s     The {@link CharSequence} to be checked
-     * @param delim The expected delimiter
-     * @return If the sequence contains the delimiter on <U>both</U> ends,
-     * then it is are stripped, otherwise nothing is done
-     */
-    public static CharSequence stripDelimiters(CharSequence s, char delim) {
-        if (isEmpty(s) || (s.length() < 2)) {
-            return s;
-        }
-
-        int lastPos = s.length() - 1;
-        if ((s.charAt(0) != delim) || (s.charAt(lastPos) != delim)) {
-            return s;
-        } else {
-            return s.subSequence(1, lastPos);
-        }
-    }
-
-    public static RuntimeException toRuntimeException(Throwable t) {
-        return toRuntimeException(t, true);
-    }
-
-    /**
-     * Converts a thrown generic exception to a {@link RuntimeException}
-     *
-     * @param t The original thrown exception
-     * @param peelThrowable Whether to determine the root cause by &quot;peeling&quot;
-     * any enclosing exceptions
-     * @return The thrown cause if already a runtime exception, otherwise a
-     * runtime exception of the resolved exception as its cause
-     * @see #peelException(Throwable)
-     */
-    public static RuntimeException toRuntimeException(Throwable t, boolean peelThrowable) {
-        Throwable e = peelThrowable ? peelException(t) : t;
-        if (e instanceof RuntimeException) {
-            return (RuntimeException) e;
-        }
-
-        return new RuntimeException(e);
-    }
-
-    /**
-     * Attempts to get to the &quot;effective&quot; exception being thrown,
-     * by taking care of some known exceptions that wrap the original thrown
-     * one.
-     *
-     * @param t The original {@link Throwable} - ignored if {@code null}
-     * @return The effective exception - same as input if not a wrapper
-     */
-    public static Throwable peelException(Throwable t) {
-        // NOTE: check order is important - e.g., InvocationTargetException extends ReflectiveOperationException
-        if (t == null) {
-            return t;
-        } else if (t instanceof UndeclaredThrowableException) {
-            Throwable wrapped = ((UndeclaredThrowableException) t).getUndeclaredThrowable();
-            // according to the Javadoc it may be null, in which case 'getCause'
-            // might contain the information we need
-            if (wrapped != null) {
-                return peelException(wrapped);
-            }
-
-            wrapped = t.getCause();
-            if (wrapped != t) {     // make sure it is a real cause
-                return peelException(wrapped);
-            }
-        } else if (t instanceof InvocationTargetException) {
-            Throwable target = ((InvocationTargetException) t).getTargetException();
-            if (target != null) {
-                return peelException(target);
-            }
-        } else if (t instanceof ReflectionException) {
-            Throwable target = ((ReflectionException) t).getTargetException();
-            if (target != null) {
-                return peelException(target);
-            }
-        } else if (t instanceof ExecutionException) {
-            Throwable wrapped = resolveExceptionCause(t);
-            if (wrapped != null) {
-                return peelException(wrapped);
-            }
-        } else if (t instanceof MBeanException) {
-            Throwable target = ((MBeanException) t).getTargetException();
-            if (target != null) {
-                return peelException(target);
-            }
-        }
-
-        return t;   // no special handling required or available
-    }
-
-    /**
-     * @param t The original {@link Throwable} - ignored if {@code null}
-     * @return If {@link Throwable#getCause()} is non-{@code null} then
-     * the cause, otherwise the original exception - {@code null} if
-     * the original exception was {@code null}
-     */
-    public static Throwable resolveExceptionCause(Throwable t) {
-        if (t == null) {
-            return t;
-        }
-
-        Throwable c = t.getCause();
-        if (c == null) {
-            return t;
-        } else {
-            return c;
-        }
-    }
-
-    /**
-     * 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 <T>     The exception type
-     * @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;
-    }
-
-    public static IOException toIOException(Throwable e) {
-        if (e instanceof IOException) {
-            return (IOException) e;
-        } else {
-            return new IOException(e);
-        }
-    }
-
-    /**
-     * Wraps a value into a {@link Supplier}
-     * @param <T> Type of value being supplied
-     * @param value The value to be supplied
-     * @return The supplier wrapper
-     */
-    public static <T> Supplier<T> supplierOf(T value) {
-        return () -> value;
-    }
-
-    /**
-     * Resolves to an always non-{@code null} iterator
-     *
-     * @param <T> Type of value being iterated
-     * @param iterable The {@link Iterable} instance
-     * @return A non-{@code null} iterator which may be empty if no iterable
-     * instance or no iterator returned from it
-     * @see #iteratorOf(Iterator)
-     */
-    public static <T> Iterator<T> iteratorOf(Iterable<T> iterable) {
-        return iteratorOf((iterable == null) ? null : iterable.iterator());
-    }
-
-    /**
-     * @param <B> Generic base class
-     * @param <D> Generic child class
-     * @return An identity {@link Function} that returns its input child class as a base class
-     */
-    public static <B, D extends B> Function<D, B> downcast() {
-        return t -> t;
-    }
-
-    /**
-     * Resolves to an always non-{@code null} iterator
-     *
-     * @param <T> Type of value being iterated
-     * @param iter The {@link Iterator} instance
-     * @return  A non-{@code null} iterator which may be empty if no iterator instance
-     * @see Collections#emptyIterator()
-     */
-    public static <T> Iterator<T> iteratorOf(Iterator<T> iter) {
-        return (iter == null) ? Collections.emptyIterator() : iter;
-    }
-
-    public static <U, V> Iterable<V> wrapIterable(Iterable<? extends U> iter, Function<? super U, ? extends V> mapper) {
-        return () -> wrapIterator(iter, mapper);
-    }
-
-    @SuppressWarnings({ "unchecked", "rawtypes" })
-    public static <U, V> Iterator<V> wrapIterator(Iterable<? extends U> iter, Function<? super U, ? extends V> mapper) {
-        return (Iterator) stream(iter).map(mapper).iterator();
-    }
-
-    public static <U, V> Iterator<V> wrapIterator(Iterator<? extends U> iter, Function<? super U, ? extends V> mapper) {
-        Iterator<? extends U> iterator = iteratorOf(iter);
-        return new Iterator<V>() {
-            @Override
-            public boolean hasNext() {
-                return iterator.hasNext();
-            }
-
-            @Override
-            public V next() {
-                U value = iterator.next();
-                return mapper.apply(value);
-            }
-        };
-    }
-
-    /**
-     * Wraps a group of {@link Supplier}s of {@link Iterable} instances into a &quot;unified&quot;
-     * {@link Iterable} of their values, in the same order as the suppliers - i.e., once the values
-     * from a specific supplier are exhausted, the next one is consulted, and so on, until all
-     * suppliers have been consulted
-     *
-     * @param <T> Type of value being iterated
-     * @param providers The providers - ignored if {@code null} (i.e., return an empty iterable instance)
-     * @return The wrapping instance
-     */
-    public static <T> Iterable<T> multiIterableSuppliers(Iterable<? extends Supplier<? extends Iterable<? extends T>>> providers) {
-        return () -> stream(providers).<T>flatMap(s -> stream(s.get())).map(Function.identity()).iterator();
-    }
-
-    public static <K, V> MapBuilder<K, V> mapBuilder() {
-        return new MapBuilder<>();
-    }
-
-    public static <K, V> MapBuilder<K, V> mapBuilder(Comparator<K> comparator) {
-        return new MapBuilder<>(comparator);
-    }
-
-    public static class MapBuilder<K, V> {
-        private Map<K, V> map;
-
-        public MapBuilder() {
-            this.map = new LinkedHashMap<>();
-        }
-
-        public MapBuilder(Comparator<? super K> comparator) {
-            this.map = new TreeMap<>(comparator);
-        }
-
-        public MapBuilder<K, V> put(K k, V v) {
-            map.put(k, v);
-            return this;
-        }
-
-        public Map<K, V> build() {
-            return map;
-        }
-
-        public Map<K, V> immutable() {
-            return Collections.unmodifiableMap(map);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/IgnoringEmptyMap.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/IgnoringEmptyMap.java b/sshd-core/src/main/java/org/apache/sshd/common/util/IgnoringEmptyMap.java
deleted file mode 100644
index 8f18bfe..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/IgnoringEmptyMap.java
+++ /dev/null
@@ -1,128 +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.util.Collection;
-import java.util.Collections;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-
-/**
- * A dummy map that ignores all {@code put/remove} calls
- *
- * @param <K> Key type
- * @param <V> Value type
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class IgnoringEmptyMap<K, V> implements Map<K, V> {
-    @SuppressWarnings("rawtypes")
-    private static final IgnoringEmptyMap INSTANCE = new IgnoringEmptyMap();
-
-    public IgnoringEmptyMap() {
-        super();
-    }
-
-    @Override
-    public int size() {
-        return 0;
-    }
-
-    @Override
-    public boolean isEmpty() {
-        return true;
-    }
-
-    @Override
-    public boolean containsValue(Object value) {
-        Objects.requireNonNull(value, "No value provided");
-        return false;
-    }
-
-    @Override
-    public boolean containsKey(Object key) {
-        Objects.requireNonNull(key, "No key provided");
-        return false;
-    }
-
-    @Override
-    public V get(Object key) {
-        Objects.requireNonNull(key, "No key provided");
-        return null;
-    }
-
-    @Override
-    public V put(K key, V value) {
-        Objects.requireNonNull(key, "No key provided");
-        Objects.requireNonNull(value, "No value provided");
-        return null;
-    }
-
-    @Override
-    public V remove(Object key) {
-        Objects.requireNonNull(key, "No key provided");
-        return null;
-    }
-
-    @Override
-    public void putAll(Map<? extends K, ? extends V> m) {
-        // ignored
-    }
-
-    @Override
-    public void clear() {
-        // ignored
-    }
-
-    @Override
-    public Set<K> keySet() {
-        return Collections.emptySet();
-    }
-
-    @Override
-    public Collection<V> values() {
-        return Collections.emptyList();
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        return o instanceof IgnoringEmptyMap<?, ?>;
-    }
-
-    @Override
-    public int hashCode() {
-        return 0;
-    }
-
-    @Override
-    public String toString() {
-        return "{}";
-    }
-
-    @Override
-    public Set<Entry<K, V>> entrySet() {
-        return Collections.emptySet();
-    }
-
-    @SuppressWarnings("unchecked")
-    public static <K, V> IgnoringEmptyMap<K, V> getInstance() {
-        return INSTANCE;
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/Int2IntFunction.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/Int2IntFunction.java b/sshd-core/src/main/java/org/apache/sshd/common/util/Int2IntFunction.java
deleted file mode 100644
index 490abb0..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/Int2IntFunction.java
+++ /dev/null
@@ -1,66 +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.util.function.IntUnaryOperator;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public final class Int2IntFunction {
-    private Int2IntFunction() {
-        throw new UnsupportedOperationException("No instance");
-    }
-
-    public static IntUnaryOperator sub(int delta) {
-        return add(0 - delta);
-    }
-
-    public static IntUnaryOperator add(int delta) {
-        if (delta == 0) {
-            return IntUnaryOperator.identity();
-        } else {
-            return value -> value + delta;
-        }
-    }
-
-    public static IntUnaryOperator mul(int factor) {
-        if (factor == 0) {
-            return constant(0);
-        } else if (factor == 1) {
-            return IntUnaryOperator.identity();
-        } else {
-            return value -> value * factor;
-        }
-    }
-
-    public static IntUnaryOperator constant(int v) {
-        return value -> v;
-    }
-
-    public static IntUnaryOperator div(int factor) {
-        if (factor == 1) {
-            return IntUnaryOperator.identity();
-        } else {
-            ValidateUtils.checkTrue(factor != 0, "Zero division factor");
-            return value -> value / factor;
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/Invoker.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/Invoker.java b/sshd-core/src/main/java/org/apache/sshd/common/util/Invoker.java
deleted file mode 100644
index 71cbebd..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/Invoker.java
+++ /dev/null
@@ -1,116 +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.util.AbstractMap.SimpleImmutableEntry;
-import java.util.Collection;
-import java.util.Map;
-
-/**
- * The complement to the {@code Callable} interface - accepts one argument
- * and possibly throws somethind
- *
- * @param <ARG> Argument type
- * @param <RET> Return type
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FunctionalInterface
-public interface Invoker<ARG, RET> {
-    RET invoke(ARG arg) throws Throwable;
-
-    static <ARG> Invoker<ARG, Void> wrapAll(Collection<? extends Invoker<? super ARG, ?>> invokers) {
-        return arg -> {
-            invokeAll(arg, invokers);
-            return null;
-        };
-    }
-
-    /**
-     * Invokes <U>all</U> the instances ignoring the return value. Any
-     * intermediate exceptions are accumulated and thrown at the end.
-     *
-     * @param <ARG> Argument type
-     * @param arg The argument to pass to the {@link #invoke(Object)} method
-     * @param invokers The invokers to scan - ignored if {@code null}/empty
-     * (also ignores {@code null} members)
-     * @throws Throwable If invocation failed
-     */
-    static <ARG> void invokeAll(ARG arg, Collection<? extends Invoker<? super ARG, ?>> invokers) throws Throwable {
-        if (GenericUtils.isEmpty(invokers)) {
-            return;
-        }
-
-        Throwable err = null;
-        for (Invoker<? super ARG, ?> i : invokers) {
-            if (i == null) {
-                continue;
-            }
-
-            try {
-                i.invoke(arg);
-            } catch (Throwable t) {
-                err = GenericUtils.accumulateException(err, t);
-            }
-        }
-
-        if (err != null) {
-            throw err;
-        }
-    }
-
-    static <ARG> Invoker<ARG, Void> wrapFirst(Collection<? extends Invoker<? super ARG, ?>> invokers) {
-        return arg -> {
-            Map.Entry<Invoker<? super ARG, ?>, Throwable> result = invokeTillFirstFailure(arg, invokers);
-            if (result != null) {
-                throw result.getValue();
-            }
-            return null;
-        };
-    }
-
-    /**
-     * Invokes all instances until 1st failure (if any)
-     *
-     * @param <ARG> Argument type
-     * @param arg The argument to pass to the {@link #invoke(Object)} method
-     * @param invokers The invokers to scan - ignored if {@code null}/empty
-     * (also ignores {@code null} members)
-     * @return A {@link SimpleImmutableEntry} representing the <U>first</U> failed
-     * invocation - {@code null} if all were successful (or none invoked).
-     */
-    static <ARG> SimpleImmutableEntry<Invoker<? super ARG, ?>, Throwable> invokeTillFirstFailure(ARG arg, Collection<? extends Invoker<? super ARG, ?>> invokers) {
-        if (GenericUtils.isEmpty(invokers)) {
-            return null;
-        }
-
-        for (Invoker<? super ARG, ?> i : invokers) {
-            if (i == null) {
-                continue;
-            }
-
-            try {
-                i.invoke(arg);
-            } catch (Throwable t) {
-                return new SimpleImmutableEntry<>(i, t);
-            }
-        }
-
-        return null;
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/MapEntryUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/MapEntryUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/MapEntryUtils.java
deleted file mode 100644
index 6bcc93a..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/MapEntryUtils.java
+++ /dev/null
@@ -1,51 +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.util.Comparator;
-import java.util.Map;
-
-/**
- * Represents an un-modifiable pair of values
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public final class MapEntryUtils {
-    @SuppressWarnings({"rawtypes", "unchecked"})
-    private static final Comparator<Map.Entry<Comparable, ?>> BY_KEY_COMPARATOR = (o1, o2) -> {
-        Comparable k1 = o1.getKey();
-        Comparable k2 = o2.getKey();
-        return k1.compareTo(k2);
-    };
-
-    private MapEntryUtils() {
-        throw new UnsupportedOperationException("No instance");
-    }
-
-    /**
-     * @param <K> The {@link Comparable} key type
-     * @param <V> The associated entry value
-     * @return A {@link Comparator} for {@link java.util.Map.Entry}-ies that
-     * compares the key values
-     */
-    @SuppressWarnings({ "unchecked", "rawtypes" })
-    public static <K extends Comparable<K>, V> Comparator<Map.Entry<K, V>> byKeyEntryComparator() {
-        return (Comparator) BY_KEY_COMPARATOR;
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/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
deleted file mode 100644
index b1aed93..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/NumberUtils.java
+++ /dev/null
@@ -1,313 +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.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.stream.IntStream;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public final class NumberUtils {
-    /**
-     * A {@link List} of all the {@link Class} types used to represent the
-     * primitive numerical values
-     */
-    public static final List<Class<?>> NUMERIC_PRIMITIVE_CLASSES =
-            GenericUtils.unmodifiableList(
-                        Byte.TYPE, Short.TYPE, Integer.TYPE, Long.TYPE,
-                        Float.TYPE, Double.TYPE
-                    );
-
-    /**
-     * A {@link List} containing all the pure powers of 2 for a {@code long}
-     * value. The value at index <I>n</I> is 2 to the power of <I>n</I>
-     */
-    public static final List<Long> POWERS_OF_TWO =
-            GenericUtils.unmodifiableList(IntStream.range(0, 64)
-                    .mapToObj(i -> 1L << i));
-
-    private NumberUtils() {
-        throw new UnsupportedOperationException("No instance");
-    }
-
-    public static boolean isPowerOf2(long value) {
-        for (Long l : POWERS_OF_TWO) {
-            if (value == l) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-    public static long getNextPowerOf2(long value) {
-        long j = 1L;
-        while (j < value) {
-            j <<= 1;
-        }
-        return j;
-    }
-
-    public static int getNextPowerOf2(int value) {
-        int j = 1;
-        while (j < value) {
-            j <<= 1;
-        }
-        return j;
-    }
-
-    public static int hashCode(long... values) {
-        return Arrays.hashCode(values);
-    }
-
-    public static int hashCode(int... values) {
-        return Arrays.hashCode(values);
-    }
-
-    public static int hashCode(byte... values) {
-        return Arrays.hashCode(values);
-    }
-
-    public static int hashCode(byte[] a, int offset, int len) {
-        if (len == 0) {
-            return 0;
-        }
-
-        int result = 1;
-        for (int pos = offset, count = 0; count < len; pos++, count++) {
-            byte element = a[pos];
-            result = 31 * result + element;
-        }
-
-        return result;
-    }
-
-    public static int diffOffset(byte[] a1, int startPos1, byte[] a2, int startPos2, int len) {
-        for (int pos1 = startPos1, pos2 = startPos2, count = 0; count < len; pos1++, pos2++, count++) {
-            byte v1 = a1[pos1];
-            byte v2 = a2[pos2];
-            if (v1 != v2) {
-                return count;
-            }
-        }
-
-        return -1;
-    }
-
-    /**
-     * @param clazz The {@link Class} to examine - ignored if {@code null}
-     * @return If the class is a {@link Number} or one of the primitive numerical types
-     * @see #NUMERIC_PRIMITIVE_CLASSES
-     */
-    public static boolean isNumericClass(Class<?> clazz) {
-        if (clazz == null) {
-            return false;
-        }
-
-        // turns out that the primitive types are not assignable to Number
-        if (Number.class.isAssignableFrom(clazz)) {
-            return true;
-        }
-
-        return NUMERIC_PRIMITIVE_CLASSES.indexOf(clazz) >= 0;
-    }
-
-    /**
-     * Converts a {@link Number} into an {@link Integer} if not already such
-     *
-     * @param n The {@link Number} - ignored if {@code null}
-     * @return The equivalent {@link Integer} value
-     */
-    public static Integer toInteger(Number n) {
-        if (n == null) {
-            return null;
-        } else if (n instanceof Integer) {
-            return (Integer) n;
-        } else {
-            return 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(v);
-        }
-
-        return l;
-    }
-
-    /**
-     * Checks if optional sign and all others are '0'-'9'
-     * @param cs The {@link CharSequence} to check
-     * @return {@code true} if valid integer number
-     */
-    public static boolean isIntegerNumber(CharSequence cs) {
-        if (GenericUtils.isEmpty(cs)) {
-            return false;
-        }
-
-        for (int index = 0; index < cs.length(); index++) {
-            char c = cs.charAt(0);
-            if ((c >= '0') && (c <= '9')) {
-                continue;
-            }
-
-            if ((c == '+') || (c == '-')) {
-                if (index == 0) {
-                    continue;
-                }
-            }
-
-            return false;
-        }
-
-        return true;
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/ObjectBuilder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/ObjectBuilder.java b/sshd-core/src/main/java/org/apache/sshd/common/util/ObjectBuilder.java
deleted file mode 100644
index 23884a2..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/ObjectBuilder.java
+++ /dev/null
@@ -1,38 +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.util.function.Supplier;
-
-/**
- * A generic builder interface
- *
- * @param <T> Type of object being built
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FunctionalInterface
-public interface ObjectBuilder<T> extends Supplier<T> {
-    @Override
-    default T get() {
-        return build();
-    }
-
-    T build();
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/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
deleted file mode 100644
index f0b2c7e..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/OsUtils.java
+++ /dev/null
@@ -1,257 +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.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicReference;
-
-/**
- * Operating system dependent utility methods.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public final class OsUtils {
-
-    /**
-     * Property that can be used to override the reported value from {@link #getCurrentUser()}.
-     * If not set then &quot;user.name&quot; system property is used
-     */
-    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";
-
-    public static final String ROOT_USER = "root";
-
-    public static final List<String> LINUX_COMMAND =
-            Collections.unmodifiableList(Arrays.asList(LINUX_SHELL_COMMAND_NAME, "-i", "-l"));
-    public static final List<String> WINDOWS_COMMAND =
-            Collections.unmodifiableList(Collections.singletonList(WINDOWS_SHELL_COMMAND_NAME));
-
-    private static final AtomicReference<String> CURRENT_USER_HOLDER = new AtomicReference<>(null);
-    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");
-    }
-
-    /**
-     * @return true if the host is a UNIX system (and not Windows).
-     */
-    public static boolean isUNIX() {
-        return !isWin32();
-    }
-
-    /**
-     * @return true if the host is Windows (and not UNIX).
-     * @see #OS_TYPE_OVERRIDE_PROP
-     * @see #setWin32(Boolean)
-     */
-    public static boolean isWin32() {
-        Boolean typeValue;
-        synchronized (OS_TYPE_HOLDER) {
-            typeValue = OS_TYPE_HOLDER.get();
-            if (typeValue != null) {    // is it the 1st time
-                return typeValue;
-            }
-
-            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;
-    }
-
-    /**
-     * 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() {
-        return resolveInteractiveCommand(isWin32());
-    }
-
-    public static List<String> resolveInteractiveCommand(boolean isWin32) {
-        if (isWin32) {
-            return WINDOWS_COMMAND;
-        } else {
-            return LINUX_COMMAND;
-        }
-    }
-
-    /**
-     * Get current user name
-     *
-     * @return Current user
-     * @see #CURRENT_USER_OVERRIDE_PROP
-     */
-    public static String getCurrentUser() {
-        String username = null;
-        synchronized (CURRENT_USER_HOLDER) {
-            username = CURRENT_USER_HOLDER.get();
-            if (username != null) {  // have we already resolved it ?
-                return username;
-            }
-
-            username = getCanonicalUser(System.getProperty(CURRENT_USER_OVERRIDE_PROP, System.getProperty("user.name")));
-            ValidateUtils.checkNotNullAndNotEmpty(username, "No username available");
-            CURRENT_USER_HOLDER.set(username);
-        }
-
-        return username;
-    }
-
-    /**
-     * Remove {@code Windows} domain and/or group prefix as well as &quot;(User);&quot suffix
-     *
-     * @param user The original username - ignored if {@code null}/empty
-     * @return The canonical user - unchanged if {@code Unix} O/S
-     */
-    public static String getCanonicalUser(String user) {
-        if (GenericUtils.isEmpty(user)) {
-            return user;
-        }
-
-        // Windows owner sometime has the domain and/or group prepended to it
-        if (isWin32()) {
-            int pos = user.lastIndexOf('\\');
-            if (pos > 0) {
-                user = user.substring(pos + 1);
-            }
-
-            pos = user.indexOf(' ');
-            if (pos > 0) {
-                user = user.substring(0, pos).trim();
-            }
-        }
-
-        return user;
-    }
-
-    /**
-     * Attempts to resolve canonical group name for {@code Windows}
-     *
-     * @param group The original group name - used if not {@code null}/empty
-     * @param user The owner name - sometimes it contains a group name
-     * @return The canonical group name
-     */
-    public static String resolveCanonicalGroup(String group, String user) {
-        if (isUNIX()) {
-            return group;
-        }
-
-        // we reach this code only for Windows
-        if (GenericUtils.isEmpty(group)) {
-            int pos = GenericUtils.isEmpty(user) ? -1 : user.lastIndexOf('\\');
-            return (pos > 0) ? user.substring(0, pos) : group;
-        }
-
-        int pos = group.indexOf(' ');
-        return (pos < 0) ? group : group.substring(0, pos).trim();
-    }
-
-    /**
-     * Can be used to programmatically set the username reported by {@link #getCurrentUser()}
-     * @param username The username to set - if {@code null} then {@link #CURRENT_USER_OVERRIDE_PROP}
-     * will be consulted
-     */
-    public static void setCurrentUser(String username) {
-        synchronized (CURRENT_USER_HOLDER) {
-            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);
-        }
-    }
-
-    /**
-     * @param path The original path
-     * @return A path that can be compared with another one where case
-     * sensitivity of the underlying O/S has been taken into account -
-     * never {@code null}
-     */
-    public static String getComparablePath(String path) {
-        String p = (path == null) ? "" : path;
-        return isWin32() ? p.toLowerCase() : p;
-    }
-}


[16/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common utilities code from sshd-core into sshd-common (new artifact)

Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/Readable.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/Readable.java b/sshd-core/src/main/java/org/apache/sshd/common/util/Readable.java
deleted file mode 100644
index 3075eb3..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/Readable.java
+++ /dev/null
@@ -1,53 +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.nio.ByteBuffer;
-import java.util.Objects;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface Readable {
-
-    int available();
-
-    void getRawBytes(byte[] data, int offset, int len);
-
-    /**
-     * Wrap a {@link ByteBuffer} as a {@link Readable} instance
-     *
-     * @param buffer The {@link ByteBuffer} to wrap - never {@code null}
-     * @return The {@link Readable} wrapper
-     */
-    static Readable readable(ByteBuffer buffer) {
-        Objects.requireNonNull(buffer, "No buffer to wrap");
-        return new Readable() {
-            @Override
-            public int available() {
-                return buffer.remaining();
-            }
-
-            @Override
-            public void getRawBytes(byte[] data, int offset, int len) {
-                buffer.get(data, offset, len);
-            }
-        };
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/ReflectionUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/ReflectionUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/ReflectionUtils.java
deleted file mode 100644
index 84a84f9..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/ReflectionUtils.java
+++ /dev/null
@@ -1,53 +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.lang.reflect.Field;
-import java.util.Collection;
-import java.util.function.Function;
-import java.util.function.Predicate;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public final class ReflectionUtils {
-    public static final Function<Field, String> FIELD_NAME_EXTRACTOR = f -> (f == null) ? null : f.getName();
-
-    private ReflectionUtils() {
-        throw new UnsupportedOperationException("No instance");
-    }
-
-    public static Collection<Field> getMatchingFields(Class<?> clazz, Predicate<? super Field> acceptor) {
-        return GenericUtils.selectMatchingMembers(acceptor, clazz.getFields());
-    }
-
-    public static Collection<Field> getMatchingDeclaredFields(Class<?> clazz, Predicate<? super Field> acceptor) {
-        return GenericUtils.selectMatchingMembers(acceptor, clazz.getDeclaredFields());
-    }
-
-    public static boolean isClassAvailable(ClassLoader cl, String className) {
-        try {
-            cl.loadClass(className);
-            return true;
-        } catch (Throwable ignored) {
-            return false;
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/SelectorUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/SelectorUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/SelectorUtils.java
deleted file mode 100644
index 53bca90..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/SelectorUtils.java
+++ /dev/null
@@ -1,805 +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.nio.file.FileSystem;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-import java.util.StringTokenizer;
-
-/**
- * <p>This is a utility class used by selectors and DirectoryScanner. The
- * functionality more properly belongs just to selectors, but unfortunately
- * DirectoryScanner exposed these as protected methods. Thus we have to
- * support any subclasses of DirectoryScanner that may access these methods.
- * </p>
- * <p>This is a Singleton.</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>
- * @version $Id$
- * @since 1.5
- */
-public final class SelectorUtils {
-
-    public static final String PATTERN_HANDLER_PREFIX = "[";
-
-    public static final String PATTERN_HANDLER_SUFFIX = "]";
-
-    public static final String REGEX_HANDLER_PREFIX = "%regex" + PATTERN_HANDLER_PREFIX;
-
-    public static final String ANT_HANDLER_PREFIX = "%ant" + PATTERN_HANDLER_PREFIX;
-
-    /**
-     * Private Constructor
-     */
-    private SelectorUtils() {
-        throw new UnsupportedOperationException("No instance allowed");
-    }
-
-    /**
-     * <p>Tests whether or not a given path matches the start of a given
-     * pattern up to the first "**".</p>
-     *
-     * <p>This is not a general purpose test and should only be used if you
-     * can live with false positives. For example, <code>pattern=**\a</code>
-     * and <code>str=b</code> will yield <code>true</code>.</p>
-     *
-     * @param pattern The pattern to match against. Must not be
-     *                {@code null}.
-     * @param str     The path to match, as a String. Must not be
-     *                {@code null}.
-     * @return whether or not a given path matches the start of a given
-     * pattern up to the first "**".
-     */
-    public static boolean matchPatternStart(String pattern, String str) {
-        return matchPatternStart(pattern, str, true);
-    }
-
-    /**
-     * <p>Tests whether or not a given path matches the start of a given
-     * pattern up to the first "**".</p>
-     *
-     * <p>This is not a general purpose test and should only be used if you
-     * can live with false positives. For example, <code>pattern=**\a</code>
-     * and <code>str=b</code> will yield <code>true</code>.</p>
-     *
-     * @param pattern         The pattern to match against. Must not be
-     *                        {@code null}.
-     * @param str             The path to match, as a String. Must not be
-     *                        {@code null}.
-     * @param isCaseSensitive Whether or not matching should be performed
-     *                        case sensitively.
-     * @return whether or not a given path matches the start of a given
-     * pattern up to the first "**".
-     */
-    public static boolean matchPatternStart(String pattern, String str,
-                                            boolean isCaseSensitive) {
-        if (pattern.length() > (REGEX_HANDLER_PREFIX.length() + PATTERN_HANDLER_SUFFIX.length() + 1)
-                && pattern.startsWith(REGEX_HANDLER_PREFIX) && pattern.endsWith(PATTERN_HANDLER_SUFFIX)) {
-            // FIXME: ICK! But we can't do partial matches for regex, so we have to reserve judgement until we have
-            // a file to deal with, or we can definitely say this is an exclusion...
-            return true;
-        } else {
-            if (pattern.length() > (ANT_HANDLER_PREFIX.length() + PATTERN_HANDLER_SUFFIX.length() + 1)
-                    && pattern.startsWith(ANT_HANDLER_PREFIX) && pattern.endsWith(PATTERN_HANDLER_SUFFIX)) {
-                pattern =
-                        pattern.substring(ANT_HANDLER_PREFIX.length(), pattern.length() - PATTERN_HANDLER_SUFFIX.length());
-            }
-
-            String altStr = str.replace('\\', '/');
-
-            return matchAntPathPatternStart(pattern, str, File.separator, isCaseSensitive)
-                    || matchAntPathPatternStart(pattern, altStr, "/", isCaseSensitive);
-        }
-    }
-
-    private static boolean matchAntPathPatternStart(String pattern, String str, String separator, boolean isCaseSensitive) {
-        // When str starts with a File.separator, pattern has to start with a
-        // File.separator.
-        // When pattern starts with a File.separator, str has to start with a
-        // File.separator.
-        if (str.startsWith(separator) != pattern.startsWith(separator)) {
-            return false;
-        }
-
-        List<String> patDirs = tokenizePath(pattern, separator);
-        List<String> strDirs = tokenizePath(str, separator);
-
-        int patIdxStart = 0;
-        int patIdxEnd = patDirs.size() - 1;
-        int strIdxStart = 0;
-        int strIdxEnd = strDirs.size() - 1;
-
-        // up to first '**'
-        while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
-            String patDir = patDirs.get(patIdxStart);
-            if (patDir.equals("**")) {
-                break;
-            }
-            if (!match(patDir, strDirs.get(strIdxStart),
-                    isCaseSensitive)) {
-                return false;
-            }
-            patIdxStart++;
-            strIdxStart++;
-        }
-
-        // CHECKSTYLE:OFF
-        if (strIdxStart > strIdxEnd) {
-            // String is exhausted
-            return true;
-        } else {
-            return patIdxStart <= patIdxEnd;
-        }
-        // CHECKSTYLE:ON
-    }
-
-    /**
-     * Tests whether or not a given path matches a given pattern.
-     *
-     * @param pattern The pattern to match against. Must not be
-     *                {@code null}.
-     * @param str     The path to match, as a String. Must not be
-     *                {@code null}.
-     * @return <code>true</code> if the pattern matches against the string,
-     * or <code>false</code> otherwise.
-     */
-    public static boolean matchPath(String pattern, String str) {
-        return matchPath(pattern, str, true);
-    }
-
-    /**
-     * Tests whether or not a given path matches a given pattern.
-     *
-     * @param pattern         The pattern to match against. Must not be
-     *                        {@code null}.
-     * @param str             The path to match, as a String. Must not be
-     *                        {@code null}.
-     * @param isCaseSensitive Whether or not matching should be performed
-     *                        case sensitively.
-     * @return <code>true</code> if the pattern matches against the string,
-     * or <code>false</code> otherwise.
-     */
-    public static boolean matchPath(String pattern, String str, boolean isCaseSensitive) {
-        if (pattern.length() > (REGEX_HANDLER_PREFIX.length() + PATTERN_HANDLER_SUFFIX.length() + 1)
-                && pattern.startsWith(REGEX_HANDLER_PREFIX) && pattern.endsWith(PATTERN_HANDLER_SUFFIX)) {
-            pattern = pattern.substring(REGEX_HANDLER_PREFIX.length(), pattern.length()
-                    - PATTERN_HANDLER_SUFFIX.length());
-
-            return str.matches(pattern);
-        } else {
-            if (pattern.length() > (ANT_HANDLER_PREFIX.length() + PATTERN_HANDLER_SUFFIX.length() + 1)
-                    && pattern.startsWith(ANT_HANDLER_PREFIX) && pattern.endsWith(PATTERN_HANDLER_SUFFIX)) {
-                pattern =
-                        pattern.substring(ANT_HANDLER_PREFIX.length(), pattern.length() - PATTERN_HANDLER_SUFFIX.length());
-            }
-
-            return matchAntPathPattern(pattern, str, isCaseSensitive);
-        }
-    }
-
-    private static boolean matchAntPathPattern(String pattern, String str, boolean isCaseSensitive) {
-        // When str starts with a File.separator, pattern has to start with a
-        // File.separator.
-        // When pattern starts with a File.separator, str has to start with a
-        // File.separator.
-        if (str.startsWith(File.separator) != pattern.startsWith(File.separator)) {
-            return false;
-        }
-
-        List<String> patDirs = tokenizePath(pattern, File.separator);
-        List<String> strDirs = tokenizePath(str, File.separator);
-
-        int patIdxStart = 0;
-        int patIdxEnd = patDirs.size() - 1;
-        int strIdxStart = 0;
-        int strIdxEnd = strDirs.size() - 1;
-
-        // up to first '**'
-        while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
-            String patDir = patDirs.get(patIdxStart);
-            if (patDir.equals("**")) {
-                break;
-            }
-            if (!match(patDir, strDirs.get(strIdxStart),
-                    isCaseSensitive)) {
-                patDirs = null;
-                strDirs = null;
-                return false;
-            }
-            patIdxStart++;
-            strIdxStart++;
-        }
-        if (strIdxStart > strIdxEnd) {
-            // String is exhausted
-            for (int i = patIdxStart; i <= patIdxEnd; i++) {
-                if (!patDirs.get(i).equals("**")) {
-                    patDirs = null;
-                    strDirs = null;
-                    return false;
-                }
-            }
-            return true;
-        } else {
-            if (patIdxStart > patIdxEnd) {
-                // String not exhausted, but pattern is. Failure.
-                patDirs = null;
-                strDirs = null;
-                return false;
-            }
-        }
-
-        // up to last '**'
-        while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
-            String patDir = patDirs.get(patIdxEnd);
-            if (patDir.equals("**")) {
-                break;
-            }
-            if (!match(patDir, strDirs.get(strIdxEnd),
-                    isCaseSensitive)) {
-                patDirs = null;
-                strDirs = null;
-                return false;
-            }
-            patIdxEnd--;
-            strIdxEnd--;
-        }
-        if (strIdxStart > strIdxEnd) {
-            // String is exhausted
-            for (int i = patIdxStart; i <= patIdxEnd; i++) {
-                if (!patDirs.get(i).equals("**")) {
-                    patDirs = null;
-                    strDirs = null;
-                    return false;
-                }
-            }
-            return true;
-        }
-
-        while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) {
-            int patIdxTmp = -1;
-            for (int i = patIdxStart + 1; i <= patIdxEnd; i++) {
-                if (patDirs.get(i).equals("**")) {
-                    patIdxTmp = i;
-                    break;
-                }
-            }
-            if (patIdxTmp == patIdxStart + 1) {
-                // '**/**' situation, so skip one
-                patIdxStart++;
-                continue;
-            }
-            // Find the pattern between padIdxStart & padIdxTmp in str between
-            // strIdxStart & strIdxEnd
-            int patLength = patIdxTmp - patIdxStart - 1;
-            int strLength = strIdxEnd - strIdxStart + 1;
-            int foundIdx = -1;
-            strLoop:
-            for (int i = 0; i <= strLength - patLength; i++) {
-                for (int j = 0; j < patLength; j++) {
-                    String subPat = patDirs.get(patIdxStart + j + 1);
-                    String subStr = strDirs.get(strIdxStart + i + j);
-                    if (!match(subPat, subStr, isCaseSensitive)) {
-                        continue strLoop;
-                    }
-                }
-
-                foundIdx = strIdxStart + i;
-                break;
-            }
-
-            if (foundIdx == -1) {
-                patDirs = null;
-                strDirs = null;
-                return false;
-            }
-
-            patIdxStart = patIdxTmp;
-            strIdxStart = foundIdx + patLength;
-        }
-
-        for (int i = patIdxStart; i <= patIdxEnd; i++) {
-            if (!patDirs.get(i).equals("**")) {
-                patDirs = null;
-                strDirs = null;
-                return false;
-            }
-        }
-
-        return true;
-    }
-
-    /**
-     * Tests whether or not a string matches against a pattern.
-     * The pattern may contain two special characters:<br>
-     * '*' means zero or more characters<br>
-     * '?' means one and only one character
-     *
-     * @param pattern The pattern to match against.
-     *                Must not be {@code null}.
-     * @param str     The string which must be matched against the pattern.
-     *                Must not be {@code null}.
-     * @return <code>true</code> if the string matches against the pattern,
-     * or <code>false</code> otherwise.
-     */
-    public static boolean match(String pattern, String str) {
-        return match(pattern, str, true);
-    }
-
-    /**
-     * Tests whether or not a string matches against a pattern.
-     * The pattern may contain two special characters:<br>
-     * '*' means zero or more characters<br>
-     * '?' means one and only one character
-     *
-     * @param pattern         The pattern to match against.
-     *                        Must not be {@code null}.
-     * @param str             The string which must be matched against the pattern.
-     *                        Must not be {@code null}.
-     * @param isCaseSensitive Whether or not matching should be performed
-     *                        case sensitively.
-     * @return <code>true</code> if the string matches against the pattern,
-     * or <code>false</code> otherwise.
-     */
-    @SuppressWarnings("PMD.AssignmentInOperand")
-    public static boolean match(String pattern, String str, boolean isCaseSensitive) {
-        char[] patArr = pattern.toCharArray();
-        char[] strArr = str.toCharArray();
-        int patIdxStart = 0;
-        int patIdxEnd = patArr.length - 1;
-        int strIdxStart = 0;
-        int strIdxEnd = strArr.length - 1;
-        char ch;
-
-        boolean containsStar = false;
-        for (char aPatArr : patArr) {
-            if (aPatArr == '*') {
-                containsStar = true;
-                break;
-            }
-        }
-
-        if (!containsStar) {
-            // No '*'s, so we make a shortcut
-            if (patIdxEnd != strIdxEnd) {
-                return false; // Pattern and string do not have the same size
-            }
-            for (int i = 0; i <= patIdxEnd; i++) {
-                ch = patArr[i];
-                if ((ch != '?') && (!equals(ch, strArr[i], isCaseSensitive))) {
-                    return false; // Character mismatch
-                }
-            }
-            return true; // String matches against pattern
-        }
-
-        if (patIdxEnd == 0) {
-            return true; // Pattern contains only '*', which matches anything
-        }
-
-        // Process characters before first star
-        // CHECKSTYLE:OFF
-        while (((ch = patArr[patIdxStart]) != '*') && (strIdxStart <= strIdxEnd)) {
-            if ((ch != '?') && (!equals(ch, strArr[strIdxStart], isCaseSensitive))) {
-                return false; // Character mismatch
-            }
-            patIdxStart++;
-            strIdxStart++;
-        }
-        // CHECKSTYLE:ON
-
-        if (strIdxStart > strIdxEnd) {
-            // All characters in the string are used. Check if only '*'s are
-            // left in the pattern. If so, we succeeded. Otherwise failure.
-            for (int i = patIdxStart; i <= patIdxEnd; i++) {
-                if (patArr[i] != '*') {
-                    return false;
-                }
-            }
-            return true;
-        }
-
-        // Process characters after last star
-        // CHECKSTYLE:OFF
-        while (((ch = patArr[patIdxEnd]) != '*') && (strIdxStart <= strIdxEnd)) {
-            if ((ch != '?') && (!equals(ch, strArr[strIdxEnd], isCaseSensitive))) {
-                return false; // Character mismatch
-            }
-            patIdxEnd--;
-            strIdxEnd--;
-        }
-        // CHECKSTYLE:ON
-
-        if (strIdxStart > strIdxEnd) {
-            // All characters in the string are used. Check if only '*'s are
-            // left in the pattern. If so, we succeeded. Otherwise failure.
-            for (int i = patIdxStart; i <= patIdxEnd; i++) {
-                if (patArr[i] != '*') {
-                    return false;
-                }
-            }
-            return true;
-        }
-
-        // process pattern between stars. padIdxStart and patIdxEnd point always to a '*'.
-        while ((patIdxStart != patIdxEnd) && (strIdxStart <= strIdxEnd)) {
-            int patIdxTmp = -1;
-            for (int i = patIdxStart + 1; i <= patIdxEnd; i++) {
-                if (patArr[i] == '*') {
-                    patIdxTmp = i;
-                    break;
-                }
-            }
-            if (patIdxTmp == patIdxStart + 1) {
-                // Two stars next to each other, skip the first one.
-                patIdxStart++;
-                continue;
-            }
-            // Find the pattern between padIdxStart & padIdxTmp in str between
-            // strIdxStart & strIdxEnd
-            int patLength = patIdxTmp - patIdxStart - 1;
-            int strLength = strIdxEnd - strIdxStart + 1;
-            int foundIdx = -1;
-            strLoop:
-            for (int i = 0; i <= strLength - patLength; i++) {
-                for (int j = 0; j < patLength; j++) {
-                    ch = patArr[patIdxStart + j + 1];
-                    if (ch != '?' && !equals(ch, strArr[strIdxStart + i + j], isCaseSensitive)) {
-                        continue strLoop;
-                    }
-                }
-
-                foundIdx = strIdxStart + i;
-                break;
-            }
-
-            if (foundIdx == -1) {
-                return false;
-            }
-
-            patIdxStart = patIdxTmp;
-            strIdxStart = foundIdx + patLength;
-        }
-
-        // All characters in the string are used. Check if only '*'s are left
-        // in the pattern. If so, we succeeded. Otherwise failure.
-        for (int i = patIdxStart; i <= patIdxEnd; i++) {
-            if (patArr[i] != '*') {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Tests whether two characters are equal.
-     * @param c1 1st character
-     * @param c2 2nd character
-     * @param isCaseSensitive Whether to compare case sensitive
-     * @return {@code true} if equal characters
-     */
-    public static boolean equals(char c1, char c2, boolean isCaseSensitive) {
-        if (c1 == c2) {
-            return true;
-        }
-        if (!isCaseSensitive) {
-            // NOTE: Try both upper case and lower case as done by String.equalsIgnoreCase()
-            if (Character.toUpperCase(c1) == Character.toUpperCase(c2)
-                    || Character.toLowerCase(c1) == Character.toLowerCase(c2)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Breaks a path up into a Vector of path elements, tokenizing on
-     * <code>File.separator</code>.
-     *
-     * @param path Path to tokenize. Must not be {@code null}.
-     * @return a List of path elements from the tokenized path
-     */
-    public static List<String> tokenizePath(String path) {
-        return tokenizePath(path, File.separator);
-    }
-
-    public static List<String> tokenizePath(String path, String separator) {
-        List<String> ret = new ArrayList<>();
-        StringTokenizer st = new StringTokenizer(path, separator);
-        while (st.hasMoreTokens()) {
-            ret.add(st.nextToken());
-        }
-        return ret;
-    }
-
-    /**   /**
-     * Converts a path to one matching the target file system by applying the
-     * &quot;slashification&quot; rules, converting it to a local path and
-     * then translating its separator to the target file system one (if different
-     * than local one)
-     * @param path          The input path
-     * @param pathSeparator The separator used to build the input path
-     * @param fs            The target {@link FileSystem} - may not be {@code null}
-     * @return The transformed path
-     * @see #translateToLocalFileSystemPath(String, char, String)
-     */
-    public static String translateToLocalFileSystemPath(String path, char pathSeparator, FileSystem fs) {
-        return translateToLocalFileSystemPath(path, pathSeparator,  Objects.requireNonNull(fs, "No target file system").getSeparator());
-    }
-
-    /**
-     * Converts a path to one matching the target file system by applying the
-     * &quot;slashification&quot; rules, converting it to a local path and
-     * then translating its separator to the target file system one (if different
-     * than local one)
-     * @param path          The input path
-     * @param pathSeparator The separator used to build the input path
-     * @param fsSeparator   The target file system separator
-     * @return The transformed path
-     * @see #applySlashifyRules(String, char)
-     * @see #translateToLocalPath(String)
-     * @see #translateToFileSystemPath(String, String, String)
-     */
-    public static String translateToLocalFileSystemPath(String path, char pathSeparator, String fsSeparator) {
-        // In case double slashes and other patterns are used
-        String slashified = applySlashifyRules(path, pathSeparator);
-        // In case we are running on Windows
-        String localPath = translateToLocalPath(slashified);
-        return translateToFileSystemPath(localPath, File.separator, fsSeparator);
-    }
-
-    /**
-     * Applies the &quot;slashification&quot; rules as specified in
-     * <A HREF="http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_266">Single Unix Specification version 3, section 3.266</A>
-     * and <A HREF="http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap04.html#tag_04_11">section 4.11 - Pathname resolution</A>
-     * @param path The original path - ignored if {@code null}/empty or does
-     * not contain any slashes
-     * @param sepChar The &quot;slash&quot; character
-     * @return The effective path - may be same as input if no changes required
-     */
-    public static String applySlashifyRules(String path, char sepChar) {
-        if (GenericUtils.isEmpty(path)) {
-            return path;
-        }
-
-        int curPos = path.indexOf(sepChar);
-        if (curPos < 0) {
-            return path;    // no slashes to handle
-        }
-
-        int lastPos = 0;
-        StringBuilder sb = null;
-        while (curPos < path.length()) {
-            curPos++;   // skip the 1st '/'
-
-            /*
-             * As per Single Unix Specification version 3, section 3.266:
-             *
-             *      Multiple successive slashes are considered to be the
-             *      same as one slash
-             */
-            int nextPos = curPos;
-            while ((nextPos < path.length()) && (path.charAt(nextPos) == sepChar)) {
-                nextPos++;
-            }
-
-            /*
-             * At this stage, nextPos is the first non-slash character after a
-             * possibly 'seqLen' sequence of consecutive slashes.
-             */
-            int seqLen = nextPos - curPos;
-            if (seqLen > 0) {
-                if (sb == null) {
-                    sb = new StringBuilder(path.length() - seqLen);
-                }
-
-                if (lastPos < curPos) {
-                    String clrText = path.substring(lastPos, curPos);
-                    sb.append(clrText);
-                }
-
-                lastPos = nextPos;
-            }
-
-            if (nextPos >= path.length()) {
-                break;  // no more data
-            }
-
-            curPos = path.indexOf(sepChar, nextPos);
-            if (curPos < nextPos) {
-                break;  // no more slashes
-            }
-        }
-
-        // check if any leftovers for the modified path
-        if (sb != null) {
-            if (lastPos < path.length()) {
-                String clrText = path.substring(lastPos);
-                sb.append(clrText);
-            }
-
-            path = sb.toString();
-        }
-
-        /*
-         * At this point we know for sure that 'path' contains only SINGLE
-         * slashes. According to section 4.11 - Pathname resolution
-         *
-         *      A pathname that contains at least one non-slash character
-         *      and that ends with one or more trailing slashes shall be
-         *      resolved as if a single dot character ( '.' ) were appended
-         *      to the pathname.
-         */
-        if ((path.length() > 1) && (path.charAt(path.length() - 1) == sepChar)) {
-            return path + ".";
-        } else {
-            return path;
-        }
-    }
-
-    /**
-     * Converts a possibly '/' separated path to a local path. <B>Note:</B>
-     * takes special care of Windows drive paths - e.g., {@code C:}
-     * by converting them to &quot;C:\&quot;
-     *
-     * @param path The original path - ignored if {@code null}/empty
-     * @return The local path
-     */
-    public static String translateToLocalPath(String path) {
-        if (GenericUtils.isEmpty(path) || (File.separatorChar == '/')) {
-            return path;
-        }
-
-        // This code is reached if we are running on Windows
-        String localPath = path.replace('/', File.separatorChar);
-        // check if '/c:' prefix
-        if ((localPath.charAt(0) == File.separatorChar) && isWindowsDriveSpecified(localPath, 1, localPath.length() - 1)) {
-            localPath = localPath.substring(1);
-        }
-        if (!isWindowsDriveSpecified(localPath)) {
-            return localPath;   // assume a relative path
-        }
-
-        /*
-         * Here we know that we have at least a "C:" string - make sure it
-         * is followed by the local file separator. Note: if all we have is
-         * just the drive, we will create a "C:\" path since this is the
-         * preferred Windows way to refer to root drives in the file system
-         */
-        if (localPath.length() == 2) {
-            return localPath + File.separator;  // all we have is "C:"
-        } else if (localPath.charAt(2) != File.separatorChar) {
-            // be nice and add the missing file separator - C:foo => C:\foo
-            return localPath.substring(0, 2) + File.separator + localPath.substring(2);
-        } else {
-            return localPath;
-        }
-    }
-
-    public static boolean isWindowsDriveSpecified(CharSequence cs) {
-        return isWindowsDriveSpecified(cs, 0, GenericUtils.length(cs));
-    }
-
-    public static boolean isWindowsDriveSpecified(CharSequence cs, int offset, int len) {
-        if ((len < 2) || (cs.charAt(offset + 1) != ':')) {
-            return false;
-        }
-
-        char drive = cs.charAt(offset);
-        return ((drive >= 'a') && (drive <= 'z')) || ((drive >= 'A') && (drive <= 'Z'));
-    }
-
-    /**
-     * Converts a path containing a specific separator to one using the
-     * specified file-system one
-     * @param path          The input path - ignored if {@code null}/empty
-     * @param pathSeparator The separator used to build the input path - may not
-     *                      be {@code null}/empty
-     * @param fs            The target {@link FileSystem} - may not be {@code null}
-     * @return The path where the separator used to build it is replaced by
-     * the file-system one (if different)
-     * @see FileSystem#getSeparator()
-     * @see #translateToFileSystemPath(String, String, String)
-     */
-    public static String translateToFileSystemPath(String path, String pathSeparator, FileSystem fs) {
-        return translateToFileSystemPath(path, pathSeparator, Objects.requireNonNull(fs, "No target file system").getSeparator());
-    }
-
-    /**
-     * Converts a path containing a specific separator to one using the
-     * specified file-system one
-     * @param path          The input path - ignored if {@code null}/empty
-     * @param pathSeparator The separator used to build the input path - may not
-     *                      be {@code null}/empty
-     * @param fsSeparator   The target file system separator - may not be {@code null}/empty
-     * @return The path where the separator used to build it is replaced by
-     * the file-system one (if different)
-     * @throws IllegalArgumentException if path or file-system separator are {@code null}/empty
-     * or if the separators are different and the path contains the target
-     * file-system separator as it would create an ambiguity
-     */
-    public static String translateToFileSystemPath(String path, String pathSeparator, String fsSeparator) {
-        ValidateUtils.checkNotNullAndNotEmpty(pathSeparator, "Missing path separator");
-        ValidateUtils.checkNotNullAndNotEmpty(fsSeparator, "Missing file-system separator");
-
-        if (GenericUtils.isEmpty(path) || Objects.equals(pathSeparator, fsSeparator)) {
-            return path;
-        }
-
-        // make sure path does not contain the target separator
-        if (path.contains(fsSeparator)) {
-            ValidateUtils.throwIllegalArgumentException("File system replacement may yield ambiguous result for %s with separator=%s", path, fsSeparator);
-        }
-
-        // check most likely case
-        if ((pathSeparator.length() == 1) && (fsSeparator.length() == 1)) {
-            return path.replace(pathSeparator.charAt(0), fsSeparator.charAt(0));
-        } else {
-            return path.replace(pathSeparator, fsSeparator);
-        }
-    }
-
-    /**
-     * Returns dependency information on these two files. If src has been
-     * modified later than target, it returns true. If target doesn't exist,
-     * it likewise returns true. Otherwise, target is newer than src and
-     * is not out of date, thus the method returns false. It also returns
-     * false if the src file doesn't even exist, since how could the
-     * target then be out of date.
-     *
-     * @param src         the original file
-     * @param target      the file being compared against
-     * @param granularity the amount in seconds of slack we will give in
-     *                    determining out of dateness
-     * @return whether the target is out of date
-     */
-    public static boolean isOutOfDate(File src, File target, int granularity) {
-        if (!src.exists()) {
-            return false;
-        }
-        if (!target.exists()) {
-            return true;
-        }
-        return (src.lastModified() - granularity) > target.lastModified();
-    }
-
-    /**
-     * "Flattens" a string by removing all whitespace (space, tab, linefeed,
-     * carriage return, and formfeed). This uses StringTokenizer and the
-     * default set of tokens as documented in the single arguement constructor.
-     *
-     * @param input a String to remove all whitespace.
-     * @return a String that has had all whitespace removed.
-     */
-    public static String removeWhitespace(String input) {
-        StringBuilder result = new StringBuilder();
-        if (input != null) {
-            StringTokenizer st = new StringTokenizer(input);
-            while (st.hasMoreTokens()) {
-                result.append(st.nextToken());
-            }
-        }
-        return result.toString();
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/SshdEventListener.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/SshdEventListener.java b/sshd-core/src/main/java/org/apache/sshd/common/util/SshdEventListener.java
deleted file mode 100644
index ea6a3dd..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/SshdEventListener.java
+++ /dev/null
@@ -1,44 +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.lang.reflect.Proxy;
-import java.util.EventListener;
-import java.util.Objects;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface SshdEventListener extends EventListener {
-
-    /**
-     * Makes sure that the listener is neither {@code null} nor a proxy
-     *
-     * @param <L> Type of {@link SshdEventListener} being validation
-     * @param listener The listener instance
-     * @param prefix Prefix text to be prepended to validation failure messages
-     * @return The validated instance
-     */
-    static <L extends SshdEventListener> L validateListener(L listener, String prefix) {
-        Objects.requireNonNull(listener, prefix + ": no instance");
-        ValidateUtils.checkTrue(!Proxy.isProxyClass(listener.getClass()), prefix + ": proxies N/A");
-        return listener;
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/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
deleted file mode 100644
index a55e5d6..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/ValidateUtils.java
+++ /dev/null
@@ -1,216 +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.util.Collection;
-import java.util.Map;
-import java.util.function.Function;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public final class ValidateUtils {
-    private ValidateUtils() {
-        throw new UnsupportedOperationException("No instance");
-    }
-
-    public static <T> T checkNotNull(T t, String message) {
-        checkTrue(t != null, message);
-        return t;
-    }
-
-    public static <T> T checkNotNull(T t, String message, Object arg) {
-        checkTrue(t != null, message, arg);
-        return t;
-    }
-
-    public static <T> T checkNotNull(T t, String message, long value) {
-        checkTrue(t != null, message, value);
-        return t;
-    }
-
-    public static <T> T checkNotNull(T t, String message, Object... args) {
-        checkTrue(t != null, message, args);
-        return t;
-    }
-
-    public static String checkNotNullAndNotEmpty(String t, String message) {
-        t = checkNotNull(t, message).trim();
-        checkTrue(GenericUtils.length(t) > 0, message);
-        return t;
-    }
-
-    public static String checkNotNullAndNotEmpty(String t, String message, Object arg) {
-        t = checkNotNull(t, message, arg).trim();
-        checkTrue(GenericUtils.length(t) > 0, message, arg);
-        return t;
-    }
-
-    public static String checkNotNullAndNotEmpty(String t, String message, Object... args) {
-        t = checkNotNull(t, message, args).trim();
-        checkTrue(GenericUtils.length(t) > 0, message, args);
-        return t;
-    }
-
-    public static <K, V, M extends Map<K, V>> M checkNotNullAndNotEmpty(M t, String message, Object... args) {
-        t = checkNotNull(t, message, args);
-        checkTrue(GenericUtils.size(t) > 0, message, args);
-        return t;
-    }
-
-    public static <T, C extends Collection<T>> C checkNotNullAndNotEmpty(C t, String message, Object... args) {
-        t = checkNotNull(t, message, args);
-        checkTrue(GenericUtils.size(t) > 0, message, args);
-        return t;
-    }
-
-    public static <T, C extends Iterable<T>> C checkNotNullAndNotEmpty(C t, String message, Object... args) {
-        t = checkNotNull(t, message, args);
-        checkTrue(GenericUtils.isNotEmpty(t), message, args);
-        return t;
-    }
-
-    public static byte[] checkNotNullAndNotEmpty(byte[] a, String message) {
-        a = checkNotNull(a, 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(NumberUtils.length(a) > 0, message, args);
-        return a;
-    }
-
-    public static char[] checkNotNullAndNotEmpty(char[] a, String message) {
-        a = checkNotNull(a, message);
-        checkTrue(GenericUtils.length(a) > 0, message);
-        return a;
-    }
-
-    public static char[] checkNotNullAndNotEmpty(char[] a, String message, Object... args) {
-        a = checkNotNull(a, message, args);
-        checkTrue(GenericUtils.length(a) > 0, message, args);
-        return a;
-    }
-
-    public static int[] checkNotNullAndNotEmpty(int[] a, String message) {
-        a = checkNotNull(a, 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(NumberUtils.length(a) > 0, message, args);
-        return a;
-    }
-
-    public static <T> T[] checkNotNullAndNotEmpty(T[] t, String message, Object... args) {
-        t = checkNotNull(t, message, args);
-        checkTrue(GenericUtils.length(t) > 0, message, args);
-        return t;
-    }
-
-    public static <T> T checkInstanceOf(Object v, Class<T> expected, String message, long value) {
-        Class<?> actual = checkNotNull(v, message, value).getClass();
-        checkTrue(expected.isAssignableFrom(actual), message, value);
-        return expected.cast(v);
-    }
-
-    public static <T> T checkInstanceOf(Object v, Class<T> expected, String message) {
-        return checkInstanceOf(v, expected, message, GenericUtils.EMPTY_OBJECT_ARRAY);
-    }
-
-    public static <T> T checkInstanceOf(Object v, Class<T> expected, String message, Object arg) {
-        Class<?> actual = checkNotNull(v, message, arg).getClass();
-        checkTrue(expected.isAssignableFrom(actual), message, arg);
-        return expected.cast(v);
-    }
-
-    public static <T> T checkInstanceOf(Object v, Class<T> expected, String message, Object... args) {
-        Class<?> actual = checkNotNull(v, message, args).getClass();
-        checkTrue(expected.isAssignableFrom(actual), message, args);
-        return expected.cast(v);
-    }
-
-    public static void checkTrue(boolean flag, String message) {
-        if (!flag) {
-            throwIllegalArgumentException(message, GenericUtils.EMPTY_OBJECT_ARRAY);
-        }
-    }
-
-    public static void checkTrue(boolean flag, String message, long value) {
-        if (!flag) {
-            throwIllegalArgumentException(message, value);
-        }
-    }
-
-    public static void checkTrue(boolean flag, String message, Object arg) {
-        if (!flag) {
-            throwIllegalArgumentException(message, arg);
-        }
-    }
-
-    public static void checkTrue(boolean flag, String message, Object... args) {
-        if (!flag) {
-            throwIllegalArgumentException(message, args);
-        }
-    }
-
-    public static void throwIllegalArgumentException(String format, Object... args) {
-        throw createFormattedException(IllegalArgumentException::new, format, args);
-    }
-
-    public static void checkState(boolean flag, String message) {
-        if (!flag) {
-            throwIllegalStateException(message, GenericUtils.EMPTY_OBJECT_ARRAY);
-        }
-    }
-
-    public static void checkState(boolean flag, String message, long value) {
-        if (!flag) {
-            throwIllegalStateException(message, value);
-        }
-    }
-
-    public static void checkState(boolean flag, String message, Object arg) {
-        if (!flag) {
-            throwIllegalStateException(message, arg);
-        }
-    }
-
-    public static void checkState(boolean flag, String message, Object... args) {
-        if (!flag) {
-            throwIllegalStateException(message, args);
-        }
-    }
-
-    public static void throwIllegalStateException(String format, Object... args) {
-        throw createFormattedException(IllegalStateException::new, format, args);
-    }
-
-    public static <T extends Throwable> T createFormattedException(
-            Function<? super String, ? extends T> constructor, String format, Object... args) {
-        String message = String.format(format, args);
-        return constructor.apply(message);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/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
deleted file mode 100644
index ed1aab7..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/VersionInfo.java
+++ /dev/null
@@ -1,137 +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.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]);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/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
deleted file mode 100644
index dd61473..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/Buffer.java
+++ /dev/null
@@ -1,798 +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.buffer;
-
-import java.math.BigInteger;
-import java.nio.ByteBuffer;
-import java.nio.CharBuffer;
-import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
-import java.security.GeneralSecurityException;
-import java.security.KeyFactory;
-import java.security.KeyPair;
-import java.security.NoSuchAlgorithmException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.interfaces.DSAParams;
-import java.security.interfaces.DSAPrivateKey;
-import java.security.interfaces.DSAPublicKey;
-import java.security.interfaces.ECPrivateKey;
-import java.security.interfaces.ECPublicKey;
-import java.security.interfaces.RSAPrivateCrtKey;
-import java.security.interfaces.RSAPublicKey;
-import java.security.spec.DSAPrivateKeySpec;
-import java.security.spec.DSAPublicKeySpec;
-import java.security.spec.ECParameterSpec;
-import java.security.spec.ECPoint;
-import java.security.spec.ECPrivateKeySpec;
-import java.security.spec.ECPublicKeySpec;
-import java.security.spec.InvalidKeySpecException;
-import java.security.spec.RSAPrivateCrtKeySpec;
-import java.security.spec.RSAPublicKeySpec;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Objects;
-import java.util.function.IntUnaryOperator;
-import java.util.logging.Level;
-
-import org.apache.sshd.common.PropertyResolver;
-import org.apache.sshd.common.SshException;
-import org.apache.sshd.common.cipher.ECCurves;
-import org.apache.sshd.common.config.keys.KeyUtils;
-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.Readable;
-import org.apache.sshd.common.util.buffer.keys.BufferPublicKeyParser;
-import org.apache.sshd.common.util.logging.SimplifiedLog;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * Provides an abstract message buffer for encoding SSH messages
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class Buffer implements Readable {
-    protected final byte[] workBuf = new byte[Long.BYTES];
-
-    protected Buffer() {
-        super();
-    }
-
-    public abstract int rpos();
-
-    public abstract void rpos(int rpos);
-
-    public abstract int wpos();
-
-    public abstract void wpos(int wpos);
-
-    public abstract int capacity();
-
-    public abstract byte[] array();
-
-    public abstract void compact();
-
-    public byte[] getCompactData() {
-        int l = available();
-        if (l > 0) {
-            byte[] b = new byte[l];
-            System.arraycopy(array(), rpos(), b, 0, l);
-            return b;
-        } else {
-            return GenericUtils.EMPTY_BYTE_ARRAY;
-        }
-    }
-
-    public void clear() {
-        clear(true);
-    }
-
-    public abstract void clear(boolean wipeData);
-
-    public boolean isValidMessageStructure(Class<?>... fieldTypes) {
-        return isValidMessageStructure(GenericUtils.isEmpty(fieldTypes) ? Collections.emptyList() : Arrays.asList(fieldTypes));
-    }
-
-    public boolean isValidMessageStructure(Collection<Class<?>> fieldTypes) {
-        if (GenericUtils.isEmpty(fieldTypes)) {
-            return true;
-        }
-
-        int remainLen = available();
-        int readOffset = 0;
-        for (Class<?> ft : fieldTypes) {
-            if ((ft == boolean.class) || (ft == Boolean.class)
-                    || (ft == byte.class) || (ft == Byte.class)) {
-                if (remainLen < Byte.BYTES) {
-                    return false;
-                }
-
-                remainLen -= Byte.BYTES;
-                readOffset += Byte.BYTES;
-            } else if ((ft == short.class) || (ft == Short.class)) {
-                if (remainLen < Short.BYTES) {
-                    return false;
-                }
-
-                remainLen -= Short.BYTES;
-                readOffset += Short.BYTES;
-            } else if ((ft == int.class) || (ft == Integer.class)) {
-                if (remainLen < Integer.BYTES) {
-                    return false;
-                }
-
-                remainLen -= Integer.BYTES;
-                readOffset += Integer.BYTES;
-            } else if ((ft == long.class) || (ft == Long.class)) {
-                if (remainLen < Long.BYTES) {
-                    return false;
-                }
-
-                remainLen -= Long.BYTES;
-                readOffset += Long.BYTES;
-            } else if ((ft == byte[].class) || (ft == String.class)) {
-                if (remainLen < Integer.BYTES) {
-                    return false;
-                }
-
-                copyRawBytes(readOffset, workBuf, 0, Integer.BYTES);
-                remainLen -= Integer.BYTES;
-                readOffset += Integer.BYTES;
-
-                long length = BufferUtils.getUInt(workBuf, 0, Integer.BYTES);
-                if (length > remainLen) {
-                    return false;
-                }
-
-                remainLen -= (int) length;
-                readOffset += (int) length;
-            }
-        }
-
-        return true;
-    }
-
-    protected abstract void copyRawBytes(int offset, byte[] buf, int pos, int len);
-
-    public String toHex() {
-        return BufferUtils.toHex(array(), rpos(), available());
-    }
-
-    public void dumpHex(SimplifiedLog logger, String prefix, PropertyResolver resolver) {
-        dumpHex(logger, BufferUtils.DEFAULT_HEXDUMP_LEVEL, prefix, resolver);
-    }
-
-    public void dumpHex(SimplifiedLog logger, Level level, String prefix, PropertyResolver resolver) {
-        BufferUtils.dumpHex(logger, level, prefix, resolver, BufferUtils.DEFAULT_HEX_SEPARATOR, array(), rpos(), available());
-    }
-
-    /*======================
-       Read methods
-     ======================*/
-
-    public int getUByte() {
-        return getByte() & 0xFF;
-    }
-
-    public byte getByte() {
-        ensureAvailable(Byte.BYTES);
-        getRawBytes(workBuf, 0, Byte.BYTES);
-        return workBuf[0];
-    }
-
-    public short getShort() {
-        ensureAvailable(Short.BYTES);
-        getRawBytes(workBuf, 0, Short.BYTES);
-        short v = (short) ((workBuf[1] << Byte.SIZE) & 0xFF00);
-        v |= (short) (workBuf[0] & 0xF);
-        return v;
-    }
-
-    public int getInt() {
-        return (int) getUInt();
-    }
-
-    public long getUInt() {
-        ensureAvailable(Integer.BYTES);
-        getRawBytes(workBuf, 0, Integer.BYTES);
-        return BufferUtils.getUInt(workBuf, 0, Integer.BYTES);
-    }
-
-    public long getLong() {
-        ensureAvailable(Long.BYTES);
-        getRawBytes(workBuf, 0, Long.BYTES);
-        long l = ((long) workBuf[0] << 56) & 0xff00000000000000L;
-        l |= ((long) workBuf[1] << 48) & 0x00ff000000000000L;
-        l |= ((long) workBuf[2] << 40) & 0x0000ff0000000000L;
-        l |= ((long) workBuf[3] << 32) & 0x000000ff00000000L;
-        l |= ((long) workBuf[4] << 24) & 0x00000000ff000000L;
-        l |= ((long) workBuf[5] << 16) & 0x0000000000ff0000L;
-        l |= ((long) workBuf[6] << 8) & 0x000000000000ff00L;
-        l |= (workBuf[7]) & 0x00000000000000ffL;
-        return l;
-    }
-
-    @SuppressWarnings("PMD.BooleanGetMethodName")
-    public boolean getBoolean() {
-        return getByte() != 0;
-    }
-
-    public String getString() {
-        return getString(StandardCharsets.UTF_8);
-    }
-
-    /**
-     * @param usePrependedLength If {@code true} then there is a 32-bit
-     *                           value indicating the number of strings to read. Otherwise, the
-     *                           method will use a &quot;greedy&quot; reading of strings while more
-     *                           data available
-     * @return A {@link Collection} of the read strings
-     * @see #getStringList(boolean, Charset)
-     */
-    public Collection<String> getStringList(boolean usePrependedLength) {
-        return getStringList(usePrependedLength, StandardCharsets.UTF_8);
-    }
-
-    /**
-     * @param usePrependedLength If {@code true} then there is a 32-bit
-     *                           value indicating the number of strings to read. Otherwise, the
-     *                           method will use a &quot;greedy&quot; reading of strings while more
-     *                           data available
-     * @param charset            The {@link Charset} to use for the string
-     * @return A {@link Collection} of the read strings
-     * @see #getStringList(int, Charset)
-     * @see #getAvailableStrings()
-     */
-    public Collection<String> getStringList(boolean usePrependedLength, Charset charset) {
-        if (usePrependedLength) {
-            int count = getInt();
-            return getStringList(count, charset);
-        } else {
-            return getAvailableStrings(charset);
-        }
-    }
-
-    /**
-     * @return The remaining data as a list of strings
-     * @see #getAvailableStrings(Charset)
-     */
-    public Collection<String> getAvailableStrings() {
-        return getAvailableStrings(StandardCharsets.UTF_8);
-    }
-
-    /**
-     * @param charset The {@link Charset} to use for the strings
-     * @return The remaining data as a list of strings
-     * @see #available()
-     * @see #getString(Charset)
-     */
-    public Collection<String> getAvailableStrings(Charset charset) {
-        Collection<String> list = new LinkedList<>();
-        while (available() > 0) {
-            String s = getString(charset);
-            list.add(s);
-        }
-
-        return list;
-    }
-
-    /**
-     * @param count The <U>exact</U> number of strings to read - can be zero
-     * @return A {@link List} with the specified number of strings
-     * @see #getStringList(int, Charset)
-     */
-    public List<String> getStringList(int count) {
-        return getStringList(count, StandardCharsets.UTF_8);
-    }
-
-    /**
-     * @param count   The <U>exact</U> number of strings to read - can be zero
-     * @param charset The {@link Charset} of the strings
-     * @return A {@link List} with the specified number of strings
-     * @see #getString(Charset)
-     */
-    public List<String> getStringList(int count, Charset charset) {
-        if (count == 0) {
-            return Collections.emptyList();
-        }
-
-        List<String> list = new ArrayList<>(count);
-        for (int index = 0; index < count; index++) {
-            String s = getString(charset);
-            list.add(s);
-        }
-
-        return list;
-    }
-
-    public abstract String getString(Charset charset);
-
-    public BigInteger getMPInt() {
-        return new BigInteger(getMPIntAsBytes());
-    }
-
-    public byte[] getMPIntAsBytes() {
-        return getBytes();
-    }
-
-    public byte[] getBytes() {
-        int len = getInt();
-        if (len < 0) {
-            throw new BufferException("Bad item length: " + len);
-        }
-        ensureAvailable(len);
-        byte[] b = new byte[len];
-        getRawBytes(b);
-        return b;
-    }
-
-    public void getRawBytes(byte[] buf) {
-        getRawBytes(buf, 0, buf.length);
-    }
-
-    public PublicKey getPublicKey() throws SshException {
-        return getPublicKey(BufferPublicKeyParser.DEFAULT);
-    }
-
-    /**
-     * @param parser A {@link BufferPublicKeyParser} to extract the key from the buffer
-     * - never {@code null}
-     * @return The extracted {@link PublicKey} - may be {@code null} if the parser so decided
-     * @throws SshException If failed to extract the key
-     * @see #getRawPublicKey(BufferPublicKeyParser)
-     */
-    public PublicKey getPublicKey(BufferPublicKeyParser<? extends PublicKey> parser) throws SshException {
-        int ow = wpos();
-        int len = getInt();
-        wpos(rpos() + len);
-        try {
-            return getRawPublicKey(parser);
-        } finally {
-            wpos(ow);
-        }
-    }
-
-    public PublicKey getRawPublicKey() throws SshException {
-        return getRawPublicKey(BufferPublicKeyParser.DEFAULT);
-    }
-
-    /**
-     * @param parser A {@link BufferPublicKeyParser} to extract the key from the buffer
-     * - never {@code null}
-     * @return The extracted {@link PublicKey} - may be {@code null} if the parser so decided
-     * @throws SshException If failed to extract the key
-     */
-    public PublicKey getRawPublicKey(BufferPublicKeyParser<? extends PublicKey> parser) throws SshException {
-        Objects.requireNonNull(parser, "No key data parser");
-        try {
-            String keyType = getString();
-            if (!parser.isKeyTypeSupported(keyType)) {
-                throw new NoSuchAlgorithmException("Key type=" + keyType + ") not supported by parser=" + parser);
-            }
-
-            return parser.getRawPublicKey(keyType, this);
-        } catch (GeneralSecurityException e) {
-            throw new SshException(e);
-        }
-    }
-
-    public KeyPair getKeyPair() throws SshException {
-        try {
-            final PublicKey pub;
-            final PrivateKey prv;
-            final String keyAlg = getString();
-            if (KeyPairProvider.SSH_RSA.equals(keyAlg)) {
-                BigInteger e = getMPInt();
-                BigInteger n = getMPInt();
-                BigInteger d = getMPInt();
-                BigInteger qInv = getMPInt();
-                BigInteger q = getMPInt();
-                BigInteger p = getMPInt();
-                BigInteger dP = d.remainder(p.subtract(BigInteger.valueOf(1)));
-                BigInteger dQ = d.remainder(q.subtract(BigInteger.valueOf(1)));
-                KeyFactory keyFactory = SecurityUtils.getKeyFactory(KeyUtils.RSA_ALGORITHM);
-                pub = keyFactory.generatePublic(new RSAPublicKeySpec(n, e));
-                prv = keyFactory.generatePrivate(new RSAPrivateCrtKeySpec(n, e, d, p, q, dP, dQ, qInv));
-            } else if (KeyPairProvider.SSH_DSS.equals(keyAlg)) {
-                BigInteger p = getMPInt();
-                BigInteger q = getMPInt();
-                BigInteger g = getMPInt();
-                BigInteger y = getMPInt();
-                BigInteger x = getMPInt();
-                KeyFactory keyFactory = SecurityUtils.getKeyFactory(KeyUtils.DSS_ALGORITHM);
-                pub = keyFactory.generatePublic(new DSAPublicKeySpec(y, p, q, g));
-                prv = keyFactory.generatePrivate(new DSAPrivateKeySpec(x, p, q, g));
-            } else if (KeyPairProvider.SSH_ED25519.equals(keyAlg)) {
-                return SecurityUtils.extractEDDSAKeyPair(this, keyAlg);
-            } else {
-                ECCurves curve = ECCurves.fromKeyType(keyAlg);
-                if (curve == null) {
-                    throw new NoSuchAlgorithmException("Unsupported key pair algorithm: " + keyAlg);
-                }
-                String curveName = curve.getName();
-                ECParameterSpec params = curve.getParameters();
-                return extractEC(curveName, params);
-            }
-
-            return new KeyPair(pub, prv);
-        } catch (GeneralSecurityException e) {
-            throw new SshException(e);
-        }
-    }
-
-    protected KeyPair extractEC(String expectedCurveName, ECParameterSpec spec) throws GeneralSecurityException {
-        String curveName = getString();
-        if (!expectedCurveName.equals(curveName)) {
-            throw new InvalidKeySpecException("extractEC(" + expectedCurveName + ") mismatched curve name: " + curveName);
-        }
-
-        byte[] groupBytes = getBytes();
-        BigInteger exponent = getMPInt();
-
-        if (spec == null) {
-            throw new InvalidKeySpecException("extractEC(" + expectedCurveName + ") missing parameters for curve");
-        }
-
-        ECPoint group;
-        try {
-            group = ECCurves.octetStringToEcPoint(groupBytes);
-        } catch (RuntimeException e) {
-            throw new InvalidKeySpecException("extractEC(" + expectedCurveName + ")"
-                    + " failed (" + e.getClass().getSimpleName() + ")"
-                    + " to decode EC group for curve: " + e.getMessage(),
-                    e);
-        }
-
-        KeyFactory keyFactory = SecurityUtils.getKeyFactory(KeyUtils.EC_ALGORITHM);
-        PublicKey pubKey = keyFactory.generatePublic(new ECPublicKeySpec(group, spec));
-        PrivateKey privKey = keyFactory.generatePrivate(new ECPrivateKeySpec(exponent, spec));
-        return new KeyPair(pubKey, privKey);
-    }
-
-    public void ensureAvailable(int reqLen) throws BufferException {
-        int availLen = available();
-        if (availLen < reqLen) {
-            throw new BufferException("Underflow: requested=" + reqLen + ", available=" + availLen);
-        }
-    }
-
-    /*======================
-       Write methods
-     ======================*/
-
-    public void putByte(byte b) {
-        ensureCapacity(Byte.BYTES);
-        workBuf[0] = b;
-        putRawBytes(workBuf, 0, Byte.BYTES);
-    }
-
-    public void putBuffer(Readable buffer) {
-        putBuffer(buffer, true);
-    }
-
-    public abstract int putBuffer(Readable buffer, boolean expand);
-
-    public abstract void putBuffer(ByteBuffer buffer);
-
-    /**
-     * Writes 16 bits
-     *
-     * @param i The 16-bit value
-     */
-    public void putShort(int i) {
-        ensureCapacity(Short.BYTES);
-        workBuf[0] = (byte) (i >> 8);
-        workBuf[1] = (byte) i;
-        putRawBytes(workBuf, 0, Short.BYTES);
-    }
-
-    /**
-     * Writes 32 bits
-     *
-     * @param i The 32-bit value
-     */
-    public void putInt(long i) {
-        BufferUtils.validateInt32Value(i, "Invalid 32-bit value: %d");
-        ensureCapacity(Integer.BYTES);
-        BufferUtils.putUInt(i, workBuf, 0, Integer.BYTES);
-        putRawBytes(workBuf, 0, Integer.BYTES);
-    }
-
-    /**
-     * Writes 64 bits
-     *
-     * @param i The 64-bit value
-     */
-    public void putLong(long i) {
-        ensureCapacity(Long.BYTES);
-        workBuf[0] = (byte) (i >> 56);
-        workBuf[1] = (byte) (i >> 48);
-        workBuf[2] = (byte) (i >> 40);
-        workBuf[3] = (byte) (i >> 32);
-        workBuf[4] = (byte) (i >> 24);
-        workBuf[5] = (byte) (i >> 16);
-        workBuf[6] = (byte) (i >> 8);
-        workBuf[7] = (byte) i;
-        putRawBytes(workBuf, 0, Long.BYTES);
-    }
-
-    public void putBoolean(boolean b) {
-        putByte(b ? (byte) 1 : (byte) 0);
-    }
-
-    /**
-     * Adds the bytes to the buffer and wipes the data from the
-     * input buffer <U>after</U> having added it - useful for sensitive
-     * information such as password
-     *
-     * @param b The buffer to add - OK if {@code null}
-     */
-    public void putAndWipeBytes(byte[] b) {
-        putAndWipeBytes(b, 0, NumberUtils.length(b));
-    }
-
-    public void putAndWipeBytes(byte[] b, int off, int len) {
-        putBytes(b, off, len);
-
-        for (int pos = off, index = 0; index < len; pos++, index++) {
-            b[pos] = (byte) 0;
-        }
-    }
-
-    public void putBytes(byte[] b) {
-        putBytes(b, 0, NumberUtils.length(b));
-    }
-
-    public void putBytes(byte[] b, int off, int len) {
-        putInt(len);
-        putRawBytes(b, off, len);
-    }
-
-    /**
-     * Encodes the {@link Objects#toString(Object, String) toString} value of each member.
-     *
-     * @param objects       The objects to be encoded in the buffer - OK if
-     *                      {@code null}/empty
-     * @param prependLength If {@code true} then the list is preceded by
-     *                      a 32-bit count of the number of members in the list
-     * @see #putStringList(Collection, Charset, boolean)
-     */
-    public void putStringList(Collection<?> objects, boolean prependLength) {
-        putStringList(objects, StandardCharsets.UTF_8, prependLength);
-    }
-
-    /**
-     * Encodes the {@link Objects#toString(Object, String) toString} value of each member
-     *
-     * @param objects       The objects to be encoded in the buffer - OK if
-     *                      {@code null}/empty
-     * @param charset       The {@link Charset} to use for encoding
-     * @param prependLength If {@code true} then the list is preceded by
-     *                      a 32-bit count of the number of members in the list
-     * @see #putString(String, Charset)
-     */
-    public void putStringList(Collection<?> objects, Charset charset, boolean prependLength) {
-        int numObjects = GenericUtils.size(objects);
-        if (prependLength) {
-            putInt(numObjects);
-        }
-
-        if (numObjects <= 0) {
-            return;
-        }
-
-        objects.forEach(o -> putString(Objects.toString(o, null), charset));
-    }
-
-    public void putString(String string) {
-        putString(string, StandardCharsets.UTF_8);
-    }
-
-    public void putString(String string, Charset charset) {
-        if (GenericUtils.isEmpty(string)) {
-            putBytes(GenericUtils.EMPTY_BYTE_ARRAY);
-        } else {
-            putBytes(string.getBytes(charset));
-        }
-    }
-
-    /**
-     * Zeroes the input array <U>after</U> having put the characters in the
-     * buffer - useful for sensitive information such as passwords
-     *
-     * @param chars The characters to put in the buffer - may be {@code null}/empty
-     * @see #putAndWipeChars(char[], Charset)
-     * @see #putChars(char[], Charset)
-     */
-    public void putAndWipeChars(char[] chars) {
-        putAndWipeChars(chars, 0, GenericUtils.length(chars));
-    }
-
-    public void putAndWipeChars(char[] chars, int offset, int len) {
-        putAndWipeChars(chars, offset, len, StandardCharsets.UTF_8);
-    }
-
-    public void putAndWipeChars(char[] chars, Charset charset) {
-        putAndWipeChars(chars, 0, GenericUtils.length(chars), charset);
-    }
-
-    public void putAndWipeChars(char[] chars, int offset, int len, Charset charset) {
-        putChars(chars, offset, len, charset);
-        for (int pos = offset, index = 0; index < len; index++, pos++) {
-            chars[pos] = '\0';
-        }
-    }
-
-    public void putChars(char[] chars) {
-        putChars(chars, 0, GenericUtils.length(chars));
-    }
-
-    public void putChars(char[] chars, int offset, int len) {
-        putChars(chars, offset, len, StandardCharsets.UTF_8);
-    }
-
-    public void putChars(char[] chars, Charset charset) {
-        putChars(chars, 0, GenericUtils.length(chars), charset);
-    }
-
-    public void putChars(char[] chars, int offset, int len, Charset charset) {
-        if (len <= 0) {
-            putBytes(GenericUtils.EMPTY_BYTE_ARRAY);
-        } else {
-            putBuffer(charset.encode(CharBuffer.wrap(chars, offset, len)));
-        }
-    }
-
-    public void putMPInt(BigInteger bi) {
-        putMPInt(bi.toByteArray());
-    }
-
-    public void putMPInt(byte[] foo) {
-        if ((foo[0] & 0x80) != 0) {
-            putInt(foo.length + 1 /* padding */);
-            putByte((byte) 0);
-        } else {
-            putInt(foo.length);
-        }
-        putRawBytes(foo);
-    }
-
-    public void putRawBytes(byte[] d) {
-        putRawBytes(d, 0, d.length);
-    }
-
-    public abstract void putRawBytes(byte[] d, int off, int len);
-
-    public void putPublicKey(PublicKey key) {
-        int ow = wpos();
-        putInt(0);
-        int ow1 = wpos();
-        putRawPublicKey(key);
-        int ow2 = wpos();
-        wpos(ow);
-        putInt(ow2 - ow1);
-        wpos(ow2);
-    }
-
-    public void putRawPublicKey(PublicKey key) {
-        Objects.requireNonNull(key, "No key");
-        if (key instanceof RSAPublicKey) {
-            RSAPublicKey rsaPub = (RSAPublicKey) key;
-
-            putString(KeyPairProvider.SSH_RSA);
-            putMPInt(rsaPub.getPublicExponent());
-            putMPInt(rsaPub.getModulus());
-        } else if (key instanceof DSAPublicKey) {
-            DSAPublicKey dsaPub = (DSAPublicKey) key;
-            DSAParams dsaParams = dsaPub.getParams();
-
-            putString(KeyPairProvider.SSH_DSS);
-            putMPInt(dsaParams.getP());
-            putMPInt(dsaParams.getQ());
-            putMPInt(dsaParams.getG());
-            putMPInt(dsaPub.getY());
-        } else if (key instanceof ECPublicKey) {
-            ECPublicKey ecKey = (ECPublicKey) key;
-            ECParameterSpec ecParams = ecKey.getParams();
-            ECCurves curve = ECCurves.fromCurveParameters(ecParams);
-            if (curve == null) {
-                throw new BufferException("Unsupported EC curve parameters");
-            }
-
-            putString(curve.getKeyType());
-            putString(curve.getName());
-            putBytes(ECCurves.encodeECPoint(ecKey.getW(), ecParams));
-        } else if (SecurityUtils.EDDSA.equals(key.getAlgorithm())) {
-            SecurityUtils.putRawEDDSAPublicKey(this, key);
-        } else {
-            throw new BufferException("Unsupported raw public key algorithm: " + key.getAlgorithm());
-        }
-    }
-
-    public void putKeyPair(KeyPair kp) {
-        PublicKey pubKey = kp.getPublic();
-        PrivateKey prvKey = kp.getPrivate();
-        if (prvKey instanceof RSAPrivateCrtKey) {
-            RSAPublicKey rsaPub = (RSAPublicKey) pubKey;
-            RSAPrivateCrtKey rsaPrv = (RSAPrivateCrtKey) prvKey;
-
-            putString(KeyPairProvider.SSH_RSA);
-            putMPInt(rsaPub.getPublicExponent());
-            putMPInt(rsaPub.getModulus());
-            putMPInt(rsaPrv.getPrivateExponent());
-            putMPInt(rsaPrv.getCrtCoefficient());
-            putMPInt(rsaPrv.getPrimeQ());
-            putMPInt(rsaPrv.getPrimeP());
-        } else if (pubKey instanceof DSAPublicKey) {
-            DSAPublicKey dsaPub = (DSAPublicKey) pubKey;
-            DSAParams dsaParams = dsaPub.getParams();
-            DSAPrivateKey dsaPrv = (DSAPrivateKey) prvKey;
-
-            putString(KeyPairProvider.SSH_DSS);
-            putMPInt(dsaParams.getP());
-            putMPInt(dsaParams.getQ());
-            putMPInt(dsaParams.getG());
-            putMPInt(dsaPub.getY());
-            putMPInt(dsaPrv.getX());
-        } else if (pubKey instanceof ECPublicKey) {
-            ECPublicKey ecPub = (ECPublicKey) pubKey;
-            ECPrivateKey ecPriv = (ECPrivateKey) prvKey;
-            ECParameterSpec ecParams = ecPub.getParams();
-            ECCurves curve = ECCurves.fromCurveParameters(ecParams);
-            if (curve == null) {
-                throw new BufferException("Unsupported EC curve parameters");
-            }
-
-            putString(curve.getKeyType());
-            putString(curve.getName());
-            putBytes(ECCurves.encodeECPoint(ecPub.getW(), ecParams));
-            putMPInt(ecPriv.getS());
-        } else if (SecurityUtils.EDDSA.equals(pubKey.getAlgorithm())) {
-            SecurityUtils.putEDDSAKeyPair(this, pubKey, prvKey);
-        } else {
-            throw new BufferException("Unsupported key pair algorithm: " + pubKey.getAlgorithm());
-        }
-    }
-
-    protected void ensureCapacity(int capacity) {
-        ensureCapacity(capacity, BufferUtils.DEFAULT_BUFFER_GROWTH_FACTOR);
-    }
-
-    /**
-     * @param capacity     The requires capacity
-     * @param growthFactor An {@link IntUnaryOperator} that is invoked
-     *                     if the current capacity is insufficient. The argument is the minimum
-     *                     required new data length, the function result should be the
-     *                     effective new data length to be allocated - if less than minimum
-     *                     then an exception is thrown
-     */
-    public abstract void ensureCapacity(int capacity, IntUnaryOperator growthFactor);
-
-    protected abstract int size();
-
-    @Override
-    public String toString() {
-        return "Buffer [rpos=" + rpos() + ", wpos=" + wpos() + ", size=" + size() + "]";
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/BufferException.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/BufferException.java b/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/BufferException.java
deleted file mode 100644
index 93f4e6b..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/BufferException.java
+++ /dev/null
@@ -1,30 +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.buffer;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class BufferException extends RuntimeException {
-    private static final long serialVersionUID = 658645233475011039L;
-
-    public BufferException(String message) {
-        super(message);
-    }
-}
\ No newline at end of file


[28/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common utilities code from sshd-core into sshd-common (new artifact)

Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/client/config/keys/id_ecdsa
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/client/config/keys/id_ecdsa b/sshd-common/src/test/resources/org/apache/sshd/client/config/keys/id_ecdsa
new file mode 100644
index 0000000..31b1268
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/client/config/keys/id_ecdsa
@@ -0,0 +1,5 @@
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIPKmiQzAASg656IP4PuuElLdLdO/MIXrGxQG6tGkKZ1HoAoGCCqGSM49
+AwEHoUQDQgAEobHtw9wkL332ep9fi8Gw5g8sEGwslNonPUCDR6YUZ9mjOehliLpF
+DLHLxlIFafrVM+LIpagjpRKZcnpGPWQDnA==
+-----END EC PRIVATE KEY-----

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/client/config/keys/id_rsa
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/client/config/keys/id_rsa b/sshd-common/src/test/resources/org/apache/sshd/client/config/keys/id_rsa
new file mode 100644
index 0000000..afc6aa8
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/client/config/keys/id_rsa
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEoQIBAAKCAQEAxr3N5fkt966xJINl0hH7Q6lLDRR1D0yMjcXCE5roE9VFut2c
+tGFuo90TCOxkPOMnwzwConeyScVF4ConZeWsxbG9VtRh61IeZ6R5P5ZTvE9xPdZB
+gIEWvU1bRfrrOfSMihqF98pODspE6NoTtND2eglwSGwxcYFmpdTAmu+8qgxgGxlE
+aaCjqwdiNPZhygrH81Mv2ruolNeZkn4Bj+wFFmZTD/waN1pQaMf+SO1+kEYIYFNl
+5+8JRGuUcr8MhHHJB+gwqMTF2BSBVITJzZUiQR0TMtkK6Vbs7yt1F9hhzDzAFDwh
+V+rsfNQaOHpl3zP07qH+/99A0XG1CVcEdHqVMwIBIwKCAQALW02YHN4OJz1Siypj
+xoNi87slUaBKBF/NlkWauGUIcpZFMTwnkIn6vCz5MhRbQC4oadRDzFNUrC/g7HdH
+prlqYe2P7uEGIfMb3YNFdk3tgOHmRsHqFgFMpVWsOjlTxNTUsQ74N3Isuxnha4wY
+9f90sBULc+WRdRvO9jbkSDaqoYVKAqCFWtocL+ZWwBXWrIrsQW4PElgZ/duc5DX7
+eeJ5DXCSj9dO+1KxsWEOKaoeABEegrRVys1/shcDNPhf/p0QShKIdPcpnDUc8cny
+1bq8GSt6jEQ+tuRoSnYrY+RD+mlkHrx373Xc1a9woV+QKTThmd9TQ8gzHMHNqq0a
+7kR7AoGBAOuPOTRiKaDtQyMTEX7eeHsPNE24EgvONjNpxyQ6gKGthG5SiB0IO7mP
+r7EggbR2EY8eMCY5HjvxzxgH86n2Pqbzsr6UlQq7YTPupCm/7fPgRknu917GA20f
+1cuY8B04Jp4FIGryBmCcScX6usXXhjfAvYCWWfkSytA8gX9+b1TNAoGBANf8shbp
+wRnQfgAzw2S+xs29pdwa6Jb/xuLvHSyklmgidrK4nsVI8G+zeCqwkqkNM02sM+vR
+c8EX7+myrGf+S2V3JS3AMNXEhavrWVH0CuqFHlBjSwHZ0uKuPpWHlCnud+23AdQz
+Bf1H7tYKt5es3J/B37o4YxhAL6U9qq+ewZH/AoGBAOTURjLjA94oT9jt870SoOyS
+bVLQEYfPol3UeE8UQnEsN4Ec+UDGK2PNaNfzsTL2WjNB5aF5UJIA184znD60seQC
+raMxQFORdF5ViYekgMEFwJ+XrnlSpD4e7PGqgtqOUWZOH33VKsRADSa5DTU3xDYo
+8porp9wDoHKD64MqXYWTAoGADFeVJeF4v6WDivukw+2k9dBSntz3WRXuG5iilNY0
+evqnsnDzITdeMkTFCcDycA9iBHA9ezCKRYxW2id3kOn1rgbO7KvliI7jESNkMJGa
+OUlvck7RFgxyc1pp+ep9fr0rbKtfMLJ1Xu4q56jXSn7oCSEFeFr+WSg9PKRwJ0rm
+fV8CgYAkYOiNY8jH5lwwoPWOIPJg62zdzmRQktrU3z7Nzk5udN6JnG3g57QjkozX
+AgHARKQ2MuXW9OfOnYNhbGeavcBQmg5XUx3eL4PRFw8mFZdjpBD/kM/dfCEwEta3
+FpRlVGn0RNqVV5xxClObD/CikkDqKZG4MSj3CrO3JK33gl1Lgg==
+-----END RSA PRIVATE KEY-----

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-DSA-KeyPair
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-DSA-KeyPair b/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-DSA-KeyPair
new file mode 100644
index 0000000..217d508
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-DSA-KeyPair
@@ -0,0 +1,21 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABsQAAAAdzc2gtZH
+NzAAAAgQD3axy1MBdh4TJC+T22DKKDe7sXnDrgJRs1jBcDl/qPdQDoLlvvTd8oHggXkIzI
+6Tx2ldiJ3KADMkDg1sBSU5zenIr9wisfEQOLNqfIOOirR9Z8jULGVlDO+pPwps0P9bcOka
+JCAfABNth6Oaz3NJjfFD8ZmmWDVaN2rSi1J3mjnwAAABUA5be00MaSti06Hor265V8Ggtj
+i/0AAACAa9MU8kBJ4Z1+UsrWruVr72eckEwjOPcRFeA6MoSM70GGdYVkLyQ78MMjKAFZcb
+seYT3lsja+c4LQ7yErROHSGFo+ImQnYGY3Cos2mS8tRBHtZ4YmKsBmS3Zrb4lEfauA1pgD
+pcGrUAavniKIAB/C9cpvj+IpZ36+rHpap4JKJ7kAAACAQP4NRzjtauzrDJvs73c0DPczAk
+LAan+90ouvIuI0CmMxFrSqLIIEaX3cB/P4dkKtBsMh0CxwJpoXfn+TO+1j7i08GuMONeqB
+3lOHn1/MB2qUAZh/kdU8RUsxWkrG80JMeP1kew1sHaoRfOUZ8+Xw/RL7nNmhhmHJR3rkgR
+8EoWEAAAHoMgIkejICJHoAAAAHc3NoLWRzcwAAAIEA92sctTAXYeEyQvk9tgyig3u7F5w6
+4CUbNYwXA5f6j3UA6C5b703fKB4IF5CMyOk8dpXYidygAzJA4NbAUlOc3pyK/cIrHxEDiz
+anyDjoq0fWfI1CxlZQzvqT8KbND/W3DpGiQgHwATbYejms9zSY3xQ/GZplg1Wjdq0otSd5
+o58AAAAVAOW3tNDGkrYtOh6K9uuVfBoLY4v9AAAAgGvTFPJASeGdflLK1q7la+9nnJBMIz
+j3ERXgOjKEjO9BhnWFZC8kO/DDIygBWXG7HmE95bI2vnOC0O8hK0Th0hhaPiJkJ2BmNwqL
+NpkvLUQR7WeGJirAZkt2a2+JRH2rgNaYA6XBq1AGr54iiAAfwvXKb4/iKWd+vqx6WqeCSi
+e5AAAAgED+DUc47Wrs6wyb7O93NAz3MwJCwGp/vdKLryLiNApjMRa0qiyCBGl93Afz+HZC
+rQbDIdAscCaaF35/kzvtY+4tPBrjDjXqgd5Th59fzAdqlAGYf5HVPEVLMVpKxvNCTHj9ZH
+sNbB2qEXzlGfPl8P0S+5zZoYZhyUd65IEfBKFhAAAAFGL9BNpShiQnNZ1noW8Woxyq1GuH
+AAAADnJvb3RAdWJ1bnR1LTE1AQIDBAU=
+-----END OPENSSH PRIVATE KEY-----
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-DSA-KeyPair.pub
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-DSA-KeyPair.pub b/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-DSA-KeyPair.pub
new file mode 100644
index 0000000..c3feece
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-DSA-KeyPair.pub
@@ -0,0 +1 @@
+ssh-dss AAAAB3NzaC1kc3MAAACBAPdrHLUwF2HhMkL5PbYMooN7uxecOuAlGzWMFwOX+o91AOguW+9N3ygeCBeQjMjpPHaV2IncoAMyQODWwFJTnN6civ3CKx8RA4s2p8g46KtH1nyNQsZWUM76k/CmzQ/1tw6RokIB8AE22Ho5rPc0mN8UPxmaZYNVo3atKLUneaOfAAAAFQDlt7TQxpK2LToeivbrlXwaC2OL/QAAAIBr0xTyQEnhnX5Sytau5WvvZ5yQTCM49xEV4DoyhIzvQYZ1hWQvJDvwwyMoAVlxux5hPeWyNr5zgtDvIStE4dIYWj4iZCdgZjcKizaZLy1EEe1nhiYqwGZLdmtviUR9q4DWmAOlwatQBq+eIogAH8L1ym+P4ilnfr6selqngkonuQAAAIBA/g1HOO1q7OsMm+zvdzQM9zMCQsBqf73Si68i4jQKYzEWtKosggRpfdwH8/h2Qq0GwyHQLHAmmhd+f5M77WPuLTwa4w416oHeU4efX8wHapQBmH+R1TxFSzFaSsbzQkx4/WR7DWwdqhF85Rnz5fD9Evuc2aGGYclHeuSBHwShYQ== root@ubuntu-15
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ECDSA-KeyPair
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ECDSA-KeyPair b/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ECDSA-KeyPair
new file mode 100644
index 0000000..403ff40
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ECDSA-KeyPair
@@ -0,0 +1,12 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAArAAAABNlY2RzYS
+1zaGEyLW5pc3RwNTIxAAAACG5pc3RwNTIxAAAAhQQAGRPv5eCmd3jFTCWrioWVHgQhHn/d
+ir8nriiEonZDPP+hEjX1AiYyahfvFWoqKI4lKRzoEmF5Wk6ct+9LM0JFGcEAck7Z3J/NXt
+CnHeEvnusHMoANjhKLExBURROOOTGziyHMuGBMBIgRFnf4rBhiTzduexJnaMglyqxIrDpG
+hOxwhQAAAAEQxPcsaMT3LGgAAAATZWNkc2Etc2hhMi1uaXN0cDUyMQAAAAhuaXN0cDUyMQ
+AAAIUEABkT7+Xgpnd4xUwlq4qFlR4EIR5/3Yq/J64ohKJ2Qzz/oRI19QImMmoX7xVqKiiO
+JSkc6BJheVpOnLfvSzNCRRnBAHJO2dyfzV7Qpx3hL57rBzKADY4SixMQVEUTjjkxs4shzL
+hgTASIERZ3+KwYYk83bnsSZ2jIJcqsSKw6RoTscIUAAAAAQQ+HCwVtvFlnRydGXZ+xpyKM
+KxDp5h7YMg5/dpRFrp3qNonm5/RHoT2Hw9i5GZtrXT2xPiR69wLOzTb4pnIWlENfAAAADn
+Jvb3RAdWJ1bnR1LTE1AQIDBAU=
+-----END OPENSSH PRIVATE KEY-----
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ECDSA-KeyPair.pub
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ECDSA-KeyPair.pub b/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ECDSA-KeyPair.pub
new file mode 100644
index 0000000..bd43737
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ECDSA-KeyPair.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAAZE+/l4KZ3eMVMJauKhZUeBCEef92KvyeuKISidkM8/6ESNfUCJjJqF+8VaioojiUpHOgSYXlaTpy370szQkUZwQByTtncn81e0Kcd4S+e6wcygA2OEosTEFRFE445MbOLIcy4YEwEiBEWd/isGGJPN257EmdoyCXKrEisOkaE7HCFAA== root@ubuntu-15

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ED25519-KeyPair
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ED25519-KeyPair b/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ED25519-KeyPair
new file mode 100644
index 0000000..3176f3b
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ED25519-KeyPair
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACB0Tsaj0scs8fYKMlnz2Mncky545NoICP9eGMGIpo5G3QAAAJjCVtyJwlbc
+iQAAAAtzc2gtZWQyNTUxOQAAACB0Tsaj0scs8fYKMlnz2Mncky545NoICP9eGMGIpo5G3Q
+AAAEDjQpuV2OWHZVy7R09w6bw2DnBa1UdZrsAmQ7dPyxasx3ROxqPSxyzx9goyWfPYydyT
+Lnjk2ggI/14YwYimjkbdAAAADnJvb3RAdWJ1bnR1LTE1AQIDBAUGBw==
+-----END OPENSSH PRIVATE KEY-----
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ED25519-KeyPair.pub
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ED25519-KeyPair.pub b/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ED25519-KeyPair.pub
new file mode 100644
index 0000000..128e883
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ED25519-KeyPair.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHROxqPSxyzx9goyWfPYydyTLnjk2ggI/14YwYimjkbd root@ubuntu-15
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-RSA-KeyPair
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-RSA-KeyPair b/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-RSA-KeyPair
new file mode 100644
index 0000000..84ee354
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-RSA-KeyPair
@@ -0,0 +1,49 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAACFwAAAAdzc2gtcn
+NhAAAAAwEAAQAAAgEArsY72FUUnK8yLqaY3WkotvMFm2aryoVVPbaHiXMqmKTTQPBFshwe
+Rk+wEJBE7th4fIq2MuTu+hMxEQ8+oYJ47Ka1+F96QE1F0uu1Tuec/ZpeuEiXsTBApWZ5Sa
+AMqQ3gMflfqgp96ipV3SOpLyEG9GIqVMJYNE/ggwIV7Uc4ybC21Zy3I7QOfHho8BeaMjV/
+703rlu2UWs8Twjo7VMWLvnGMUjNMzNGql9WouLEzHshmkUZBRdKvnTjsT9vx1q5OycaH/g
+Hos23EbNzn50rbiIm93PcZ/otGBjjfbHcsRmjxZhqCzLss5Nr93PLIePN8Cez91S7aLjCw
+Ri4ugKxK0EZwNHnHPi0yR0eI1eMfJLttdYnrOZiSxEVbCsZiDfTybuBYUDaH2EtgmzH9N2
+5IZeJOjrjE68NZhiwXnaIoHvfpkPTkWMSGLsLY3Kg0dYwUbAy9ErqkS/MEMmN70OxIxqTI
+hFSZ0bv3FBbZTZnl/GAEWmV+KSjURKuJlS9EBYE4ZvzYhGi6K7g3IQJZwrcqy6s8u24r5u
+qAQCihfuWFabq4nSYB1zczQ7Qq+V8wemak6lgoFGq4f58k3FUhkJbwQRFJoQYvaMI63dls
+9q67bkocjycsGPvWDU7IE9dVs36z7FevlQjsoHwAr4+0T1PFsqHn++KJ0p3AlopIV7ObS0
+cAAAdIyEoveMhKL3gAAAAHc3NoLXJzYQAAAgEArsY72FUUnK8yLqaY3WkotvMFm2aryoVV
+PbaHiXMqmKTTQPBFshweRk+wEJBE7th4fIq2MuTu+hMxEQ8+oYJ47Ka1+F96QE1F0uu1Tu
+ec/ZpeuEiXsTBApWZ5SaAMqQ3gMflfqgp96ipV3SOpLyEG9GIqVMJYNE/ggwIV7Uc4ybC2
+1Zy3I7QOfHho8BeaMjV/703rlu2UWs8Twjo7VMWLvnGMUjNMzNGql9WouLEzHshmkUZBRd
+KvnTjsT9vx1q5OycaH/gHos23EbNzn50rbiIm93PcZ/otGBjjfbHcsRmjxZhqCzLss5Nr9
+3PLIePN8Cez91S7aLjCwRi4ugKxK0EZwNHnHPi0yR0eI1eMfJLttdYnrOZiSxEVbCsZiDf
+TybuBYUDaH2EtgmzH9N25IZeJOjrjE68NZhiwXnaIoHvfpkPTkWMSGLsLY3Kg0dYwUbAy9
+ErqkS/MEMmN70OxIxqTIhFSZ0bv3FBbZTZnl/GAEWmV+KSjURKuJlS9EBYE4ZvzYhGi6K7
+g3IQJZwrcqy6s8u24r5uqAQCihfuWFabq4nSYB1zczQ7Qq+V8wemak6lgoFGq4f58k3FUh
+kJbwQRFJoQYvaMI63dls9q67bkocjycsGPvWDU7IE9dVs36z7FevlQjsoHwAr4+0T1PFsq
+Hn++KJ0p3AlopIV7ObS0cAAAADAQABAAACAEC2ca42Qn2JeSFA2lUQ/NxQu4DA0VcZa2iA
+T0sbc2g1j2r6DDRYqPULyHs5j+yIBoTGr5PEpJ+/v2k7pcsTjkbBq5sdbxyj//iLAgUHSV
++1auD9L/2Rij4z5TDXrBhkZODcHwnM/LZZmpVZAOnMZtDSEC52D0a/VLfta8UBnAtB/VC2
+yDDWGI8J3oEXnCZ/HLG0vkiSC15cUkqWWSWYR5fqXzSIfiVQdMPnfqN0KSN6vE+KHd0Gl7
+O1QfqHy9HblJytf+kTN+BIAnT0joFbfFHf/ob0j5FqbDEdmcyFi8U+UjOmGUxI+eR7GYhX
+9NakAQeD0rC+ulZj/MDflKRcJw6+H/wHn2pdhahZY1A644SFin4SliFKl57njRkY/psjSg
+jpP0Nneg4+YFyFk9iUnmzxStJwuGE48I5eRESTFKgaMce5IVGOpz/YchQ9AL+ll2Qy6JfN
+nhpjuZD6Zh5KwsuLtCbaGfu8K8bbMEZg8F7jU6UdbpLmRLroKt6yFhffEp/tRrsX2hFoMt
+qCMpTEX2G+EzItPfFzq3/bFtnMxHmDiu4P8J03ALza/0OSIa4bdqMYG+97VBMbMeT6U76Q
+iYoDJH5/cdCBO/Cx+elM7oyFOFaV928h+6p01LGywcQdsgFPDyHSjXnF9t1fnLN4vhz5vU
+9QiVY7ldr+3PfxxpnRAAABAQDI87mazAnKsSJtkl70etg7vexltSxqkbhV/Hd5TYVMn19v
+U6rIjCY7q2xlHaUJdDNggwMpU679amkRP5BFPk9G4u8Xf5MyyDQpv+Hgpl7H0B1qEeJsBs
+huxURrX3EhLmb3300onEZXvlRwKOelIoVx5RZLUPhY0SbjBgH4sbO9NlYzs8xp4nLyK/jq
+DsqzmkkoQvK8zMybs3JwtGmTjDHvzmgcBgpm+s9gXyDJn3xe4CBW7v92N5/7eUehdXwomL
+4eDs/gR9QRb71bBSv9UaF5nK/TGJlysiM71JhBa2lmXlWg7KhALAu3SdTxxdvw0oyY+q6F
+E8zC0ljNMId8imVoAAABAQDi0m1WAW02b/IqwsJz0CAJXGpvhnhTSak3muZhTM0LPoavIe
+ow9KYL5Or4kQvbQXraYcaaRTp1Q9yJOrnXszAUWCGzqe38bLsI/SoRynLQaGEhdy1gr4tP
+WFKw0M34AshzyauEwHAVJIm6KZW/LgHsrxSXPFAABNQkQtB4olokNgSb/bysgo06OkkMBr
+N8mfY40m90RjTIvg3Cwei7xmna67MO1Bxzis8c7qQlf4hshFwnp+ixeLuhteYG1/DGxkfz
+yZ4UYXVKiuq3Xq+jFpYbKJeU17HL5TQFX34KCMN0BsMCcHLJ222IZkvFZj50715Z+jh/Vp
+xa6et++PX26BnVAAABAQDFQc26F12qnGA4heqa7Ig9WspGLEB4x5+cANOKaT/LtUFYWaYd
+O7Meeijkk+C1XcoOMac62QysRmjk8JEWdxgLiCFuKLp+6vxZLmEXBrBF0guHGv4/HlbpYn
+CxpMenxr0dhhwd2Flr1VtiadzEAfnc6MZE4byU8vVssYXORpVYIU46uI2OzIalnBhTXJYW
+KuamE/R+O1+dqnGgevQqUBkA5p4k8w+RMEY3hvSuNd6TFy5uoLXkCyOOpo60fGFVqMZyvJ
+IgU1et0umOA/JHrGzKHAl7WibRo4kf5MrPRLiDeDu92mqpwOl9NQjuTMR0OdZcXwOdrlG+
+daAgz5LetOKrAAAADnJvb3RAdWJ1bnR1LTE1AQIDBA==
+-----END OPENSSH PRIVATE KEY-----
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-RSA-KeyPair.pub
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-RSA-KeyPair.pub b/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-RSA-KeyPair.pub
new file mode 100644
index 0000000..c59321c
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-RSA-KeyPair.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCuxjvYVRScrzIuppjdaSi28wWbZqvKhVU9toeJcyqYpNNA8EWyHB5GT7AQkETu2Hh8irYy5O76EzERDz6hgnjsprX4X3pATUXS67VO55z9ml64SJexMEClZnlJoAypDeAx+V+qCn3qKlXdI6kvIQb0YipUwlg0T+CDAhXtRzjJsLbVnLcjtA58eGjwF5oyNX/vTeuW7ZRazxPCOjtUxYu+cYxSM0zM0aqX1ai4sTMeyGaRRkFF0q+dOOxP2/HWrk7Jxof+AeizbcRs3OfnStuIib3c9xn+i0YGON9sdyxGaPFmGoLMuyzk2v3c8sh483wJ7P3VLtouMLBGLi6ArErQRnA0ecc+LTJHR4jV4x8ku211ies5mJLERVsKxmIN9PJu4FhQNofYS2CbMf03bkhl4k6OuMTrw1mGLBedoige9+mQ9ORYxIYuwtjcqDR1jBRsDL0SuqRL8wQyY3vQ7EjGpMiEVJnRu/cUFtlNmeX8YARaZX4pKNREq4mVL0QFgThm/NiEaLoruDchAlnCtyrLqzy7bivm6oBAKKF+5YVpuridJgHXNzNDtCr5XzB6ZqTqWCgUarh/nyTcVSGQlvBBEUmhBi9owjrd2Wz2rrtuShyPJywY+9YNTsgT11WzfrPsV6+VCOygfACvj7RPU8Wyoef74onSncCWikhXs5tLRw== root@ubuntu-15

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/util/security/EDDSAProviderTest-EDDSA-OpenSSH-KeyPair
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/util/security/EDDSAProviderTest-EDDSA-OpenSSH-KeyPair b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/EDDSAProviderTest-EDDSA-OpenSSH-KeyPair
new file mode 100644
index 0000000..59af379
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/EDDSAProviderTest-EDDSA-OpenSSH-KeyPair
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACB0Tsaj0scs8fYKMlnz2Mncky545NoICP9eGMGIpo5G3QAAAJjCVtyJwlbc
+iQAAAAtzc2gtZWQyNTUxOQAAACB0Tsaj0scs8fYKMlnz2Mncky545NoICP9eGMGIpo5G3Q
+AAAEDjQpuV2OWHZVy7R09w6bw2DnBa1UdZrsAmQ7dPyxasx3ROxqPSxyzx9goyWfPYydyT
+Lnjk2ggI/14YwYimjkbdAAAADnJvb3RAdWJ1bnR1LTE1AQIDBAUGBw==
+-----END OPENSSH PRIVATE KEY-----

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/util/security/EDDSAProviderTest-EDDSA-OpenSSH-KeyPair.pub
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/util/security/EDDSAProviderTest-EDDSA-OpenSSH-KeyPair.pub b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/EDDSAProviderTest-EDDSA-OpenSSH-KeyPair.pub
new file mode 100644
index 0000000..128e883
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/EDDSAProviderTest-EDDSA-OpenSSH-KeyPair.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHROxqPSxyzx9goyWfPYydyTLnjk2ggI/14YwYimjkbd root@ubuntu-15
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-DSA-KeyPair
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-DSA-KeyPair b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-DSA-KeyPair
new file mode 100644
index 0000000..1d3ef24
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-DSA-KeyPair
@@ -0,0 +1,12 @@
+-----BEGIN DSA PRIVATE KEY-----
+MIIBvAIBAAKBgQDIPyMbBuQcZxeYDOyCqqkdK37cWQvp+RpWzvieB/oiG/ykfDQX
+oZMRtwqwWTBfejNitbBBmC6G/t5OK+9aFmj7pfJ+a7fZKXfiUquIg9soDsoOindf
+2AwR6MZ3os8uiP2xrC8IQAClnETa15mFShs4a4b2VjddgCQ6tphnY97MywIVAPtr
+YyW11RIXsVTf/9KlbhYaNlt5AoGAX9JzbHykC/0xDKOyKU6xDIOVdEZ0ooAl9Psl
+BEUuNhlv2XgmQScO6C9l2W7gbbut7zIw4FaZ2/dgXa3D4IyexBVug5XMnrssErZo
+NcoF5g0dgEAsb9Hl9gkIK3VHM5kWteeUg1VE700JTtSMisdL8CgIdR+xN8iVH5Ew
+CbLWxmECgYEAtv+cdRfNevYFkp55jVqazc8zRLvfb64jzgc5oSJVc64kFs4yx+ab
+YpGX9WxNxDlG6g2WiY8voDBB0YnUJsn0kVRjBKX9OceROxrfT4K4dVbQZsdt+SLa
+XWL4lGJFrFZL3LZqvySvq6xfhJfakQDDivW4hUOhFPXPHrE5/Ia3T7ACFQCE6flG
+nmVCAbzo9YsbdJWBnxMnBA==
+-----END DSA PRIVATE KEY-----
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-DSA-KeyPair.pub
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-DSA-KeyPair.pub b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-DSA-KeyPair.pub
new file mode 100644
index 0000000..c0790ed
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-DSA-KeyPair.pub
@@ -0,0 +1 @@
+ssh-dss AAAAB3NzaC1kc3MAAACBAMg/IxsG5BxnF5gM7IKqqR0rftxZC+n5GlbO+J4H+iIb/KR8NBehkxG3CrBZMF96M2K1sEGYLob+3k4r71oWaPul8n5rt9kpd+JSq4iD2ygOyg6Kd1/YDBHoxneizy6I/bGsLwhAAKWcRNrXmYVKGzhrhvZWN12AJDq2mGdj3szLAAAAFQD7a2MltdUSF7FU3//SpW4WGjZbeQAAAIBf0nNsfKQL/TEMo7IpTrEMg5V0RnSigCX0+yUERS42GW/ZeCZBJw7oL2XZbuBtu63vMjDgVpnb92BdrcPgjJ7EFW6DlcyeuywStmg1ygXmDR2AQCxv0eX2CQgrdUczmRa155SDVUTvTQlO1IyKx0vwKAh1H7E3yJUfkTAJstbGYQAAAIEAtv+cdRfNevYFkp55jVqazc8zRLvfb64jzgc5oSJVc64kFs4yx+abYpGX9WxNxDlG6g2WiY8voDBB0YnUJsn0kVRjBKX9OceROxrfT4K4dVbQZsdt+SLaXWL4lGJFrFZL3LZqvySvq6xfhJfakQDDivW4hUOhFPXPHrE5/Ia3T7A= dsa-key-20130709
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-EC-256-KeyPair
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-EC-256-KeyPair b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-EC-256-KeyPair
new file mode 100644
index 0000000..31b1268
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-EC-256-KeyPair
@@ -0,0 +1,5 @@
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIPKmiQzAASg656IP4PuuElLdLdO/MIXrGxQG6tGkKZ1HoAoGCCqGSM49
+AwEHoUQDQgAEobHtw9wkL332ep9fi8Gw5g8sEGwslNonPUCDR6YUZ9mjOehliLpF
+DLHLxlIFafrVM+LIpagjpRKZcnpGPWQDnA==
+-----END EC PRIVATE KEY-----

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-EC-256-KeyPair.pub
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-EC-256-KeyPair.pub b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-EC-256-KeyPair.pub
new file mode 100644
index 0000000..1c9763f
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-EC-256-KeyPair.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBKGx7cPcJC999nqfX4vBsOYPLBBsLJTaJz1Ag0emFGfZoznoZYi6RQyxy8ZSBWn61TPiyKWoI6USmXJ6Rj1kA5w= root@osv-linux
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-EC-384-KeyPair
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-EC-384-KeyPair b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-EC-384-KeyPair
new file mode 100644
index 0000000..29af76f
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-EC-384-KeyPair
@@ -0,0 +1,6 @@
+-----BEGIN EC PRIVATE KEY-----
+MIGkAgEBBDB15z4n/vjug4fcEXPcgeonCHQuxJOwgFDIap/rgtM3EwuFDpE9wkfM
+K64UwV1ZSlygBwYFK4EEACKhZANiAARSJmbXE4/ONrLZXFRyxQRcUxMe5bt41vWm
+Qr3dK/X1DSmei20T4epdaCeKMwK58O163kAVHOaDXfRweUTSfI5dZ1l2OXFwQOzH
+Gayma2JpPs8TYR+lC/pDC2iZMp4CR0M=
+-----END EC PRIVATE KEY-----

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-EC-384-KeyPair.pub
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-EC-384-KeyPair.pub b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-EC-384-KeyPair.pub
new file mode 100644
index 0000000..93ceef0
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-EC-384-KeyPair.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBFImZtcTj842stlcVHLFBFxTEx7lu3jW9aZCvd0r9fUNKZ6LbRPh6l1oJ4ozArnw7XreQBUc5oNd9HB5RNJ8jl1nWXY5cXBA7McZrKZrYmk+zxNhH6UL+kMLaJkyngJHQw== root@osv-linux
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-EC-521-KeyPair
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-EC-521-KeyPair b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-EC-521-KeyPair
new file mode 100644
index 0000000..43f79d7
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-EC-521-KeyPair
@@ -0,0 +1,7 @@
+-----BEGIN EC PRIVATE KEY-----
+MIHcAgEBBEIBUacw+zn8Mw0PYaqqtAlOyaVXARegI6sK5YBhl5E1l9sqTzVN77ce
+1RrqQ8smfvZ6Hiw5gdGcPTszbiorVV5npg6gBwYFK4EEACOhgYkDgYYABACg4siC
+q1iqr4U/spXmw6b2VwBMsof7XLQGoD9wfwUikb8XWthNSmPP1nL6rlzJ5j8Bezn9
+BSSDfVAJfgqxmGIHdgHRVc0mkdq1/Q/DKhBgRyjZc29eo0o2ck3SNGNVaAabRYj6
+ck/iub/U6trKM7bdqy/joYYMwZdxLyYW5YxkPbqEfQ==
+-----END EC PRIVATE KEY-----

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-EC-521-KeyPair.pub
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-EC-521-KeyPair.pub b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-EC-521-KeyPair.pub
new file mode 100644
index 0000000..520b64e
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-EC-521-KeyPair.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBACg4siCq1iqr4U/spXmw6b2VwBMsof7XLQGoD9wfwUikb8XWthNSmPP1nL6rlzJ5j8Bezn9BSSDfVAJfgqxmGIHdgHRVc0mkdq1/Q/DKhBgRyjZc29eo0o2ck3SNGNVaAabRYj6ck/iub/U6trKM7bdqy/joYYMwZdxLyYW5YxkPbqEfQ== root@osv-linux
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-RSA-KeyPair
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-RSA-KeyPair b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-RSA-KeyPair
new file mode 100644
index 0000000..afc6aa8
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-RSA-KeyPair
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEoQIBAAKCAQEAxr3N5fkt966xJINl0hH7Q6lLDRR1D0yMjcXCE5roE9VFut2c
+tGFuo90TCOxkPOMnwzwConeyScVF4ConZeWsxbG9VtRh61IeZ6R5P5ZTvE9xPdZB
+gIEWvU1bRfrrOfSMihqF98pODspE6NoTtND2eglwSGwxcYFmpdTAmu+8qgxgGxlE
+aaCjqwdiNPZhygrH81Mv2ruolNeZkn4Bj+wFFmZTD/waN1pQaMf+SO1+kEYIYFNl
+5+8JRGuUcr8MhHHJB+gwqMTF2BSBVITJzZUiQR0TMtkK6Vbs7yt1F9hhzDzAFDwh
+V+rsfNQaOHpl3zP07qH+/99A0XG1CVcEdHqVMwIBIwKCAQALW02YHN4OJz1Siypj
+xoNi87slUaBKBF/NlkWauGUIcpZFMTwnkIn6vCz5MhRbQC4oadRDzFNUrC/g7HdH
+prlqYe2P7uEGIfMb3YNFdk3tgOHmRsHqFgFMpVWsOjlTxNTUsQ74N3Isuxnha4wY
+9f90sBULc+WRdRvO9jbkSDaqoYVKAqCFWtocL+ZWwBXWrIrsQW4PElgZ/duc5DX7
+eeJ5DXCSj9dO+1KxsWEOKaoeABEegrRVys1/shcDNPhf/p0QShKIdPcpnDUc8cny
+1bq8GSt6jEQ+tuRoSnYrY+RD+mlkHrx373Xc1a9woV+QKTThmd9TQ8gzHMHNqq0a
+7kR7AoGBAOuPOTRiKaDtQyMTEX7eeHsPNE24EgvONjNpxyQ6gKGthG5SiB0IO7mP
+r7EggbR2EY8eMCY5HjvxzxgH86n2Pqbzsr6UlQq7YTPupCm/7fPgRknu917GA20f
+1cuY8B04Jp4FIGryBmCcScX6usXXhjfAvYCWWfkSytA8gX9+b1TNAoGBANf8shbp
+wRnQfgAzw2S+xs29pdwa6Jb/xuLvHSyklmgidrK4nsVI8G+zeCqwkqkNM02sM+vR
+c8EX7+myrGf+S2V3JS3AMNXEhavrWVH0CuqFHlBjSwHZ0uKuPpWHlCnud+23AdQz
+Bf1H7tYKt5es3J/B37o4YxhAL6U9qq+ewZH/AoGBAOTURjLjA94oT9jt870SoOyS
+bVLQEYfPol3UeE8UQnEsN4Ec+UDGK2PNaNfzsTL2WjNB5aF5UJIA184znD60seQC
+raMxQFORdF5ViYekgMEFwJ+XrnlSpD4e7PGqgtqOUWZOH33VKsRADSa5DTU3xDYo
+8porp9wDoHKD64MqXYWTAoGADFeVJeF4v6WDivukw+2k9dBSntz3WRXuG5iilNY0
+evqnsnDzITdeMkTFCcDycA9iBHA9ezCKRYxW2id3kOn1rgbO7KvliI7jESNkMJGa
+OUlvck7RFgxyc1pp+ep9fr0rbKtfMLJ1Xu4q56jXSn7oCSEFeFr+WSg9PKRwJ0rm
+fV8CgYAkYOiNY8jH5lwwoPWOIPJg62zdzmRQktrU3z7Nzk5udN6JnG3g57QjkozX
+AgHARKQ2MuXW9OfOnYNhbGeavcBQmg5XUx3eL4PRFw8mFZdjpBD/kM/dfCEwEta3
+FpRlVGn0RNqVV5xxClObD/CikkDqKZG4MSj3CrO3JK33gl1Lgg==
+-----END RSA PRIVATE KEY-----

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-RSA-KeyPair.pub
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-RSA-KeyPair.pub b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-RSA-KeyPair.pub
new file mode 100644
index 0000000..9fac6f7
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-RSA-KeyPair.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAxr3N5fkt966xJINl0hH7Q6lLDRR1D0yMjcXCE5roE9VFut2ctGFuo90TCOxkPOMnwzwConeyScVF4ConZeWsxbG9VtRh61IeZ6R5P5ZTvE9xPdZBgIEWvU1bRfrrOfSMihqF98pODspE6NoTtND2eglwSGwxcYFmpdTAmu+8qgxgGxlEaaCjqwdiNPZhygrH81Mv2ruolNeZkn4Bj+wFFmZTD/waN1pQaMf+SO1+kEYIYFNl5+8JRGuUcr8MhHHJB+gwqMTF2BSBVITJzZUiQR0TMtkK6Vbs7yt1F9hhzDzAFDwhV+rsfNQaOHpl3zP07qH+/99A0XG1CVcEdHqVMw== lgoldstein@LGOLDSTEIN-WIN7

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-AES-128-key
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-AES-128-key b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-AES-128-key
new file mode 100644
index 0000000..2b93a42
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-AES-128-key
@@ -0,0 +1,30 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-128-CBC,D41AC063160FCC09B1E1931FB43BCEAA
+
+V218dGT2pUpimnwEVn+2ljvK6mvm2aNLlaakMunlHfIswrakJ1WTs8a61pYILOn9
+MGHrCiqe6ZI7FBJ2wXpSxhcuM3fzk6/dW4Ghh4EHG1Y94w97EizxNfyz/iI2XQw0
+i6ttaDLVzP8UcSRElqG+Zpe1A7EE/DkdkXD3f/DaGHtu1zirVeaEIggMLjfTdwnR
+sH9VnUZhe74VdPV0x16h7JjLt5fcbIjqJ6NWW4QvQpPBv3k0oiUy/nP4FXg1b7VW
+7SowuCPi+mF821hj4xSO8ETlAU1eZdtgXqtejtKm0iDtsjnTBZPvDDrq5ephBlxO
+k7JBJG1LFUiDIGnpxos5nCsKEo8UAw9a5/D4xE3C6UTocXon28XGzVCbkZBN6jcd
+UbpjCVwKMJmFL97487u9S57xrGTmJdi1AtF9Rei8juTTQY4+r3l2c7JtdtcbLUhj
+iLvdYnbh6kUEyE19/+omJaWGQlFhYp7ZMRRQSiz6TD8lhSIBPpXzs+uMfhkrifVk
+3WpjRoikmPOOFLtecee5Rp+SpGd700XgLnxwZ47l0FNfrKKqd3+nZX4JILQ2M0JP
+sBx8gcIew8aUqMzWrwZxbrt9Pd1+2kSNVG9hpLoNoA4WpQnYQMo4L0eTCeMNUOap
+f9H0Hh3QnqXTPHbcYZJCGE2RUxLzn/d7rUxUdEzER+pkhJcw9JbV/izTrpDHs9bM
+cfBLggQvs+UIBww2OFz2BztwoQzsSEuNW/SxG/y6SfRUQq5TZw9NxYnrrqfBXKtx
+svB1JVbn2fKq2Lvi8AZ1fF3tyrNot/tptDf0yDHejWDUvVx5cXsKVK2BbVjbZ88k
+mBtUbw7ea9Ev7ZsihNB2EdhPjLhhKlKLIZznPKeXL3GDTXqCgCxTVh4wLvaR8rDU
+C3Isil4WprCeynmZpOe7bxAZDm2QCobnDB8sLQqBI4zgH8X/1iyXJVdSKfK9vxcB
+sJ5pYCcS2q0C+CJkn6HVTlMQ5CyyzvPaDJukJoxwxsZ5hgCsUHFzrvyGnXqGfTBD
+qEW+oA7cj48CfweV5pXHj+mZpCrpn1zRVJRz4h1FZRsttPGtBRAlns5I3kh5BPRs
+4m1BO1jiWyp/7HkUrDRhEf/QeJsP+mTH32pQgnngZ/AGA0PUcKanMUpe1d2ju83V
+EIcTz9ycTHPiOAM6GaVt54fKj9WRBU+7pf14ZdJmfhp6twc0jNtaTh+/I6Pfb0jN
+0d6yKV//pOeJJBNhuOJgm/0vfkOnOojIJchOQCRt5Lg/a4fD/JXtLOed2zOQa+0J
+3d8Y93mQX/iN1wi95/sG79YBYF3FkJYVhjosSKbiIaxIn76zIx4IAlziycDKvgpr
+JgZcVvCDc4flwrf3Cv/uHK7UWOE+16X1CfAy8JzFg5bhiMmhgsJyHmd+zDGrY6NX
+zz+wLmwOenEwC40gpt89OXbgMcwJMtfiSusatRtZ+AAs0jb/8jExVXfcYE3m3r1/
+FqLZ7seTQT2D01YoPlwUtSPxzaZbziAJ/NaGmURnBGVibDCJxwUAiOSIQH4prIfg
+Q2FCJeMTbLV43Lanlby5nrmLkzsw3uo1MO8Of1DbcnVUHNSwrp/nNzrYdxBLIvqS
+-----END RSA PRIVATE KEY-----

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-AES-128-key.pub
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-AES-128-key.pub b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-AES-128-key.pub
new file mode 100644
index 0000000..b1d66a6
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-AES-128-key.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC/oDbs/yYxBdT02ldP6JIrcETJQ+TCml1tHYuo8cIQp0DZCgRZiEZ4foucAT8R/vLK01nnjRzrI42MXiCzyAHb1sPRD0Fsbpa4TFJczPBBRM2mp56airnArQUMmg/ZKlOf82hn+u7Kgn+ljyjYG5FrdoUBju62i0H4+oBfX+pTkd5ruUgqLyPUC3qtNLwjS4PIPAda/pfpsi9UawQ4ommWCCLlwK55NiSrPDBwKNuVWROcQps2NZRxzRLQEiiCEVBEdiUqqUQ+dg2beLV/4cCS860ZZRvCfe+ko8TUBJ7SLtcrvOEYJOKIZDVhcnQKN/wyXCHExSYytUconlFn/9YX root@devenv-aas
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-AES-192-key
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-AES-192-key b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-AES-192-key
new file mode 100644
index 0000000..014410d
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-AES-192-key
@@ -0,0 +1,30 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-192-CBC,EDF8E3E634D2106991113815A3C1F11F
+
+PZaaf2TCSCRmPZDnUa9Hq+RVGAXRXl8T8rduiqFLKG2Vk192AEo4WRPWhIcJ4ov+
+o6N69V0Fj/NrRyuwjihQQM27gTK2JK8cvDen+WB3mp5wmfFaazHXO21xNGMAp3bm
+obEeO+NHaG0080yxQ4AU2BzPZ1q/ePjDGBiENUsBAHeZvN2fM5uI4Cs9pqko2OM/
+ZiJQkn2QlnpSPm8B7kspqpEV9bn1peVYAMIkp/yES18Kw50kan3R2CqccutVQfdP
+8ddTPPUvWUrDSrW/Ae2qZvRoCXVLDhy1jBKQDMUCrxqDJCo58GR0FWxK4nhCgfeL
+wmxGaKL/3uKDl5otkj93C1qCbEvQCJm5rcwEjkRVfeSPvNNh+WQrJE7uNcJB4kXX
+tF8c5peZI8ymdlFFXlCqjVpdhJh0YKRhpxhWKLA15I1bbwwY4/mwIIMyXOcAQo58
+G7XOwBPw01/lSJ/YTR8y1ubFbWJ6LgoLgSzsOLnBxJJji148UiNDPPbal0FN8vK2
+VsSRBrjrNHSmh+BSA0AStq4QtQpwNMU0qKFLzx5HvoYiaSG+OKtJLZNqTGzDG5aS
+Xe8rNoTQTaZBy5yTY1zTn2lMFngWSt/aYyXfc1BXdYyTOk/eoGHtXxp91nQtCdkG
+4VMMfPlnbW1Q2Cq4ATdcIUPPKLaNVlP2TbloBb1wYjXy8RfFFqZVNOLnxXnXOXUG
+U5SlG44XV5AouwhrlUPrm+0+9jIh4pO4sfG0ldGwabGbUMZ6vvgcU9w1yRcsjycG
+v9yH7CfFKKXjUL5RR2weZVD8vRjQMbfVHRt3P4NFClI6e1gXx92FmT7x97866e+T
+TohyC7uZQq2prBzt8BwxFP0h19+CiTVzofe2I9x+Hce67F+ATnLq9fxOnjZlwK1i
+K95eK+Itc9zSu9tIZGaLvghOExZ24893Ncz5NJ2nn0YptxSi24e8PcECHhSUrjv9
+1VOi8JUmv58bQ5ocuUlHmB2FkpnsYUNjtQx1OT2IRovQJIDCttHhT3QIGoiqoeTw
+sSwyz0gDYzAJtuVkSZxqXJ4LEMozaA3uwxv0TfaQordm+GuGKuV5MsKyEQtITUk5
+5bgQfS0VQqkbq2fRjtd3nIJWCF5otrVulY0tzoTBjGZ2trNj5nz5ZEUTFdRSAaxb
+F+i3Mv8gGPC+2YIo8feDCHnRQ1AALjLon8nKefRsWAOYUozNCtHvu4g7WK6+GcPd
+eiYM7tbSrNCHg+KdB+eWrJCfVCgVHTj8mGb3EiH6oxgx51TmlyuQYJAVseWjQOdm
+IDKiAsNNZijmgMWLsFZG1VvF/446VWIGkSW9eB6bB7aR1Kg4F18VkXdn+SPmaHrC
+NHhNa9dOmcOvlNXIs7jlTjRVEC0pp8DH0AUdPjqlR/F1IHb7uFyqlza8Cqb7PZJs
+x+9GbSwuyKLKLvMzmsnBtpRT32Srsbx6pWst40gaoOMFxto22vmGxz4IABGi96V/
+NrrDHtuZ5OaePl7GihKcUCSSVcoFO2jKKNMMHSSVGhxkfir7qGzMe5LZ9/zl+bvW
+vBS0wSPUd+P3mLc0m53mH+g26I479ryX6/2W+GecB1h5p7S7vJMPQjqZAJskogf5
+-----END RSA PRIVATE KEY-----

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-AES-192-key.pub
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-AES-192-key.pub b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-AES-192-key.pub
new file mode 100644
index 0000000..3fda036
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-AES-192-key.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCutvykV6G6t4JlLhlJOQKknSB++PXLTtuSjXaH77hUUEl/yc9VbEsfOz1NySBRQDWT0sgRZ1D2YAxaIvK9+0wZPnRNO1ptDrwj650Gflh8vKkEQOlHH7ISTxQTHtyHGeXfm3Bl+qqxTMPG5gcxxkKINMvKAkNYBnOyVX1OXUvqtM1jx6THlfWPQ9yFuVmZDVj1W5VItxjG293RejbjN1EjhhGRBiqwczcIPgPiMDfR0+xFNEQt6YD4k9TFEkLmIfCn+fcqKJhP/z99bRosMHtkRwfX/Rco+G9dRwe1sHvVH7ETeS7v3vl3WoM58AlpYYGUIXsxozRdGE93AmTkaSIj SecurityUtilsTest
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-AES-256-key
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-AES-256-key b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-AES-256-key
new file mode 100644
index 0000000..26b699e
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-AES-256-key
@@ -0,0 +1,30 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-256-CBC,4FF564F3E05187BEDEDA0529D44EE13E
+
+z7KGBVkxJzNS1Z8vMurcYmHhlbxnUYHry87VgzV38UkwsLSo5FIPNUaxvLPeD3Im
+YW4hQ/1Z+3onSwTgaUqxih6YK+fHS0RZD5R4HjOWaERCPZa5/oDciLrjOU3EKkSV
+n84/ezqUn8GlMJYcpB2sURCmnJZ6erHCVSPAhVlsrWPO9uZHx8BpaclqsvC4Gq7a
+C4liGjDNsFvVO998eDIDLmHeO+qGnyeQK+pu3/+Ausvd9QjUf4L3T+Lu+Cgr0w59
+tB3TxeGxPel9Av88VYIhCddIEvRyMcN8lzLrrYrsLNPMmaF1x2uNDORJCTlEjvLw
+QG6zugofiUVdZdsWRPFmM8DbujeoUHkPkwImXedhiKz1Tp0Rz6gqGx6tzsqEe0kD
+eS2e0Ibj41fFxpkQlaG/xDliUvntkoQ6v7ZKbXQrsBP4hyoMcjeUMhWL+5shkJEV
+RzvZOl7WKoovFnf3glLdiDFqlfLK5BhBJckLmdmnVvsD1HYeKqSDrx+r3hUF3yZ5
+Obxxk++QTC1qHhnyAwqW7Y2owq81pmOhgxKqh/4OoxYV0trIgzI47+5hKsp12Van
+7qBCfTtXv4IlBKaKARTroQ2sDPAi1B2j8mU5lrGm81BzsLSqh1IJjOMsccWNfjMS
+jQMKvcQN94y7AvxcBDGVn9FZftNWs9/ZwitRCFfrz/n0u6hLDQBbH6muDffo4ZhF
+ZiH23W743N/ZASC/kA7LZ3pmH6TNl2Zf1gP+tTrfXu17BRDJYi8VqHBcuck/M4Ox
+qOpP4mK2B/aAVmTkty67+QJerIwsW+h8LsjkQTnXGoneobsx2nL8OvJtPHkOUo4b
+1zFyFvQK+V8DWWAml4Y8V+4NCgrybqqK11ApRl7FfaOyCMgZ+AvqzjK3E5s1gsaf
+AaxORV0iDE0Y8nhq9EEhDNTr5Yi6FeHuRmVsack1AQqYn1U64k21x4ZEmDjslq9t
+WKizqG6jz6xtRtsypr0oCBYirt8p5qDmeO58MdES6A+nkOZC0ysyhizpDWdj01rQ
+6w3el0t/wUh1lBC3UCTeD7Vz20B1rAs5AlbdFmOUGHoH2Hjty1DQVZ+Fl5/K9AQi
+yaVR07IDE45i9rvl7DThRHT6Qd0KAb7bIo+Xlz/xKUgA0UVLkpfGtgDNi1DSTsBQ
+m6xlvR9M3tmMwnHlOEfX8YAGIQR9JLiImUtypjXnp6IdxCn682UIWC9XakqMFbzS
+Hm4Y0VBHYPisByl7PuavEP69pgYVdt1PqFFOc3xL42QVihYsM9huw1j4rlP7/jXB
+zJyH6yuqZHV7BYmurBByxRCe4joN24D6HYWTLKhPjgIG++n/IZ+PO7s0khrTh3Pl
+L3bOujyLtr88w6PPhBOCVDPgmJEC6lIGC8zhh+S9Knewr50PsvOoQVKGDTSPO+G7
+NNa/ugN9/eTeO2ru2y6HuahWrf2seKrTQB8JVYP0SZkOe4zVdMRR5M559iLIdh0e
+U/hrj6v/dYVbIkmSWdhtxx0jXbM2VlWTeQQjUYzeVumVhZ1pk2P0LeHrSWhHa5h2
+hadw69Ht0hRTbljZ1MHjK9BbQI1Ph+YBuDr0aPVBf2ePYFvPObw2Mik7ctiDlmQT
+-----END RSA PRIVATE KEY-----

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-AES-256-key.pub
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-AES-256-key.pub b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-AES-256-key.pub
new file mode 100644
index 0000000..6a3a0ac
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-AES-256-key.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDNUSajd7RzgqvQ2Q2em3CFA4Fh2f8YJkzJTPrcn9L9SFZnent2rABlVVr216jh3hAjhRFEkRhsoWUxUB7UYN6Ox/hGXrfCtD9wB0QbpOjNb0tnSovl3e+LfnWO5mpaRAb9croiuKVwQXuiwLNOQqs5C2wvLJhc77U4myuvFycYQ9Gh9g12g05Q8Qs+Du6hFSV1n1ZDMTtiVYknhe2gCLmBh1ghpaGeG3eWpd8EXumTrpwrylb1hJtj4yo748dXFa/YUJpqzCNyoA+0aY+v0MwgxZcS6zWt5VQ3yqRi4YrXX7vvhz80rKmJQlEhvM9rVKAwKc/qkJdTWlU+v0+/zY7/ hello@world
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-DES-EDE3-key
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-DES-EDE3-key b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-DES-EDE3-key
new file mode 100644
index 0000000..e6f6156
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-DES-EDE3-key
@@ -0,0 +1,30 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,7DD956370CB0B83E
+
+oTBNWVUGIKQ3LX/bIE+upuTtiVSWQ9ooTxdNJRIpGsq6oTevtxgkuIF9oO+ShZJq
+zf/mTQPtAyRHSX8JsTKw9GZhd5w7c07V3wfzx689RHFCZDW1DIl67KvwyhRwfRtY
+HXZGYPW/XmOgh3Gw31csmLlJd42r8d+7MRIfmo25SO5Sy67mNccRJikdbYa/HH50
+yMVaf7o73tLLIS3WNnhaKjf9hRsSW370aICk3hfS/NMriae0xAMpqpYQmGkn9Zhh
+zTg+Kjo0FiEZEnMNKZQRRWx7VsoQRfrebOZq3LF7tfEnJcKneJ+grvQp0FpkEMzN
+JeiaEwtoIrXRU5KENQbEnXqF2Mpai6ejh5lVflcHqI01lwWGWkwxnNpxgMbWCKuB
+Unad3fQQLFQU67B8jQpES2AQVXO7UaaDHNtATftC2gFpBktlkSurT/KZBT4bVfS7
+z3K9gFB/lYrji2ef+xuzbLmq+eja9OlZIVxH7RTxODkyrxeNhtEUXJgezOB64Cny
+YFrsDPcR8rSRc5SjMMuAy2dSkyndSp3m2YmtVFHibPrX18K2lblRkiuVDci0EX4p
+1XzaCJ9mhdtMedpIzFdwgCOt/M8OBw9g02qgOK1uBu37tXdACNxhX/wrFPCtbJjD
+yBwnBbXoeca8ZF45uw1egKlHaY2d7kTsH+bdx04bTo4Xq0+lXMALxxkfwTOt27xL
+JQp15AFm6ZAshVt7hxCtm512HoSkkFYMPUPawH9HVZE6f4sbNBlbqgCmYhD5HPMN
+Kd3ssQWTXwiOSd+k8HuBUBhtdZsgdpSdR5xH9wEymS9fcGmrf2yLuRlxhvoqo44X
++go5CqLoAKoUtVJdtZrkmMdF1OxufDBZ16+fKN8NNE8vQv8Kt221yg/mmDy3t6mh
+axynBSxk1Qtjz2H9iZJ5nglF2Hceb1FQXHGlLCNv+cwA9vRC0pQJ25fPHVhvxhmW
+gnLJ5OY+zXENUWvMIeAGCSfjSVtnSVWAfdJ7kgHZc/wCWO6rYAwlfsPDq8ZdQ8Za
+4Hm6sUnkHHtagvJZahjdSPoziE68FiHqFU3PdmuQsZpCFOzloxahWImgx0UM9n8k
+2DlRPK6bMBCjpUrb5D0jp8YU2KDepw2D4O7RaFLh47w78oxrO1gABY23M4JQfUiP
+lzsfsGy9OI1twCcsJhFOX95nL9w+hlvFALTkY5UT3dB3eWP3k2nE12UxQhE3hVZ9
+vI100y2m/FUnKkeGYkvhRw9yD611jjYuRYGL3LsKNaOK97Ses+vAB0LNOss+12vz
+7bYvDUVJ5csfpPGqxyzYwIbytDwUcvxTLxRlgQNb2YMqzSmVodO0lyXdNF1/KUFF
+nIdlh0KSe4BALzYhya9V71MngLHFifp/erS4gdnFbbBCmZTEZhziBnqimRPoN5Co
+K+22blcRkDytiL+NTRybvfOYvy6Ulb4OK/So+Y5r1B5KGhJqFWSeo0g2ftAB6r1h
+HSbfoxYO/xCMQy7aNXuJI0giVRThcQsQapTO2DMeymt/0nj8qEPF0dubtbmgJ4dd
+aZuYaY1Qxk0umVfq9zHsRTbRb+16TuZtp+7brDDRpRe6lSzdbTNShg==
+-----END RSA PRIVATE KEY-----

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-DES-EDE3-key.pub
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-DES-EDE3-key.pub b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-DES-EDE3-key.pub
new file mode 100644
index 0000000..f5c8c85
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-DES-EDE3-key.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAxvz5QWkCxznYbBOUbMFsCf+PJLauyZTcPsHLPZ/lHYH7EIa9s8NWbfFyTJXVwcuJiDCoRZ8d7KBahAuNjGeXsuJQuCqlLLl6GKoFnI0hnbKbFHDDSub+s3WvLdaTeF22qlTmlPb3RFYUO0cWk9MVpVNM6ev33CUGv4Dmbr0dZNkgUOkcPHiQpxKizsED+EWSVh1Ptx8AEDObNyFHJljbHUeJrHTIlcaekJheRXQWLvsJqKD0TN+Dkvi044MDWG9VjVyNsyXBCz1Vk9VaK1dNkkH+RDGTsFvFj7IPwBS/FliEVRrKOUgjMrmKUPbAm8IblIsno+HLZ60OB6X6hu2iHw== lgoldstein@LGOLDSTEIN-WIN7

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-contrib/pom.xml
----------------------------------------------------------------------
diff --git a/sshd-contrib/pom.xml b/sshd-contrib/pom.xml
index 672069b..ac00587 100644
--- a/sshd-contrib/pom.xml
+++ b/sshd-contrib/pom.xml
@@ -67,7 +67,7 @@
             <artifactId>eddsa</artifactId>
             <optional>true</optional>
         </dependency>
-
+            <!-- Test dependencies -->
         <dependency>
             <groupId>org.apache.sshd</groupId>
             <artifactId>sshd-core</artifactId>
@@ -76,6 +76,13 @@
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>org.apache.sshd</groupId>
+            <artifactId>sshd-common</artifactId>
+            <version>${project.version}</version>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>jcl-over-slf4j</artifactId>
             <scope>test</scope>

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-contrib/src/test/java/org/apache/sshd/server/scp/SimpleAccessControlScpEventListenerTest.java
----------------------------------------------------------------------
diff --git a/sshd-contrib/src/test/java/org/apache/sshd/server/scp/SimpleAccessControlScpEventListenerTest.java b/sshd-contrib/src/test/java/org/apache/sshd/server/scp/SimpleAccessControlScpEventListenerTest.java
index 4312dda..ecc9678 100644
--- a/sshd-contrib/src/test/java/org/apache/sshd/server/scp/SimpleAccessControlScpEventListenerTest.java
+++ b/sshd-contrib/src/test/java/org/apache/sshd/server/scp/SimpleAccessControlScpEventListenerTest.java
@@ -36,7 +36,7 @@ import org.apache.sshd.common.scp.ScpException;
 import org.apache.sshd.common.scp.ScpHelper;
 import org.apache.sshd.server.SshServer;
 import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.Utils;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.FixMethodOrder;
@@ -94,12 +94,12 @@ public class SimpleAccessControlScpEventListenerTest extends BaseTestSupport {
                 ScpClient scp = creator.createScpClient(session);
                 Path targetPath = detectTargetFolder();
                 Path parentPath = targetPath.getParent();
-                Path scpRoot = Utils.resolve(targetPath, ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
-                Utils.deleteRecursive(scpRoot);
+                Path scpRoot = CommonTestSupportUtils.resolve(targetPath, ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
+                CommonTestSupportUtils.deleteRecursive(scpRoot);
 
                 Path remoteDir = assertHierarchyTargetFolderExists(scpRoot.resolve("remote"));
                 Path remoteFile = remoteDir.resolve("file.txt");
-                String remotePath = Utils.resolveRelativeRemotePath(parentPath, remoteFile);
+                String remotePath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, remoteFile);
                 byte[] data = (getClass().getName() + "#" + getCurrentTestName()).getBytes(StandardCharsets.UTF_8);
                 Files.write(remoteFile, data);
                 byte[] downloaded = scp.downloadBytes(remotePath);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-contrib/src/test/java/org/apache/sshd/server/subsystem/sftp/SimpleAccessControlSftpEventListenerTest.java
----------------------------------------------------------------------
diff --git a/sshd-contrib/src/test/java/org/apache/sshd/server/subsystem/sftp/SimpleAccessControlSftpEventListenerTest.java b/sshd-contrib/src/test/java/org/apache/sshd/server/subsystem/sftp/SimpleAccessControlSftpEventListenerTest.java
index ddbe162..f824811 100644
--- a/sshd-contrib/src/test/java/org/apache/sshd/server/subsystem/sftp/SimpleAccessControlSftpEventListenerTest.java
+++ b/sshd-contrib/src/test/java/org/apache/sshd/server/subsystem/sftp/SimpleAccessControlSftpEventListenerTest.java
@@ -38,7 +38,7 @@ import org.apache.sshd.common.subsystem.sftp.SftpException;
 import org.apache.sshd.server.SshServer;
 import org.apache.sshd.server.scp.ScpCommandFactory;
 import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.Utils;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.FixMethodOrder;
@@ -89,7 +89,7 @@ public class SimpleAccessControlSftpEventListenerTest extends BaseTestSupport {
     public void testReadOnlyFileAccess() throws Exception {
         Path targetPath = detectTargetFolder();
         Path parentPath = targetPath.getParent();
-        Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
+        Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
         Path testFile = assertHierarchyTargetFolderExists(lclSftp).resolve("file.txt");
         byte[] data = (getClass().getName() + "#" + getCurrentTestName()).getBytes(StandardCharsets.UTF_8);
         Files.deleteIfExists(testFile);
@@ -103,7 +103,7 @@ public class SimpleAccessControlSftpEventListenerTest extends BaseTestSupport {
                 session.auth().verify(5L, TimeUnit.SECONDS);
 
                 try (SftpClient sftp = SftpClientFactory.instance().createSftpClient(session)) {
-                    String file = Utils.resolveRelativeRemotePath(parentPath, testFile);
+                    String file = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, testFile);
                     try (CloseableHandle handle = sftp.open(file, OpenMode.Read)) {
                         byte[] actual = new byte[data.length];
                         int readLen = sftp.read(handle, 0L, actual);
@@ -139,7 +139,7 @@ public class SimpleAccessControlSftpEventListenerTest extends BaseTestSupport {
     public void testReadOnlyDirectoryAccess() throws Exception {
         Path targetPath = detectTargetFolder();
         Path parentPath = targetPath.getParent();
-        Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
+        Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
         Path testFile = assertHierarchyTargetFolderExists(lclSftp).resolve("file.txt");
         byte[] data = (getClass().getName() + "#" + getCurrentTestName()).getBytes(StandardCharsets.UTF_8);
         Files.deleteIfExists(testFile);
@@ -153,12 +153,12 @@ public class SimpleAccessControlSftpEventListenerTest extends BaseTestSupport {
                 session.auth().verify(5L, TimeUnit.SECONDS);
 
                 try (SftpClient sftp = SftpClientFactory.instance().createSftpClient(session)) {
-                    String folder = Utils.resolveRelativeRemotePath(parentPath, targetPath);
+                    String folder = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, targetPath);
                     for (SftpClient.DirEntry entry : sftp.readDir(folder)) {
                         assertNotNull("No entry", entry);
                     }
 
-                    String file = Utils.resolveRelativeRemotePath(parentPath, testFile);
+                    String file = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, testFile);
                     try {
                         sftp.remove(file);
                         fail("Unexpected file remove success");

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/pom.xml
----------------------------------------------------------------------
diff --git a/sshd-core/pom.xml b/sshd-core/pom.xml
index 0bcab83..c81d75e 100644
--- a/sshd-core/pom.xml
+++ b/sshd-core/pom.xml
@@ -39,9 +39,11 @@
 
     <dependencies>
         <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-api</artifactId>
+            <groupId>org.apache.sshd</groupId>
+            <artifactId>sshd-common</artifactId>
+            <version>${project.version}</version>
         </dependency>
+
         <dependency>
             <groupId>tomcat</groupId>
             <artifactId>tomcat-apr</artifactId>
@@ -68,6 +70,14 @@
 
             <!-- test dependencies -->
         <dependency>
+            <groupId>org.apache.sshd</groupId>
+            <artifactId>sshd-common</artifactId>
+            <version>${project.version}</version>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
             <groupId>org.apache.mina</groupId>
             <artifactId>mina-core</artifactId>
             <scope>test</scope>
@@ -117,11 +127,6 @@
             <artifactId>ganymed-ssh2</artifactId>
             <scope>test</scope>
         </dependency>
-        <dependency>
-            <groupId>org.apache.servicemix.bundles</groupId>
-            <artifactId>org.apache.servicemix.bundles.not-yet-commons-ssl</artifactId>
-            <scope>test</scope>
-        </dependency>
     </dependencies>
 
     <build>

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/filtered-resources/org/apache/sshd/sshd-version.properties
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/filtered-resources/org/apache/sshd/sshd-version.properties b/sshd-core/src/main/filtered-resources/org/apache/sshd/sshd-version.properties
deleted file mode 100644
index 2c32c28..0000000
--- a/sshd-core/src/main/filtered-resources/org/apache/sshd/sshd-version.properties
+++ /dev/null
@@ -1,23 +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.
-##
-
-groupId=${pom.groupId}
-artifactId=${pom.artifactId}
-version=${pom.version}
-sshd-version=${pom.artifactId}-${pom.version}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java b/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java
index 533cae5..76f2350 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java
@@ -25,6 +25,7 @@ import java.net.InetSocketAddress;
 import java.net.SocketAddress;
 import java.net.SocketTimeoutException;
 import java.nio.file.LinkOption;
+import java.nio.file.Path;
 import java.security.GeneralSecurityException;
 import java.security.KeyPair;
 import java.util.ArrayList;
@@ -47,6 +48,7 @@ import org.apache.sshd.client.auth.password.UserAuthPasswordFactory;
 import org.apache.sshd.client.auth.pubkey.UserAuthPublicKeyFactory;
 import org.apache.sshd.client.config.hosts.HostConfigEntry;
 import org.apache.sshd.client.config.hosts.HostConfigEntryResolver;
+import org.apache.sshd.client.config.keys.ClientIdentity;
 import org.apache.sshd.client.config.keys.ClientIdentityLoader;
 import org.apache.sshd.client.config.keys.DefaultClientIdentitiesWatcher;
 import org.apache.sshd.client.future.ConnectFuture;
@@ -68,6 +70,7 @@ import org.apache.sshd.common.ServiceFactory;
 import org.apache.sshd.common.channel.Channel;
 import org.apache.sshd.common.config.keys.FilePasswordProvider;
 import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.config.keys.PublicKeyEntry;
 import org.apache.sshd.common.future.SshFutureListener;
 import org.apache.sshd.common.helpers.AbstractFactoryManager;
 import org.apache.sshd.common.io.IoConnectFuture;
@@ -769,4 +772,61 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
     public static SshClient setUpDefaultClient() {
         return ClientBuilder.builder().build();
     }
+
+    /**
+     * @param <C>           The generic client class
+     * @param client        The {@link SshClient} to updated
+     * @param strict        If {@code true} then files that do not have the required
+     *                      access rights are excluded from consideration
+     * @param supportedOnly If {@code true} then ignore identities that are not
+     *                      supported internally
+     * @param provider      A {@link FilePasswordProvider} - may be {@code null}
+     *                      if the loaded keys are <U>guaranteed</U> not to be encrypted. The argument
+     *                      to {@link FilePasswordProvider#getPassword(String)} is the path of the
+     *                      file whose key is to be loaded
+     * @param options       The {@link LinkOption}s to apply when checking
+     *                      for existence
+     * @return The updated <tt>client</tt> instance - provided a non-{@code null}
+     * {@link KeyPairProvider} was generated
+     * @throws IOException              If failed to access the file system
+     * @throws GeneralSecurityException If failed to load the keys
+     * @see #setKeyPairProvider(SshClient, Path, boolean, boolean, FilePasswordProvider, LinkOption...)
+     */
+    public static <C extends SshClient> C setKeyPairProvider(
+            C client, boolean strict, boolean supportedOnly, FilePasswordProvider provider, LinkOption... options)
+            throws IOException, GeneralSecurityException {
+        return setKeyPairProvider(client, PublicKeyEntry.getDefaultKeysFolderPath(), strict, supportedOnly, provider, options);
+    }
+
+    /**
+     * @param <C>           The generic client class
+     * @param client        The {@link SshClient} to updated
+     * @param dir           The folder to scan for the built-in identities
+     * @param strict        If {@code true} then files that do not have the required
+     *                      access rights are excluded from consideration
+     * @param supportedOnly If {@code true} then ignore identities that are not
+     *                      supported internally
+     * @param provider      A {@link FilePasswordProvider} - may be {@code null}
+     *                      if the loaded keys are <U>guaranteed</U> not to be encrypted. The argument
+     *                      to {@link FilePasswordProvider#getPassword(String)} is the path of the
+     *                      file whose key is to be loaded
+     * @param options       The {@link LinkOption}s to apply when checking
+     *                      for existence
+     * @return The updated <tt>client</tt> instance - provided a non-{@code null}
+     * {@link KeyPairProvider} was generated
+     * @throws IOException              If failed to access the file system
+     * @throws GeneralSecurityException If failed to load the keys
+     * @see #loadDefaultKeyPairProvider(Path, boolean, boolean, FilePasswordProvider, LinkOption...)
+     */
+    public static <C extends SshClient> C setKeyPairProvider(
+            C client, Path dir, boolean strict, boolean supportedOnly, FilePasswordProvider provider, LinkOption... options)
+            throws IOException, GeneralSecurityException {
+        KeyPairProvider kpp =
+            ClientIdentity.loadDefaultKeyPairProvider(dir, strict, supportedOnly, provider, options);
+        if (kpp != null) {
+            client.setKeyPairProvider(kpp);
+        }
+
+        return client;
+    }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/auth/AuthenticationIdentitiesProvider.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/AuthenticationIdentitiesProvider.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/AuthenticationIdentitiesProvider.java
deleted file mode 100644
index cfd1f85..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/AuthenticationIdentitiesProvider.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.client.auth;
-
-import java.security.KeyPair;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-
-import org.apache.sshd.client.auth.password.PasswordIdentityProvider;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.keyprovider.KeyIdentityProvider;
-import org.apache.sshd.common.util.GenericUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface AuthenticationIdentitiesProvider extends KeyIdentityProvider, PasswordIdentityProvider {
-
-    /**
-     * Compares 2 password identities - returns zero ONLY if <U>both</U> compared
-     * objects are {@link String}s and equal to each other
-     */
-    Comparator<Object> PASSWORD_IDENTITY_COMPARATOR = (o1, o2) -> {
-        if (!(o1 instanceof String) || !(o2 instanceof String)) {
-            return -1;
-        } else {
-            return ((String) o1).compareTo((String) o2);
-        }
-    };
-
-    /**
-     * Compares 2 {@link KeyPair} identities - returns zero ONLY if <U>both</U> compared
-     * objects are {@link KeyPair}s and equal to each other
-     */
-    Comparator<Object> KEYPAIR_IDENTITY_COMPARATOR = (o1, o2) -> {
-        if ((!(o1 instanceof KeyPair)) || (!(o2 instanceof KeyPair))) {
-            return -1;
-        } else if (KeyUtils.compareKeyPairs((KeyPair) o1, (KeyPair) o2)) {
-            return 0;
-        } else {
-            return 1;
-        }
-    };
-
-    /**
-     * @return All the currently available identities - passwords, keys, etc...
-     */
-    Iterable<?> loadIdentities();
-
-    static int findIdentityIndex(List<?> identities, Comparator<? super Object> comp, Object target) {
-        for (int index = 0; index < identities.size(); index++) {
-            Object value = identities.get(index);
-            if (comp.compare(value, target) == 0) {
-                return index;
-            }
-        }
-
-        return -1;
-    }
-
-    /**
-     * @param identities The {@link Iterable} identities - OK if {@code null}/empty
-     * @return An {@link AuthenticationIdentitiesProvider} wrapping the identities
-     */
-    static AuthenticationIdentitiesProvider wrapIdentities(Iterable<?> identities) {
-        return new AuthenticationIdentitiesProvider() {
-            @Override
-            public Iterable<KeyPair> loadKeys() {
-                return selectIdentities(KeyPair.class);
-            }
-
-            @Override
-            public Iterable<String> loadPasswords() {
-                return selectIdentities(String.class);
-            }
-
-            @Override
-            public Iterable<?> loadIdentities() {
-                return selectIdentities(Object.class);
-            }
-
-            // NOTE: returns a NEW Collection on every call so that the original
-            //      identities remain unchanged
-            private <T> Collection<T> selectIdentities(Class<T> type) {
-                Collection<T> matches = null;
-                for (Iterator<?> iter = GenericUtils.iteratorOf(identities); iter.hasNext();) {
-                    Object o = iter.next();
-                    Class<?> t = o.getClass();
-                    if (!type.isAssignableFrom(t)) {
-                        continue;
-                    }
-
-                    if (matches == null) {
-                        matches = new LinkedList<>();
-                    }
-
-                    matches.add(type.cast(o));
-                }
-
-                return (matches == null) ? Collections.<T>emptyList() : matches;
-            }
-        };
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/auth/hostbased/HostKeyIdentityProvider.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/hostbased/HostKeyIdentityProvider.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/hostbased/HostKeyIdentityProvider.java
deleted file mode 100644
index 81c26ca..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/hostbased/HostKeyIdentityProvider.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.client.auth.hostbased;
-
-import java.security.KeyPair;
-import java.security.cert.X509Certificate;
-import java.util.AbstractMap.SimpleImmutableEntry;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.sshd.common.util.GenericUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FunctionalInterface
-public interface HostKeyIdentityProvider {
-    /**
-     * @return The host keys as a {@link java.util.Map.Entry} of key + certificates (which can be {@code null}/empty)
-     */
-    Iterable<? extends Map.Entry<KeyPair, List<X509Certificate>>> loadHostKeys();
-
-    static Iterator<? extends Map.Entry<KeyPair, List<X509Certificate>>> iteratorOf(HostKeyIdentityProvider provider) {
-        return GenericUtils.iteratorOf((provider == null) ? null : provider.loadHostKeys());
-    }
-
-    static HostKeyIdentityProvider wrap(KeyPair... pairs) {
-        return wrap(GenericUtils.asList(pairs));
-    }
-
-    static HostKeyIdentityProvider wrap(Iterable<? extends KeyPair> pairs) {
-        return () -> GenericUtils.wrapIterable(pairs, kp -> new SimpleImmutableEntry<>(kp, Collections.<X509Certificate>emptyList()));
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/auth/keyboard/UserAuthKeyboardInteractive.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/keyboard/UserAuthKeyboardInteractive.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/keyboard/UserAuthKeyboardInteractive.java
index d765b11..479e5bc 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/keyboard/UserAuthKeyboardInteractive.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/auth/keyboard/UserAuthKeyboardInteractive.java
@@ -25,7 +25,6 @@ import java.util.concurrent.atomic.AtomicInteger;
 
 import org.apache.sshd.client.ClientAuthenticationManager;
 import org.apache.sshd.client.auth.AbstractUserAuth;
-import org.apache.sshd.client.auth.password.PasswordIdentityProvider;
 import org.apache.sshd.client.session.ClientSession;
 import org.apache.sshd.common.RuntimeSshException;
 import org.apache.sshd.common.SshConstants;
@@ -87,7 +86,7 @@ public class UserAuthKeyboardInteractive extends AbstractUserAuth {
     @Override
     public void init(ClientSession session, String service) throws Exception {
         super.init(session, service);
-        passwords = PasswordIdentityProvider.iteratorOf(session);
+        passwords = ClientSession.passwordIteratorOf(session);
         maxTrials = session.getIntProperty(ClientAuthenticationManager.PASSWORD_PROMPTS, ClientAuthenticationManager.DEFAULT_PASSWORD_PROMPTS);
         ValidateUtils.checkTrue(maxTrials > 0, "Non-positive max. trials: %d", maxTrials);
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/auth/password/PasswordIdentityProvider.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/password/PasswordIdentityProvider.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/password/PasswordIdentityProvider.java
deleted file mode 100644
index 6af6696..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/password/PasswordIdentityProvider.java
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.client.auth.password;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.function.Function;
-import java.util.function.Supplier;
-
-import org.apache.sshd.client.session.ClientSession;
-import org.apache.sshd.common.util.GenericUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FunctionalInterface
-public interface PasswordIdentityProvider {
-
-    /**
-     * An &quot;empty&quot implementation of {@link PasswordIdentityProvider} that returns
-     * and empty group of passwords
-     */
-    PasswordIdentityProvider EMPTY_PASSWORDS_PROVIDER = new PasswordIdentityProvider() {
-        @Override
-        public Iterable<String> loadPasswords() {
-            return Collections.emptyList();
-        }
-
-        @Override
-        public String toString() {
-            return "EMPTY";
-        }
-    };
-
-    /**
-     * Invokes {@link PasswordIdentityProvider#loadPasswords()} and returns the result.
-     * Ignores {@code null} providers (i.e., returns an empty iterable instance)
-     */
-    Function<PasswordIdentityProvider, Iterable<String>> LOADER = p ->
-            (p == null) ? Collections.emptyList() : p.loadPasswords();
-
-    /**
-     * @return The currently available passwords - ignored if {@code null}
-     */
-    Iterable<String> loadPasswords();
-
-    /**
-     * Creates a &quot;unified&quot; {@link Iterator} of passwords out of the registered
-     * passwords and the extra available ones as a single iterator of passwords
-     *
-     * @param session The {@link ClientSession} - ignored if {@code null} (i.e., empty
-     * iterator returned)
-     * @return The wrapping iterator
-     * @see ClientSession#getRegisteredIdentities()
-     * @see ClientSession#getPasswordIdentityProvider()
-     */
-    static Iterator<String> iteratorOf(ClientSession session) {
-        return (session == null) ? Collections.<String>emptyIterator() : iteratorOf(session.getRegisteredIdentities(), session.getPasswordIdentityProvider());
-    }
-
-    /**
-     * Creates a &quot;unified&quot; {@link Iterator} of passwords out of 2 possible
-     * {@link PasswordIdentityProvider}
-     *
-     * @param identities The registered passwords
-     * @param passwords Extra available passwords
-     * @return The wrapping iterator
-     * @see #resolvePasswordIdentityProvider(PasswordIdentityProvider, PasswordIdentityProvider)
-     */
-    static Iterator<String> iteratorOf(PasswordIdentityProvider identities, PasswordIdentityProvider passwords) {
-        return iteratorOf(resolvePasswordIdentityProvider(identities, passwords));
-    }
-
-    /**
-     * Resolves a non-{@code null} iterator of the available passwords
-     *
-     * @param provider The {@link PasswordIdentityProvider} - ignored if {@code null} (i.e.,
-     * return an empty iterator)
-     * @return A non-{@code null} iterator - which may be empty if no provider or no passwords
-     */
-    static Iterator<String> iteratorOf(PasswordIdentityProvider provider) {
-        return GenericUtils.iteratorOf((provider == null) ? null : provider.loadPasswords());
-    }
-
-    /**
-     * <P>Creates a &quot;unified&quot; {@link PasswordIdentityProvider} out of 2 possible ones
-     * as follows:</P></BR>
-     * <UL>
-     *      <LI>If both are {@code null} then return {@code null}.</LI>
-     *      <LI>If either one is {@code null} then use the non-{@code null} one.</LI>
-     *      <LI>If both are the same instance then use it.</U>
-     *      <LI>Otherwise, returns a wrapper that groups both providers.</LI>
-     * </UL>
-     * @param identities The registered passwords
-     * @param passwords The extra available passwords
-     * @return The resolved provider
-     * @see #multiProvider(PasswordIdentityProvider...)
-     */
-    static PasswordIdentityProvider resolvePasswordIdentityProvider(PasswordIdentityProvider identities, PasswordIdentityProvider passwords) {
-        if ((passwords == null) || (identities == passwords)) {
-            return identities;
-        } else if (identities == null) {
-            return passwords;
-        } else {
-            return multiProvider(identities, passwords);
-        }
-    }
-
-    /**
-     * Wraps a group of {@link PasswordIdentityProvider} into a single one
-     *
-     * @param providers The providers - ignored if {@code null}/empty (i.e., returns
-     * {@link #EMPTY_PASSWORDS_PROVIDER}
-     * @return The wrapping provider
-     * @see #multiProvider(Collection)
-     */
-    static PasswordIdentityProvider multiProvider(PasswordIdentityProvider... providers) {
-        return multiProvider(GenericUtils.asList(providers));
-    }
-
-    /**
-     * Wraps a group of {@link PasswordIdentityProvider} into a single one
-     *
-     * @param providers The providers - ignored if {@code null}/empty (i.e., returns
-     * {@link #EMPTY_PASSWORDS_PROVIDER}
-     * @return The wrapping provider
-     */
-    static PasswordIdentityProvider multiProvider(Collection<? extends PasswordIdentityProvider> providers) {
-        return GenericUtils.isEmpty(providers) ? EMPTY_PASSWORDS_PROVIDER : wrapPasswords(iterableOf(providers));
-    }
-
-    /**
-     * Wraps a group of {@link PasswordIdentityProvider} into an {@link Iterable} of their combined passwords
-     *
-     * @param providers The providers - ignored if {@code null}/empty (i.e., returns an empty iterable instance)
-     * @return The wrapping iterable
-     */
-    static Iterable<String> iterableOf(Collection<? extends PasswordIdentityProvider> providers) {
-        Iterable<Supplier<Iterable<String>>> passwordSuppliers =
-                GenericUtils.<PasswordIdentityProvider, Supplier<Iterable<String>>>wrapIterable(providers, p -> p::loadPasswords);
-        return GenericUtils.multiIterableSuppliers(passwordSuppliers);
-    }
-
-    /**
-     * Wraps a group of passwords into a {@link PasswordIdentityProvider}
-     *
-     * @param passwords The passwords - ignored if {@code null}/empty
-     * (i.e., returns {@link #EMPTY_PASSWORDS_PROVIDER})
-     * @return The provider wrapper
-     */
-    static PasswordIdentityProvider wrapPasswords(String... passwords) {
-        return wrapPasswords(GenericUtils.asList(passwords));
-    }
-
-    /**
-     * Wraps a group of passwords into a {@link PasswordIdentityProvider}
-     *
-     * @param passwords The passwords {@link Iterable} - ignored if {@code null}
-     * (i.e., returns {@link #EMPTY_PASSWORDS_PROVIDER})
-     * @return The provider wrapper
-     */
-    static PasswordIdentityProvider wrapPasswords(Iterable<String> passwords) {
-        return (passwords == null) ? EMPTY_PASSWORDS_PROVIDER : () -> passwords;
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/auth/password/UserAuthPassword.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/password/UserAuthPassword.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/password/UserAuthPassword.java
index 53ec529..6785305 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/password/UserAuthPassword.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/auth/password/UserAuthPassword.java
@@ -48,7 +48,7 @@ public class UserAuthPassword extends AbstractUserAuth {
     @Override
     public void init(ClientSession session, String service) throws Exception {
         super.init(session, service);
-        passwords = PasswordIdentityProvider.iteratorOf(session);
+        passwords = ClientSession.passwordIteratorOf(session);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/PublicKeyIdentity.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/PublicKeyIdentity.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/PublicKeyIdentity.java
deleted file mode 100644
index 51444ae..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/PublicKeyIdentity.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sshd.client.auth.pubkey;
-
-import java.security.PublicKey;
-
-/**
- * Represents a public key identity
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface PublicKeyIdentity {
-    /**
-     * @return The {@link PublicKey} identity value
-     */
-    PublicKey getPublicKey();
-
-    /**
-     * Proves the public key identity by signing the given data
-     *
-     * @param data Data to sign
-     * @return Signed data - using the identity
-     * @throws Exception If failed to sign the data
-     */
-    byte[] sign(byte[] data) throws Exception;
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/UserAuthPublicKeyIterator.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/UserAuthPublicKeyIterator.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/UserAuthPublicKeyIterator.java
index 202b0a8..02ce1dc 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/UserAuthPublicKeyIterator.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/UserAuthPublicKeyIterator.java
@@ -70,7 +70,7 @@ public class UserAuthPublicKeyIterator extends AbstractKeyPairIterator<PublicKey
             }
         }
 
-        identities.add(Stream.of(KeyIdentityProvider.providerOf(session))
+        identities.add(Stream.of(ClientSession.providerOf(session))
             .map(KeyIdentityProvider::loadKeys)
             .flatMap(GenericUtils::stream)
             .map(kp -> new KeyPairIdentity(signatureFactories, session, kp)));

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/config/SshClientConfigFileReader.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/SshClientConfigFileReader.java b/sshd-core/src/main/java/org/apache/sshd/client/config/SshClientConfigFileReader.java
index ccd8f26..c205377 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/SshClientConfigFileReader.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/config/SshClientConfigFileReader.java
@@ -32,7 +32,8 @@ public final class SshClientConfigFileReader {
         throw new UnsupportedOperationException("No instance allowed");
     }
 
-    public static <C extends SshClient> C configure(C client, PropertyResolver props, boolean lenient, boolean ignoreUnsupported) {
+    public static <C extends SshClient> C configure(
+            C client, PropertyResolver props, boolean lenient, boolean ignoreUnsupported) {
         SshConfigFileReader.configure((AbstractFactoryManager) client, props, lenient, ignoreUnsupported);
         SshConfigFileReader.configureKeyExchanges(client, props, lenient, ClientBuilder.DH2KEX, ignoreUnsupported);
         return client;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/ConfigFileHostEntryResolver.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/ConfigFileHostEntryResolver.java b/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/ConfigFileHostEntryResolver.java
deleted file mode 100644
index f1c9ea8..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/ConfigFileHostEntryResolver.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.client.config.hosts;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.LinkOption;
-import java.nio.file.Path;
-import java.util.Collection;
-import java.util.List;
-import java.util.Objects;
-import java.util.concurrent.atomic.AtomicReference;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.common.util.io.ModifiableFileWatcher;
-
-/**
- * Watches for changes in a configuration file and automatically reloads any changes
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class ConfigFileHostEntryResolver extends ModifiableFileWatcher implements HostConfigEntryResolver {
-    private final AtomicReference<HostConfigEntryResolver> delegateHolder = // assumes initially empty
-            new AtomicReference<>(HostConfigEntryResolver.EMPTY);
-
-    public ConfigFileHostEntryResolver(File file) {
-        this(Objects.requireNonNull(file, "No file to watch").toPath());
-    }
-
-    public ConfigFileHostEntryResolver(Path file) {
-        this(file, IoUtils.EMPTY_LINK_OPTIONS);
-    }
-
-    public ConfigFileHostEntryResolver(Path file, LinkOption... options) {
-        super(file, options);
-    }
-
-    @Override
-    public HostConfigEntry resolveEffectiveHost(String host, int port, String username) throws IOException {
-        try {
-            HostConfigEntryResolver delegate = Objects.requireNonNull(resolveEffectiveResolver(host, port, username), "No delegate");
-            HostConfigEntry entry = delegate.resolveEffectiveHost(host, port, username);
-            if (log.isDebugEnabled()) {
-                log.debug("resolveEffectiveHost({}@{}:{}) => {}", username, host, port, entry);
-            }
-
-            return entry;
-        } catch (Throwable e) {
-            if (log.isDebugEnabled()) {
-                log.debug("resolveEffectiveHost({}@{}:{}) failed ({}) to resolve: {}",
-                          username, host, port, e.getClass().getSimpleName(), e.getMessage());
-            }
-
-            if (log.isTraceEnabled()) {
-                log.trace("resolveEffectiveHost(" + username + "@" + host + ":" + port + ") resolution failure details", e);
-            }
-            if (e instanceof IOException) {
-                throw (IOException) e;
-            } else {
-                throw new IOException(e);
-            }
-        }
-    }
-
-    protected HostConfigEntryResolver resolveEffectiveResolver(String host, int port, String username) throws IOException {
-        if (checkReloadRequired()) {
-            delegateHolder.set(HostConfigEntryResolver.EMPTY);  // start fresh
-
-            Path path = getPath();
-            if (exists()) {
-                Collection<HostConfigEntry> entries = reloadHostConfigEntries(path, host, port, username);
-                if (GenericUtils.size(entries) > 0) {
-                    delegateHolder.set(HostConfigEntry.toHostConfigEntryResolver(entries));
-                }
-            } else {
-                log.info("resolveEffectiveResolver({}@{}:{}) no configuration file at {}", username, host, port, path);
-            }
-        }
-
-        return delegateHolder.get();
-    }
-
-    protected List<HostConfigEntry> reloadHostConfigEntries(Path path, String host, int port, String username) throws IOException {
-        List<HostConfigEntry> entries = HostConfigEntry.readHostConfigEntries(path);
-        log.info("resolveEffectiveResolver({}@{}:{}) loaded {} entries from {}", username, host, port, GenericUtils.size(entries), path);
-        updateReloadAttributes();
-        return entries;
-    }
-}


[34/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common utilities code from sshd-core into sshd-common (new artifact)

Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/Ed25519PublicKeyDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/Ed25519PublicKeyDecoder.java b/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/Ed25519PublicKeyDecoder.java
new file mode 100644
index 0000000..793965c
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/Ed25519PublicKeyDecoder.java
@@ -0,0 +1,97 @@
+/*
+ * 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.security.eddsa;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.KeyPairGenerator;
+import java.util.Collections;
+import java.util.Objects;
+
+import org.apache.sshd.common.config.keys.KeyEntryResolver;
+import org.apache.sshd.common.config.keys.impl.AbstractPublicKeyEntryDecoder;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+import net.i2p.crypto.eddsa.EdDSAPrivateKey;
+import net.i2p.crypto.eddsa.EdDSAPublicKey;
+import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec;
+import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class Ed25519PublicKeyDecoder extends AbstractPublicKeyEntryDecoder<EdDSAPublicKey, EdDSAPrivateKey> {
+    public static final Ed25519PublicKeyDecoder INSTANCE = new Ed25519PublicKeyDecoder();
+
+    private Ed25519PublicKeyDecoder() {
+        super(EdDSAPublicKey.class, EdDSAPrivateKey.class, Collections.unmodifiableList(Collections.singletonList(KeyPairProvider.SSH_ED25519)));
+    }
+
+    @Override
+    public EdDSAPublicKey clonePublicKey(EdDSAPublicKey key) throws GeneralSecurityException {
+        if (key == null) {
+            return null;
+        } else {
+            return generatePublicKey(new EdDSAPublicKeySpec(key.getA(), key.getParams()));
+        }
+    }
+
+    @Override
+    public EdDSAPrivateKey clonePrivateKey(EdDSAPrivateKey key) throws GeneralSecurityException {
+        if (key == null) {
+            return null;
+        } else {
+            return generatePrivateKey(new EdDSAPrivateKeySpec(key.getSeed(), key.getParams()));
+        }
+    }
+
+    @Override
+    public KeyPairGenerator getKeyPairGenerator() throws GeneralSecurityException {
+        return SecurityUtils.getKeyPairGenerator(SecurityUtils.EDDSA);
+    }
+
+    @Override
+    public String encodePublicKey(OutputStream s, EdDSAPublicKey key) throws IOException {
+        Objects.requireNonNull(key, "No public key provided");
+        KeyEntryResolver.encodeString(s, KeyPairProvider.SSH_ED25519);
+        byte[] seed = getSeedValue(key);
+        KeyEntryResolver.writeRLEBytes(s, seed);
+        return KeyPairProvider.SSH_ED25519;
+    }
+
+    @Override
+    public KeyFactory getKeyFactoryInstance() throws GeneralSecurityException {
+        return SecurityUtils.getKeyFactory(SecurityUtils.EDDSA);
+    }
+
+    @Override
+    public EdDSAPublicKey decodePublicKey(String keyType, InputStream keyData) throws IOException, GeneralSecurityException {
+        byte[] seed = KeyEntryResolver.readRLEBytes(keyData);
+        return EdDSAPublicKey.class.cast(SecurityUtils.generateEDDSAPublicKey(keyType, seed));
+    }
+
+    public static byte[] getSeedValue(EdDSAPublicKey key) {
+        // a bit of reverse-engineering on the EdDSAPublicKeySpec
+        return (key == null) ? null : key.getAbyte();
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderRegistrar.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderRegistrar.java b/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderRegistrar.java
new file mode 100644
index 0000000..61f16e9
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderRegistrar.java
@@ -0,0 +1,104 @@
+/*
+ * 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.security.eddsa;
+
+import java.security.KeyFactory;
+import java.security.KeyPairGenerator;
+import java.security.Provider;
+import java.security.Signature;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ReflectionUtils;
+import org.apache.sshd.common.util.security.AbstractSecurityProviderRegistrar;
+import org.apache.sshd.common.util.security.SecurityUtils;
+import org.apache.sshd.common.util.threads.ThreadUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class EdDSASecurityProviderRegistrar extends AbstractSecurityProviderRegistrar {
+    public static final String PROVIDER_CLASS = "net.i2p.crypto.eddsa.EdDSASecurityProvider";
+    // Do not define a static registrar instance to minimize class loading issues
+    private final AtomicReference<Boolean> supportHolder = new AtomicReference<>(null);
+
+    public EdDSASecurityProviderRegistrar() {
+        super(SecurityUtils.EDDSA);
+    }
+
+    @Override
+    public boolean isEnabled() {
+        if (!super.isEnabled()) {
+            return false;
+        }
+
+        // For backward compatibility
+        return this.getBooleanProperty(SecurityUtils.EDDSA_SUPPORTED_PROP, true);
+    }
+
+    @Override
+    public Provider getSecurityProvider() {
+        try {
+            return getOrCreateProvider(PROVIDER_CLASS);
+        } catch (ReflectiveOperationException t) {
+            Throwable e = GenericUtils.peelException(t);
+            log.error("getSecurityProvider({}) failed ({}) to instantiate {}: {}",
+                      getName(), e.getClass().getSimpleName(), PROVIDER_CLASS, e.getMessage());
+            if (e instanceof RuntimeException) {
+                throw (RuntimeException) e;
+            }
+
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public boolean isSecurityEntitySupported(Class<?> entityType, String name) {
+        if (!isSupported()) {
+            return false;
+        }
+
+        if (KeyPairGenerator.class.isAssignableFrom(entityType)
+                || KeyFactory.class.isAssignableFrom(entityType)) {
+            return Objects.compare(name, getName(), String.CASE_INSENSITIVE_ORDER) == 0;
+        } else if (Signature.class.isAssignableFrom(entityType)) {
+            return Objects.compare(SecurityUtils.CURVE_ED25519_SHA512, name, String.CASE_INSENSITIVE_ORDER) == 0;
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public boolean isSupported() {
+        Boolean supported;
+        synchronized (supportHolder) {
+            supported = supportHolder.get();
+            if (supported != null) {
+                return supported.booleanValue();
+            }
+
+            ClassLoader cl = ThreadUtils.resolveDefaultClassLoader(getClass());
+            supported = ReflectionUtils.isClassAvailable(cl, "net.i2p.crypto.eddsa.EdDSAKey");
+            supportHolder.set(supported);
+        }
+
+        return supported.booleanValue();
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderUtils.java
new file mode 100644
index 0000000..242f550
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderUtils.java
@@ -0,0 +1,201 @@
+/*
+ * 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.security.eddsa;
+
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.util.Arrays;
+import java.util.Objects;
+
+import org.apache.sshd.common.config.keys.PrivateKeyEntryDecoder;
+import org.apache.sshd.common.config.keys.PublicKeyEntryDecoder;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+import net.i2p.crypto.eddsa.EdDSAEngine;
+import net.i2p.crypto.eddsa.EdDSAKey;
+import net.i2p.crypto.eddsa.EdDSAPrivateKey;
+import net.i2p.crypto.eddsa.EdDSAPublicKey;
+import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
+import net.i2p.crypto.eddsa.spec.EdDSAParameterSpec;
+import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec;
+import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class EdDSASecurityProviderUtils {
+    // See EdDSANamedCurveTable
+    public static final String CURVE_ED25519_SHA512 = "Ed25519";
+
+    private EdDSASecurityProviderUtils() {
+        throw new UnsupportedOperationException("No instance");
+    }
+
+    public static Class<? extends PublicKey> getEDDSAPublicKeyType() {
+        return EdDSAPublicKey.class;
+    }
+
+    public static Class<? extends PrivateKey> getEDDSAPrivateKeyType() {
+        return EdDSAPrivateKey.class;
+    }
+
+    public static int getEDDSAKeySize(Key key) {
+        return (SecurityUtils.isEDDSACurveSupported() && (key instanceof EdDSAKey)) ? 256 : -1;
+    }
+
+    public static boolean compareEDDSAPPublicKeys(PublicKey k1, PublicKey k2) {
+        if (!SecurityUtils.isEDDSACurveSupported()) {
+            return false;
+        }
+
+        if ((k1 instanceof EdDSAPublicKey) && (k2 instanceof EdDSAPublicKey)) {
+            if (Objects.equals(k1, k2)) {
+                return true;
+            } else if (k1 == null || k2 == null) {
+                return false;   // both null is covered by Objects#equals
+            }
+
+            EdDSAPublicKey ed1 = (EdDSAPublicKey) k1;
+            EdDSAPublicKey ed2 = (EdDSAPublicKey) k2;
+            return Arrays.equals(ed1.getAbyte(), ed2.getAbyte())
+                && compareEDDSAKeyParams(ed1.getParams(), ed2.getParams());
+        }
+
+        return false;
+    }
+
+    public static boolean isEDDSASignatureAlgorithm(String algorithm) {
+        return EdDSAEngine.SIGNATURE_ALGORITHM.equalsIgnoreCase(algorithm);
+    }
+
+    public static EdDSAPublicKey recoverEDDSAPublicKey(PrivateKey key) throws GeneralSecurityException {
+        ValidateUtils.checkTrue(SecurityUtils.isEDDSACurveSupported(), SecurityUtils.EDDSA + " not supported");
+        if (!(key instanceof EdDSAPrivateKey)) {
+            throw new InvalidKeyException("Private key is not " + SecurityUtils.EDDSA);
+        }
+
+        EdDSAPrivateKey prvKey = (EdDSAPrivateKey) key;
+        EdDSAPublicKeySpec keySpec = new EdDSAPublicKeySpec(prvKey.getAbyte(), prvKey.getParams());
+        KeyFactory factory = SecurityUtils.getKeyFactory(SecurityUtils.EDDSA);
+        return EdDSAPublicKey.class.cast(factory.generatePublic(keySpec));
+    }
+
+    public static org.apache.sshd.common.signature.Signature getEDDSASignature() {
+        ValidateUtils.checkTrue(SecurityUtils.isEDDSACurveSupported(), SecurityUtils.EDDSA + " not supported");
+        return new SignatureEd25519();
+    }
+
+    public static boolean isEDDSAKeyFactoryAlgorithm(String algorithm) {
+        return SecurityUtils.EDDSA.equalsIgnoreCase(algorithm);
+    }
+
+    public static boolean isEDDSAKeyPairGeneratorAlgorithm(String algorithm) {
+        return SecurityUtils.EDDSA.equalsIgnoreCase(algorithm);
+    }
+
+    public static PublicKeyEntryDecoder<? extends PublicKey, ? extends PrivateKey> getEDDSAPublicKeyEntryDecoder() {
+        ValidateUtils.checkTrue(SecurityUtils.isEDDSACurveSupported(), SecurityUtils.EDDSA + " not supported");
+        return Ed25519PublicKeyDecoder.INSTANCE;
+    }
+
+    public static PrivateKeyEntryDecoder<? extends PublicKey, ? extends PrivateKey> getOpenSSHEDDSAPrivateKeyEntryDecoder() {
+        ValidateUtils.checkTrue(SecurityUtils.isEDDSACurveSupported(), SecurityUtils.EDDSA + " not supported");
+        return OpenSSHEd25519PrivateKeyEntryDecoder.INSTANCE;
+    }
+
+    public static boolean compareEDDSAPrivateKeys(PrivateKey k1, PrivateKey k2) {
+        if (!SecurityUtils.isEDDSACurveSupported()) {
+            return false;
+        }
+
+        if ((k1 instanceof EdDSAPrivateKey) && (k2 instanceof EdDSAPrivateKey)) {
+            if (Objects.equals(k1, k2)) {
+                return true;
+            } else if (k1 == null || k2 == null) {
+                return false;   // both null is covered by Objects#equals
+            }
+
+            EdDSAPrivateKey ed1 = (EdDSAPrivateKey) k1;
+            EdDSAPrivateKey ed2 = (EdDSAPrivateKey) k2;
+            return Arrays.equals(ed1.getSeed(), ed2.getSeed())
+                && compareEDDSAKeyParams(ed1.getParams(), ed2.getParams());
+        }
+
+        return false;
+    }
+
+    public static boolean compareEDDSAKeyParams(EdDSAParameterSpec s1, EdDSAParameterSpec s2) {
+        if (Objects.equals(s1, s2)) {
+            return true;
+        } else if (s1 == null || s2 == null) {
+            return false;   // both null is covered by Objects#equals
+        } else {
+            return Objects.equals(s1.getHashAlgorithm(), s2.getHashAlgorithm())
+                && Objects.equals(s1.getCurve(), s2.getCurve())
+                && Objects.equals(s1.getB(), s2.getB());
+        }
+    }
+
+    public static PublicKey generateEDDSAPublicKey(byte[] seed) throws GeneralSecurityException {
+        if (!SecurityUtils.isEDDSACurveSupported()) {
+            throw new NoSuchAlgorithmException(SecurityUtils.EDDSA + " not supported");
+        }
+
+        EdDSAParameterSpec params = EdDSANamedCurveTable.getByName(CURVE_ED25519_SHA512);
+        EdDSAPublicKeySpec keySpec = new EdDSAPublicKeySpec(seed, params);
+        KeyFactory factory = SecurityUtils.getKeyFactory(SecurityUtils.EDDSA);
+        return factory.generatePublic(keySpec);
+    }
+
+    public static PrivateKey generateEDDSAPrivateKey(byte[] seed) throws GeneralSecurityException {
+        if (!SecurityUtils.isEDDSACurveSupported()) {
+            throw new NoSuchAlgorithmException(SecurityUtils.EDDSA + " not supported");
+        }
+
+        EdDSAParameterSpec params = EdDSANamedCurveTable.getByName(CURVE_ED25519_SHA512);
+        EdDSAPrivateKeySpec keySpec = new EdDSAPrivateKeySpec(seed, params);
+        KeyFactory factory = SecurityUtils.getKeyFactory(SecurityUtils.EDDSA);
+        return factory.generatePrivate(keySpec);
+    }
+
+    public static <B extends Buffer> B putRawEDDSAPublicKey(B buffer, PublicKey key) {
+        ValidateUtils.checkTrue(SecurityUtils.isEDDSACurveSupported(), SecurityUtils.EDDSA + " not supported");
+        EdDSAPublicKey edKey = ValidateUtils.checkInstanceOf(key, EdDSAPublicKey.class, "Not an EDDSA public key: %s", key);
+        byte[] seed = Ed25519PublicKeyDecoder.getSeedValue(edKey);
+        ValidateUtils.checkNotNull(seed, "No seed extracted from key: %s", edKey.getA());
+        buffer.putString(KeyPairProvider.SSH_ED25519);
+        buffer.putBytes(seed);
+        return buffer;
+    }
+
+    public static <B extends Buffer> B putEDDSAKeyPair(B buffer, PublicKey pubKey, PrivateKey prvKey) {
+        ValidateUtils.checkTrue(SecurityUtils.isEDDSACurveSupported(), SecurityUtils.EDDSA + " not supported");
+        ValidateUtils.checkInstanceOf(pubKey, EdDSAPublicKey.class, "Not an EDDSA public key: %s", pubKey);
+        ValidateUtils.checkInstanceOf(prvKey, EdDSAPrivateKey.class, "Not an EDDSA private key: %s", prvKey);
+        throw new UnsupportedOperationException("Full SSHD-440 implementation N/A");
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/OpenSSHEd25519PrivateKeyEntryDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/OpenSSHEd25519PrivateKeyEntryDecoder.java b/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/OpenSSHEd25519PrivateKeyEntryDecoder.java
new file mode 100644
index 0000000..4888818
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/OpenSSHEd25519PrivateKeyEntryDecoder.java
@@ -0,0 +1,172 @@
+/*
+ * 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.security.eddsa;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Locale;
+import java.util.Objects;
+
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.KeyEntryResolver;
+import org.apache.sshd.common.config.keys.impl.AbstractPrivateKeyEntryDecoder;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+import net.i2p.crypto.eddsa.EdDSAPrivateKey;
+import net.i2p.crypto.eddsa.EdDSAPublicKey;
+import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
+import net.i2p.crypto.eddsa.spec.EdDSAParameterSpec;
+import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec;
+import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class OpenSSHEd25519PrivateKeyEntryDecoder extends AbstractPrivateKeyEntryDecoder<EdDSAPublicKey, EdDSAPrivateKey> {
+    public static final OpenSSHEd25519PrivateKeyEntryDecoder INSTANCE = new OpenSSHEd25519PrivateKeyEntryDecoder();
+    private static final int PK_SIZE = 32;
+    private static final int SK_SIZE = 32;
+    private static final int KEYPAIR_SIZE = PK_SIZE + SK_SIZE;
+
+    public OpenSSHEd25519PrivateKeyEntryDecoder() {
+        super(EdDSAPublicKey.class, EdDSAPrivateKey.class, Collections.unmodifiableList(Collections.singletonList(KeyPairProvider.SSH_ED25519)));
+    }
+
+    @Override
+    public EdDSAPrivateKey decodePrivateKey(String keyType, FilePasswordProvider passwordProvider, InputStream keyData)
+            throws IOException, GeneralSecurityException {
+        if (!KeyPairProvider.SSH_ED25519.equals(keyType)) {
+            throw new InvalidKeyException("Unsupported key type: " + keyType);
+        }
+
+        if (!SecurityUtils.isEDDSACurveSupported()) {
+            throw new NoSuchAlgorithmException(SecurityUtils.EDDSA + " provider not supported");
+        }
+
+        // ed25519 bernstein naming: pk .. public key, sk .. secret key
+        // we expect to find two byte arrays with the following structure (type:size):
+        // [pk:32], [sk:32,pk:32]
+
+        byte[] pk = KeyEntryResolver.readRLEBytes(keyData);
+        byte[] keypair = KeyEntryResolver.readRLEBytes(keyData);
+
+        if (pk.length != PK_SIZE) {
+            throw new InvalidKeyException(String.format(Locale.ENGLISH, "Unexpected pk size: %s (expected %s)", pk.length, PK_SIZE));
+        }
+
+        if (keypair.length != KEYPAIR_SIZE) {
+            throw new InvalidKeyException(String.format(Locale.ENGLISH, "Unexpected keypair size: %s (expected %s)", keypair.length, KEYPAIR_SIZE));
+        }
+
+        byte[] sk = Arrays.copyOf(keypair, SK_SIZE);
+
+        // verify that the keypair contains the expected pk
+        // yes, it's stored redundant, this seems to mimic the output structure of the keypair generation interface
+        if (!Arrays.equals(pk, Arrays.copyOfRange(keypair, SK_SIZE, KEYPAIR_SIZE))) {
+            throw new InvalidKeyException("Keypair did not contain the public key.");
+        }
+
+        // create the private key
+        EdDSAParameterSpec params = EdDSANamedCurveTable.getByName(EdDSASecurityProviderUtils.CURVE_ED25519_SHA512);
+        EdDSAPrivateKey privateKey = generatePrivateKey(new EdDSAPrivateKeySpec(sk, params));
+
+        // the private key class contains the calculated public key (Abyte)
+        // pointers to the corresponding code:
+        // EdDSAPrivateKeySpec.EdDSAPrivateKeySpec(byte[], EdDSAParameterSpec): A = spec.getB().scalarMultiply(a);
+        // EdDSAPrivateKey.EdDSAPrivateKey(EdDSAPrivateKeySpec): this.Abyte = this.A.toByteArray();
+
+        // we can now verify the generated pk matches the one we read
+        if (!Arrays.equals(privateKey.getAbyte(), pk)) {
+            throw new InvalidKeyException("The provided pk does NOT match the computed pk for the given sk.");
+        }
+
+        return privateKey;
+    }
+
+    @Override
+    public String encodePrivateKey(OutputStream s, EdDSAPrivateKey key) throws IOException {
+        Objects.requireNonNull(key, "No private key provided");
+
+        // ed25519 bernstein naming: pk .. public key, sk .. secret key
+        // we are expected to write the following arrays (type:size):
+        // [pk:32], [sk:32,pk:32]
+
+        byte[] sk = key.getSeed();
+        byte[] pk = key.getAbyte();
+
+        Objects.requireNonNull(sk, "No seed");
+
+        byte[] keypair = new byte[KEYPAIR_SIZE];
+        System.arraycopy(sk, 0, keypair, 0, SK_SIZE);
+        System.arraycopy(pk, 0, keypair, SK_SIZE, PK_SIZE);
+
+        KeyEntryResolver.writeRLEBytes(s, pk);
+        KeyEntryResolver.writeRLEBytes(s, keypair);
+
+        return KeyPairProvider.SSH_ED25519;
+    }
+
+    @Override
+    public boolean isPublicKeyRecoverySupported() {
+        return true;
+    }
+
+    @Override
+    public EdDSAPublicKey recoverPublicKey(EdDSAPrivateKey prvKey) throws GeneralSecurityException {
+        return EdDSASecurityProviderUtils.recoverEDDSAPublicKey(prvKey);
+    }
+
+    @Override
+    public EdDSAPublicKey clonePublicKey(EdDSAPublicKey key) throws GeneralSecurityException {
+        if (key == null) {
+            return null;
+        } else {
+            return generatePublicKey(new EdDSAPublicKeySpec(key.getA(), key.getParams()));
+        }
+    }
+
+    @Override
+    public EdDSAPrivateKey clonePrivateKey(EdDSAPrivateKey key) throws GeneralSecurityException {
+        if (key == null) {
+            return null;
+        } else {
+            return generatePrivateKey(new EdDSAPrivateKeySpec(key.getSeed(), key.getParams()));
+        }
+    }
+
+    @Override
+    public KeyPairGenerator getKeyPairGenerator() throws GeneralSecurityException {
+        return SecurityUtils.getKeyPairGenerator(SecurityUtils.EDDSA);
+    }
+
+    @Override
+    public KeyFactory getKeyFactoryInstance() throws GeneralSecurityException {
+        return SecurityUtils.getKeyFactory(SecurityUtils.EDDSA);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/SignatureEd25519.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/SignatureEd25519.java b/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/SignatureEd25519.java
new file mode 100644
index 0000000..012be95
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/SignatureEd25519.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util.security.eddsa;
+
+import java.util.Map;
+
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.signature.AbstractSignature;
+import org.apache.sshd.common.util.ValidateUtils;
+
+import net.i2p.crypto.eddsa.EdDSAEngine;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class SignatureEd25519 extends AbstractSignature {
+    public SignatureEd25519() {
+        super(EdDSAEngine.SIGNATURE_ALGORITHM);
+    }
+
+    @Override
+    public boolean verify(byte[] sig) throws Exception {
+        byte[] data = sig;
+        Map.Entry<String, byte[]> encoding = extractEncodedSignature(data);
+        if (encoding != null) {
+            String keyType = encoding.getKey();
+            ValidateUtils.checkTrue(KeyPairProvider.SSH_ED25519.equals(keyType), "Mismatched key type: %s", keyType);
+            data = encoding.getValue();
+        }
+
+        return doVerify(data);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/threads/CloseableExecutorService.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/threads/CloseableExecutorService.java b/sshd-common/src/main/java/org/apache/sshd/common/util/threads/CloseableExecutorService.java
new file mode 100644
index 0000000..3b9beeb
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/threads/CloseableExecutorService.java
@@ -0,0 +1,28 @@
+/*
+ * 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.threads;
+
+import java.util.concurrent.ExecutorService;
+
+import org.apache.sshd.common.Closeable;
+
+public interface CloseableExecutorService extends ExecutorService, Closeable {
+    // Nothing extra
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/threads/ExecutorServiceCarrier.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/threads/ExecutorServiceCarrier.java b/sshd-common/src/main/java/org/apache/sshd/common/util/threads/ExecutorServiceCarrier.java
new file mode 100644
index 0000000..b44bd46
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/threads/ExecutorServiceCarrier.java
@@ -0,0 +1,31 @@
+/*
+ * 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.threads;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface ExecutorServiceCarrier {
+    /**
+     * @return The {@link CloseableExecutorService} to use
+     */
+    CloseableExecutorService getExecutorService();
+
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/threads/NoCloseExecutor.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/threads/NoCloseExecutor.java b/sshd-common/src/main/java/org/apache/sshd/common/util/threads/NoCloseExecutor.java
new file mode 100644
index 0000000..cb42805
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/threads/NoCloseExecutor.java
@@ -0,0 +1,160 @@
+/*
+ * 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.threads;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.apache.sshd.common.future.CloseFuture;
+import org.apache.sshd.common.future.DefaultCloseFuture;
+import org.apache.sshd.common.future.SshFutureListener;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * Wraps an {@link ExecutorService} as a {@link CloseableExecutorService}
+ * and avoids calling its {@code shutdown} methods when the wrapper is shut down
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class NoCloseExecutor implements CloseableExecutorService {
+    protected final ExecutorService executor;
+    protected final CloseFuture closeFuture;
+
+    public NoCloseExecutor(ExecutorService executor) {
+        this.executor = executor;
+        closeFuture = new DefaultCloseFuture(null, null);
+    }
+
+    @Override
+    public <T> Future<T> submit(Callable<T> task) {
+        ValidateUtils.checkState(!isShutdown(), "Executor has been shut down");
+        return executor.submit(task);
+    }
+
+    @Override
+    public <T> Future<T> submit(Runnable task, T result) {
+        ValidateUtils.checkState(!isShutdown(), "Executor has been shut down");
+        return executor.submit(task, result);
+    }
+
+    @Override
+    public Future<?> submit(Runnable task) {
+        ValidateUtils.checkState(!isShutdown(), "Executor has been shut down");
+        return executor.submit(task);
+    }
+
+    @Override
+    public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
+            throws InterruptedException {
+        ValidateUtils.checkState(!isShutdown(), "Executor has been shut down");
+        return executor.invokeAll(tasks);
+    }
+
+    @Override
+    public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
+            throws InterruptedException {
+        ValidateUtils.checkState(!isShutdown(), "Executor has been shut down");
+        return executor.invokeAll(tasks, timeout, unit);
+    }
+
+    @Override
+    public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
+            throws InterruptedException, ExecutionException {
+        ValidateUtils.checkState(!isShutdown(), "Executor has been shut down");
+        return executor.invokeAny(tasks);
+    }
+
+    @Override
+    public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
+            throws InterruptedException, ExecutionException, TimeoutException {
+        ValidateUtils.checkState(!isShutdown(), "Executor has been shut down");
+        return executor.invokeAny(tasks, timeout, unit);
+    }
+
+    @Override
+    public void execute(Runnable command) {
+        ValidateUtils.checkState(!isShutdown(), "Executor has been shut down");
+        executor.execute(command);
+    }
+
+    @Override
+    public void shutdown() {
+        close(true);
+    }
+
+    @Override
+    public List<Runnable> shutdownNow() {
+        close(true);
+        return Collections.emptyList();
+    }
+
+    @Override
+    public boolean isShutdown() {
+        return isClosed();
+    }
+
+    @Override
+    public boolean isTerminated() {
+        return isClosed();
+    }
+
+    @Override
+    public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
+        try {
+            return closeFuture.await(timeout, unit);
+        } catch (IOException e) {
+            throw (InterruptedException) new InterruptedException().initCause(e);
+        }
+    }
+
+    @Override
+    public CloseFuture close(boolean immediately) {
+        closeFuture.setClosed();
+        return closeFuture;
+    }
+
+    @Override
+    public void addCloseFutureListener(SshFutureListener<CloseFuture> listener) {
+        closeFuture.addListener(listener);
+    }
+
+    @Override
+    public void removeCloseFutureListener(SshFutureListener<CloseFuture> listener) {
+        closeFuture.removeListener(listener);
+    }
+
+    @Override
+    public boolean isClosed() {
+        return closeFuture.isClosed();
+    }
+
+    @Override
+    public boolean isClosing() {
+        return isClosed();
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/threads/SshThreadPoolExecutor.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/threads/SshThreadPoolExecutor.java b/sshd-common/src/main/java/org/apache/sshd/common/util/threads/SshThreadPoolExecutor.java
new file mode 100644
index 0000000..ccaa655
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/threads/SshThreadPoolExecutor.java
@@ -0,0 +1,138 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util.threads;
+
+import java.util.List;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.RejectedExecutionHandler;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.sshd.common.future.CloseFuture;
+import org.apache.sshd.common.future.SshFutureListener;
+import org.apache.sshd.common.util.closeable.AbstractCloseable;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class SshThreadPoolExecutor extends ThreadPoolExecutor implements CloseableExecutorService {
+    protected final DelegateCloseable closeable = new DelegateCloseable();
+
+    protected class DelegateCloseable extends AbstractCloseable {
+        protected DelegateCloseable() {
+            super();
+        }
+
+        @Override
+        protected CloseFuture doCloseGracefully() {
+            shutdown();
+            return closeFuture;
+        }
+
+        @Override
+        protected void doCloseImmediately() {
+            shutdownNow();
+            super.doCloseImmediately();
+        }
+
+        protected void setClosed() {
+            closeFuture.setClosed();
+        }
+    }
+
+    public SshThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
+        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
+    }
+
+    public SshThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
+            BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
+        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
+    }
+
+    public SshThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
+            BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {
+        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
+    }
+
+    public SshThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
+            BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
+        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
+    }
+
+    @Override
+    protected void terminated() {
+        closeable.doCloseImmediately();
+    }
+
+    @Override
+    public void shutdown() {
+        super.shutdown();
+    }
+
+    @Override
+    public List<Runnable> shutdownNow() {
+        return super.shutdownNow();
+    }
+
+    @Override
+    public boolean isShutdown() {
+        return super.isShutdown();
+    }
+
+    @Override
+    public boolean isTerminating() {
+        return super.isTerminating();
+    }
+
+    @Override
+    public boolean isTerminated() {
+        return super.isTerminated();
+    }
+
+    @Override
+    public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
+        return super.awaitTermination(timeout, unit);
+    }
+
+    @Override
+    public CloseFuture close(boolean immediately) {
+        return closeable.close(immediately);
+    }
+
+    @Override
+    public void addCloseFutureListener(SshFutureListener<CloseFuture> listener) {
+        closeable.addCloseFutureListener(listener);
+    }
+
+    @Override
+    public void removeCloseFutureListener(SshFutureListener<CloseFuture> listener) {
+        closeable.removeCloseFutureListener(listener);
+    }
+
+    @Override
+    public boolean isClosed() {
+        return closeable.isClosed();
+    }
+
+    @Override
+    public boolean isClosing() {
+        return closeable.isClosing();
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/threads/SshdThreadFactory.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/threads/SshdThreadFactory.java b/sshd-common/src/main/java/org/apache/sshd/common/util/threads/SshdThreadFactory.java
new file mode 100644
index 0000000..5dc0c7b
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/threads/SshdThreadFactory.java
@@ -0,0 +1,78 @@
+/*
+ * 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.threads;
+
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
+
+/**
+ * Default {@link ThreadFactory} used by {@link ThreadUtils} to create
+ * thread pools if user did provide one
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class SshdThreadFactory extends AbstractLoggingBean implements ThreadFactory {
+    private final ThreadGroup group;
+    private final AtomicInteger threadNumber = new AtomicInteger(1);
+    private final String namePrefix;
+
+    public SshdThreadFactory(String name) {
+        SecurityManager s = System.getSecurityManager();
+        group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
+        String effectiveName = name.replace(' ', '-');
+        namePrefix = "sshd-" + effectiveName + "-thread-";
+    }
+
+    @Override
+    public Thread newThread(Runnable r) {
+        Thread t;
+        try {
+            // see SSHD-668
+            if (System.getSecurityManager() != null) {
+                t = AccessController.doPrivileged((PrivilegedExceptionAction<Thread>) () ->
+                        new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0));
+            } else {
+                t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0);
+            }
+        } catch (PrivilegedActionException e) {
+            Exception err = e.getException();
+            if (err instanceof RuntimeException) {
+                throw (RuntimeException) err;
+            } else {
+                throw new RuntimeException(err);
+            }
+        }
+
+        if (!t.isDaemon()) {
+            t.setDaemon(true);
+        }
+        if (t.getPriority() != Thread.NORM_PRIORITY) {
+            t.setPriority(Thread.NORM_PRIORITY);
+        }
+        if (log.isTraceEnabled()) {
+            log.trace("newThread({})[{}] runnable={}", group, t.getName(), r);
+        }
+        return t;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/threads/ThreadUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/threads/ThreadUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/util/threads/ThreadUtils.java
new file mode 100644
index 0000000..c803389
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/threads/ThreadUtils.java
@@ -0,0 +1,185 @@
+/*
+ * 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.threads;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Utility class for thread pools.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class ThreadUtils {
+    private ThreadUtils() {
+        throw new UnsupportedOperationException("No instance");
+    }
+
+    /**
+     * Wraps an {@link CloseableExecutorService} in such a way as to &quot;protect&quot;
+     * it for calls to the {@link CloseableExecutorService#shutdown()} or
+     * {@link CloseableExecutorService#shutdownNow()}. All other calls are delegated as-is
+     * to the original service. <B>Note:</B> the exposed wrapped proxy will
+     * answer correctly the {@link CloseableExecutorService#isShutdown()} query if indeed
+     * one of the {@code shutdown} methods was invoked.
+     *
+     * @param executorService The original service - ignored if {@code null}
+     * @param shutdownOnExit  If {@code true} then it is OK to shutdown the executor
+     *                        so no wrapping takes place.
+     * @return Either the original service or a wrapped one - depending on the
+     * value of the <tt>shutdownOnExit</tt> parameter
+     */
+    public static CloseableExecutorService protectExecutorServiceShutdown(CloseableExecutorService executorService, boolean shutdownOnExit) {
+        if (executorService == null || shutdownOnExit || executorService instanceof NoCloseExecutor) {
+            return executorService;
+        } else {
+            return new NoCloseExecutor(executorService);
+        }
+    }
+
+    public static CloseableExecutorService noClose(CloseableExecutorService executorService) {
+        return protectExecutorServiceShutdown(executorService, false);
+    }
+
+    public static ClassLoader resolveDefaultClassLoader(Object anchor) {
+        return resolveDefaultClassLoader(anchor == null ? null : anchor.getClass());
+    }
+
+    public static Iterable<ClassLoader> resolveDefaultClassLoaders(Object anchor) {
+        return resolveDefaultClassLoaders(anchor == null ? null : anchor.getClass());
+    }
+
+    public static <T> T createDefaultInstance(Class<?> anchor, Class<T> targetType, String className)
+            throws ReflectiveOperationException {
+        return createDefaultInstance(resolveDefaultClassLoaders(anchor), targetType, className);
+    }
+
+    public static <T> T createDefaultInstance(ClassLoader cl, Class<T> targetType, String className)
+            throws ReflectiveOperationException {
+        Class<?> instanceType = cl.loadClass(className);
+        Object instance = instanceType.newInstance();
+        return targetType.cast(instance);
+    }
+
+    public static <T> T createDefaultInstance(Iterable<ClassLoader> cls, Class<T> targetType, String className)
+            throws ReflectiveOperationException {
+        for (ClassLoader cl : cls) {
+            try {
+                return createDefaultInstance(cl, targetType, className);
+            } catch (ClassNotFoundException e) {
+                // Ignore
+            }
+        }
+        throw new ClassNotFoundException(className);
+    }
+
+    /**
+     * <P>Attempts to find the most suitable {@link ClassLoader} as follows:</P>
+     * <UL>
+     * <LI><P>
+     * Check the {@link Thread#getContextClassLoader()} value
+     * </P></LI>
+     *
+     * <LI><P>
+     * If no thread context class loader then check the anchor
+     * class (if given) for its class loader
+     * </P></LI>
+     *
+     * <LI><P>
+     * If still no loader available, then use {@link ClassLoader#getSystemClassLoader()}
+     * </P></LI>
+     * </UL>
+     *
+     * @param anchor The anchor {@link Class} to use if no current thread
+     *               - ignored if {@code null}
+     *               context class loader
+     * @return The resolver {@link ClassLoader}
+     */
+    public static ClassLoader resolveDefaultClassLoader(Class<?> anchor) {
+        Thread thread = Thread.currentThread();
+        ClassLoader cl = thread.getContextClassLoader();
+        if (cl != null) {
+            return cl;
+        }
+
+        if (anchor != null) {
+            cl = anchor.getClassLoader();
+        }
+
+        if (cl == null) {   // can happen for core Java classes
+            cl = ClassLoader.getSystemClassLoader();
+        }
+
+        return cl;
+    }
+
+    public static Iterable<ClassLoader> resolveDefaultClassLoaders(Class<?> anchor) {
+        Set<ClassLoader> cls = new LinkedHashSet<>();
+        Thread thread = Thread.currentThread();
+        ClassLoader cl = thread.getContextClassLoader();
+        if (cl != null) {
+            cls.add(cl);
+        }
+        if (anchor != null) {
+            cls.add(anchor.getClassLoader());
+        }
+        cls.add(ClassLoader.getSystemClassLoader());
+        return cls;
+    }
+
+    public static CloseableExecutorService newFixedThreadPoolIf(CloseableExecutorService executorService, String poolName, int nThreads) {
+        return executorService == null ? newFixedThreadPool(poolName, nThreads) : executorService;
+    }
+
+    public static CloseableExecutorService newFixedThreadPool(String poolName, int nThreads) {
+        return new SshThreadPoolExecutor(
+                nThreads, nThreads,
+                0L, TimeUnit.MILLISECONDS, // TODO make this configurable
+                new LinkedBlockingQueue<>(),
+                new SshdThreadFactory(poolName),
+                new ThreadPoolExecutor.CallerRunsPolicy());
+    }
+
+    public static CloseableExecutorService newCachedThreadPoolIf(CloseableExecutorService executorService, String poolName) {
+        return executorService == null ? newCachedThreadPool(poolName) : executorService;
+    }
+
+    public static CloseableExecutorService newCachedThreadPool(String poolName) {
+        return new SshThreadPoolExecutor(
+                0, Integer.MAX_VALUE, // TODO make this configurable
+                60L, TimeUnit.SECONDS, // TODO make this configurable
+                new SynchronousQueue<>(),
+                new SshdThreadFactory(poolName),
+                new ThreadPoolExecutor.CallerRunsPolicy());
+    }
+
+    public static ScheduledExecutorService newSingleThreadScheduledExecutor(String poolName) {
+        return new ScheduledThreadPoolExecutor(1, new SshdThreadFactory(poolName));
+    }
+
+    public static CloseableExecutorService newSingleThreadExecutor(String poolName) {
+        return newFixedThreadPool(poolName, 1);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/server/keyprovider/AbstractGeneratorHostKeyProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/server/keyprovider/AbstractGeneratorHostKeyProvider.java b/sshd-common/src/main/java/org/apache/sshd/server/keyprovider/AbstractGeneratorHostKeyProvider.java
new file mode 100644
index 0000000..9131f99
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/server/keyprovider/AbstractGeneratorHostKeyProvider.java
@@ -0,0 +1,293 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.server.keyprovider;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.OpenOption;
+import java.nio.file.Path;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.PublicKey;
+import java.security.spec.AlgorithmParameterSpec;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.sshd.common.cipher.ECCurves;
+import org.apache.sshd.common.config.keys.BuiltinIdentities;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.keyprovider.AbstractKeyPairProvider;
+import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * Holds a <U>single</U> {@link KeyPair} which is generated the 1st time
+ * {@link #loadKeys()} is called. If there is a file backing it up and the
+ * file exists, the key is loaded from it. Otherwise a new key pair is
+ * generated and saved (provided a path is configured and {@link #isOverwriteAllowed()}
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractGeneratorHostKeyProvider extends AbstractKeyPairProvider {
+    public static final String DEFAULT_ALGORITHM = KeyUtils.RSA_ALGORITHM;
+    public static final boolean DEFAULT_ALLOWED_TO_OVERWRITE = true;
+
+    private final AtomicReference<KeyPair> keyPairHolder = new AtomicReference<>();
+
+    private Path path;
+    private String algorithm = DEFAULT_ALGORITHM;
+    private int keySize;
+    private AlgorithmParameterSpec keySpec;
+    private boolean overwriteAllowed = DEFAULT_ALLOWED_TO_OVERWRITE;
+
+    protected AbstractGeneratorHostKeyProvider() {
+        super();
+    }
+
+    public Path getPath() {
+        return path;
+    }
+
+    public void setFile(File file) {
+        setPath((file == null) ? null : file.toPath());
+    }
+
+    public void setPath(Path path) {
+        this.path = (path == null) ? null : path.toAbsolutePath();
+    }
+
+    public String getAlgorithm() {
+        return algorithm;
+    }
+
+    public void setAlgorithm(String algorithm) {
+        this.algorithm = algorithm;
+    }
+
+    public int getKeySize() {
+        return keySize;
+    }
+
+    public void setKeySize(int keySize) {
+        this.keySize = keySize;
+    }
+
+    public AlgorithmParameterSpec getKeySpec() {
+        return keySpec;
+    }
+
+    public void setKeySpec(AlgorithmParameterSpec keySpec) {
+        this.keySpec = keySpec;
+    }
+
+    public boolean isOverwriteAllowed() {
+        return overwriteAllowed;
+    }
+
+    public void setOverwriteAllowed(boolean overwriteAllowed) {
+        this.overwriteAllowed = overwriteAllowed;
+    }
+
+    public void clearLoadedKeys() {
+        KeyPair kp;
+        synchronized (keyPairHolder) {
+            kp = keyPairHolder.getAndSet(null);
+        }
+
+        if ((kp != null) & log.isDebugEnabled()) {
+            PublicKey key = kp.getPublic();
+            log.debug("clearLoadedKeys({}) removed key={}-{}",
+                      getPath(), KeyUtils.getKeyType(key), KeyUtils.getFingerPrint(key));
+        }
+    }
+
+    @Override   // co-variant return
+    public synchronized List<KeyPair> loadKeys() {
+        Path keyPath = getPath();
+        KeyPair kp;
+        synchronized (keyPairHolder) {
+            kp = keyPairHolder.get();
+            if (kp == null) {
+                try {
+                    kp = resolveKeyPair(keyPath);
+                    if (kp != null) {
+                        keyPairHolder.set(kp);
+                    }
+                } catch (Throwable t) {
+                    log.warn("loadKeys({}) Failed ({}) to resolve: {}",
+                            keyPath, t.getClass().getSimpleName(), t.getMessage());
+                    if (log.isDebugEnabled()) {
+                        log.debug("loadKeys(" + keyPath + ") resolution failure details", t);
+                    }
+                }
+            }
+        }
+
+        if (kp == null) {
+            return Collections.emptyList();
+        } else {
+            return Collections.singletonList(kp);
+        }
+    }
+
+    protected KeyPair resolveKeyPair(Path keyPath) throws IOException, GeneralSecurityException {
+        String alg = getAlgorithm();
+        KeyPair kp;
+        if (keyPath != null) {
+            try {
+                kp = loadFromFile(alg, keyPath);
+                if (kp != null) {
+                    return kp;
+                }
+            } catch (Throwable e) {
+                log.warn("resolveKeyPair({}) Failed ({}) to load: {}",
+                        keyPath, e.getClass().getSimpleName(), e.getMessage());
+                if (log.isDebugEnabled()) {
+                    log.debug("resolveKeyPair(" + keyPath + ") load failure details", e);
+                }
+            }
+        }
+
+        // either no file specified or no key in file
+        try {
+            kp = generateKeyPair(alg);
+            if (kp == null) {
+                return null;
+            }
+
+            if (log.isDebugEnabled()) {
+                PublicKey key = kp.getPublic();
+                log.debug("resolveKeyPair({}) generated {} key={}-{}",
+                          keyPath, alg, KeyUtils.getKeyType(key), KeyUtils.getFingerPrint(key));
+            }
+        } catch (Throwable e) {
+            log.warn("resolveKeyPair({})[{}] Failed ({}) to generate {} key-pair: {}",
+                     keyPath, alg, e.getClass().getSimpleName(), alg, e.getMessage());
+            if (log.isDebugEnabled()) {
+                log.debug("resolveKeyPair(" + keyPath + ")[" + alg + "] key-pair generation failure details", e);
+            }
+
+            return null;
+        }
+
+        if (keyPath != null) {
+            try {
+                writeKeyPair(kp, keyPath);
+            } catch (Throwable e) {
+                log.warn("resolveKeyPair({})[{}] Failed ({}) to write {} key: {}",
+                         alg, keyPath, e.getClass().getSimpleName(), alg, e.getMessage());
+                if (log.isDebugEnabled()) {
+                    log.debug("resolveKeyPair(" + keyPath + ")[" + alg + "] write failure details", e);
+                }
+            }
+        }
+
+        return kp;
+    }
+
+    protected KeyPair loadFromFile(String alg, Path keyPath) throws IOException, GeneralSecurityException {
+        LinkOption[] options = IoUtils.getLinkOptions(true);
+        if ((!Files.exists(keyPath, options)) || (!Files.isRegularFile(keyPath, options))) {
+            return null;
+        }
+
+        KeyPair kp = readKeyPair(keyPath, IoUtils.EMPTY_OPEN_OPTIONS);
+        if (kp == null) {
+            return null;
+        }
+
+        PublicKey key = kp.getPublic();
+        String keyAlgorithm = key.getAlgorithm();
+        if (BuiltinIdentities.Constants.ECDSA.equalsIgnoreCase(keyAlgorithm)) {
+            keyAlgorithm = KeyUtils.EC_ALGORITHM;
+        } else if (BuiltinIdentities.Constants.ED25519.equalsIgnoreCase(keyAlgorithm)) {
+            keyAlgorithm = SecurityUtils.EDDSA;
+        }
+
+        if (Objects.equals(alg, keyAlgorithm)) {
+            if (log.isDebugEnabled()) {
+                log.debug("resolveKeyPair({}) loaded key={}-{}",
+                          keyPath, KeyUtils.getKeyType(key), KeyUtils.getFingerPrint(key));
+            }
+            return kp;
+        }
+
+        // Not same algorithm - start again
+        if (log.isDebugEnabled()) {
+            log.debug("resolveKeyPair({}) mismatched loaded key algorithm: expected={}, loaded={}",
+                      keyPath, alg, keyAlgorithm);
+        }
+        Files.deleteIfExists(keyPath);
+        return null;
+    }
+
+    protected KeyPair readKeyPair(Path keyPath, OpenOption... options) throws IOException, GeneralSecurityException {
+        try (InputStream inputStream = Files.newInputStream(keyPath, options)) {
+            return doReadKeyPair(keyPath.toString(), inputStream);
+        }
+    }
+
+    protected KeyPair doReadKeyPair(String resourceKey, InputStream inputStream) throws IOException, GeneralSecurityException {
+        return SecurityUtils.loadKeyPairIdentity(resourceKey, inputStream, null);
+    }
+
+    protected void writeKeyPair(KeyPair kp, Path keyPath, OpenOption... options) throws IOException, GeneralSecurityException {
+        if ((!Files.exists(keyPath)) || isOverwriteAllowed()) {
+            try (OutputStream os = Files.newOutputStream(keyPath, options)) {
+                doWriteKeyPair(keyPath.toString(), kp, os);
+            } catch (Throwable e) {
+                log.warn("writeKeyPair({}) failed ({}) to write key {}: {}",
+                         keyPath, e.getClass().getSimpleName(), e.getMessage());
+                if (log.isDebugEnabled()) {
+                    log.debug("writeKeyPair(" + keyPath + ") write failure details", e);
+                }
+            }
+        } else {
+            log.error("Overwriting key ({}) is disabled: using throwaway {}: {}",
+                      keyPath, KeyUtils.getKeyType(kp), KeyUtils.getFingerPrint((kp == null) ? null : kp.getPublic()));
+        }
+    }
+
+    protected abstract void doWriteKeyPair(String resourceKey, KeyPair kp, OutputStream outputStream) throws IOException, GeneralSecurityException;
+
+    protected KeyPair generateKeyPair(String algorithm) throws GeneralSecurityException {
+        KeyPairGenerator generator = SecurityUtils.getKeyPairGenerator(algorithm);
+        if (keySpec != null) {
+            generator.initialize(keySpec);
+            log.info("generateKeyPair(" + algorithm + ") generating host key - spec=" + keySpec.getClass().getSimpleName());
+        } else if (keySize != 0) {
+            generator.initialize(keySize);
+            log.info("generateKeyPair(" + algorithm + ") generating host key - size=" + keySize);
+        } else if (KeyUtils.EC_ALGORITHM.equals(algorithm)) {
+            // If left to our own devices choose the biggest key size possible
+            int numCurves = ECCurves.SORTED_KEY_SIZE.size();
+            ECCurves curve = ECCurves.SORTED_KEY_SIZE.get(numCurves - 1);
+            generator.initialize(curve.getParameters());
+        }
+
+        return generator.generateKeyPair();
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProvider.java b/sshd-common/src/main/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProvider.java
new file mode 100644
index 0000000..3bccde8
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProvider.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.server.keyprovider;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+import java.nio.file.Path;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.security.spec.InvalidKeySpecException;
+
+/**
+ * TODO Add javadoc
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class SimpleGeneratorHostKeyProvider extends AbstractGeneratorHostKeyProvider {
+    public SimpleGeneratorHostKeyProvider() {
+        super();
+    }
+
+    public SimpleGeneratorHostKeyProvider(File file) {
+        this((file == null) ? null : file.toPath());
+    }
+
+    public SimpleGeneratorHostKeyProvider(Path path) {
+        setPath(path);
+    }
+
+    @Override
+    protected KeyPair doReadKeyPair(String resourceKey, InputStream inputStream) throws IOException, GeneralSecurityException {
+        try (ObjectInputStream r = new ObjectInputStream(inputStream)) {
+            try {
+                return (KeyPair) r.readObject();
+            } catch (ClassNotFoundException e) {
+                throw new InvalidKeySpecException("Missing classes: " + e.getMessage(), e);
+            }
+        }
+    }
+
+    @Override
+    protected void doWriteKeyPair(String resourceKey, KeyPair kp, OutputStream outputStream) throws IOException, GeneralSecurityException {
+        try (ObjectOutputStream w = new ObjectOutputStream(outputStream)) {
+            w.writeObject(kp);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/client/auth/password/PasswordIdentityProviderTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/client/auth/password/PasswordIdentityProviderTest.java b/sshd-common/src/test/java/org/apache/sshd/client/auth/password/PasswordIdentityProviderTest.java
new file mode 100644
index 0000000..165805d
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/client/auth/password/PasswordIdentityProviderTest.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.client.auth.password;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class PasswordIdentityProviderTest extends JUnitTestSupport {
+    public PasswordIdentityProviderTest() {
+        super();
+    }
+
+    @Test
+    public void testMultiProvider() {
+        String[][] values = {
+            {getClass().getSimpleName(), getCurrentTestName()},
+            {new Date(System.currentTimeMillis()).toString()},
+            {getClass().getPackage().getName()}
+        };
+        List<String> expected = new ArrayList<>();
+        Collection<PasswordIdentityProvider> providers = new LinkedList<>();
+        for (String[] va : values) {
+            Collection<String> passwords = Arrays.asList(va);
+            expected.addAll(passwords);
+
+            PasswordIdentityProvider p = PasswordIdentityProvider.wrapPasswords(passwords);
+            assertProviderContents("Wrapped", p, passwords);
+            providers.add(p);
+        }
+
+        PasswordIdentityProvider p = PasswordIdentityProvider.multiProvider(providers);
+        assertProviderContents("Multi", p, expected);
+    }
+
+    private static void assertProviderContents(String message, PasswordIdentityProvider p, Iterable<String> expected) {
+        assertNotNull(message + ": no provider", p);
+        assertEquals(message, expected, p.loadPasswords());
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/client/config/hosts/ConfigFileHostEntryResolverTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/client/config/hosts/ConfigFileHostEntryResolverTest.java b/sshd-common/src/test/java/org/apache/sshd/client/config/hosts/ConfigFileHostEntryResolverTest.java
new file mode 100644
index 0000000..741ba7b
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/client/config/hosts/ConfigFileHostEntryResolverTest.java
@@ -0,0 +1,139 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.client.config.hosts;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class ConfigFileHostEntryResolverTest extends JUnitTestSupport {
+    public ConfigFileHostEntryResolverTest() {
+        super();
+    }
+
+    @Test
+    public void testConfigFileReload() throws IOException {
+        Path dir = getTempTargetRelativeFile(getClass().getSimpleName());
+        AtomicInteger reloadCount = new AtomicInteger();
+        ConfigFileHostEntryResolver resolver = new ConfigFileHostEntryResolver(assertHierarchyTargetFolderExists(dir).resolve(getCurrentTestName() + ".config.txt")) {
+            @Override
+            protected List<HostConfigEntry> reloadHostConfigEntries(Path path, String host, int port, String username)
+                    throws IOException {
+                reloadCount.incrementAndGet();
+                return super.reloadHostConfigEntries(path, host, port, username);
+            }
+        };
+        Path path = resolver.getPath();
+
+        HostConfigEntry expected = new HostConfigEntry(getCurrentTestName(), getCurrentTestName(), 7365, getCurrentTestName());
+        testConfigFileReload("Non-existing", path, reloadCount, null, resolver, expected, null);
+        testConfigFileReload("Empty", path, reloadCount, Collections.emptyList(), resolver, expected, null);
+        testConfigFileReload("Global", path, reloadCount,
+                Collections.singletonList(new HostConfigEntry(HostPatternsHolder.ALL_HOSTS_PATTERN, expected.getHost(), expected.getPort(), expected.getUsername())),
+                resolver, expected, expected);
+        testConfigFileReload("Wildcard", path, reloadCount,
+                Arrays.asList(
+                        new HostConfigEntry(
+                                HostPatternsHolder.ALL_HOSTS_PATTERN,
+                                getClass().getSimpleName(),
+                                1234,
+                                getClass().getSimpleName()),
+                        new HostConfigEntry(
+                                expected.getHost() + Character.toString(HostPatternsHolder.WILDCARD_PATTERN),
+                                expected.getHost(),
+                                expected.getPort(),
+                                expected.getUsername())),
+                resolver, expected, expected);
+        testConfigFileReload("Specific", path, reloadCount,
+                Arrays.asList(
+                        new HostConfigEntry(
+                                HostPatternsHolder.ALL_HOSTS_PATTERN,
+                                getClass().getSimpleName(),
+                                1234,
+                                getClass().getSimpleName()),
+                        new HostConfigEntry(
+                                getClass().getSimpleName() + Character.toString(HostPatternsHolder.WILDCARD_PATTERN),
+                                getClass().getSimpleName(),
+                                1234,
+                                getClass().getSimpleName()),
+                        expected),
+                resolver, expected, expected);
+    }
+
+    private static void testConfigFileReload(
+            String phase, Path path, AtomicInteger reloadCount,
+            Collection<? extends HostConfigEntry> entries,
+            HostConfigEntryResolver resolver,
+            HostConfigEntry query,
+            HostConfigEntry expected)
+                    throws IOException {
+        if (entries == null) {
+            if (Files.exists(path)) {
+                Files.delete(path);
+            }
+        } else {
+            HostConfigEntry.writeHostConfigEntries(path, entries, IoUtils.EMPTY_OPEN_OPTIONS);
+        }
+
+        reloadCount.set(0);
+
+        for (int index = 1; index < Byte.SIZE; index++) {
+            HostConfigEntry actual =
+                    resolver.resolveEffectiveHost(query.getHostName(), query.getPort(), query.getUsername());
+
+            if (entries == null) {
+                assertEquals(phase + "[" + index + "]: mismatched reload count", 0, reloadCount.get());
+            } else {
+                assertEquals(phase + "[" + index + "]: mismatched reload count", 1, reloadCount.get());
+            }
+
+            if (expected == null) {
+                assertNull(phase + "[" + index + "]: Unexpected success for " + query, actual);
+            } else {
+                assertNotNull(phase + "[" + index + "]: No result for " + query, actual);
+                assertNotSame(phase + "[" + index + "]: No cloned result for " + query, expected, actual);
+                assertEquals(phase + "[" + index + "]: Mismatched host for " + query,
+                        expected.getHostName(), actual.getHostName());
+                assertEquals(phase + "[" + index + "]: Mismatched port for " + query,
+                        expected.getPort(), actual.getPort());
+                assertEquals(phase + "[" + index + "]: Mismatched user for " + query,
+                        expected.getUsername(), actual.getUsername());
+            }
+        }
+    }
+}