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

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

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:
- *
- * "Hash Visualization: a New Technique to improve Real-World Security",
- * 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();
-    }
-}