[SSHD-703] Add support for reading an ED25519 private key from a file

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

Branch: refs/heads/master
Commit: 0d7af8c8e052e1f7f980f18ec9795cff8f8af833
Parents: c951aa2
Author: Lyor Goldstein <[email protected]>
Authored: Fri Oct 21 11:12:16 2016 +0300
Committer: Lyor Goldstein <[email protected]>
Committed: Fri Oct 21 11:12:16 2016 +0300

----------------------------------------------------------------------
 README.md                                       |  11 +-
 .../java/org/apache/sshd/client/SshClient.java  |  14 +-
 .../org/apache/sshd/common/cipher/ECCurves.java |  10 +
 .../keys/AbstractPublicKeyEntryDecoder.java     | 125 ------
 .../common/config/keys/BuiltinIdentities.java   |   7 +
 .../config/keys/DSSPublicKeyEntryDecoder.java   | 117 ------
 .../config/keys/ECDSAPublicKeyEntryDecoder.java | 379 ------------------
 .../config/keys/FilePasswordProvider.java       |  12 +-
 .../common/config/keys/KeyEntryResolver.java    | 210 ++++++++++
 .../sshd/common/config/keys/KeyUtils.java       |   3 +
 .../config/keys/PrivateKeyEntryDecoder.java     | 142 +++++++
 .../config/keys/PrivateKeyEntryResolver.java    |  70 ++++
 .../config/keys/PublicKeyEntryDecoder.java      | 143 +------
 .../common/config/keys/RSAPublicKeyDecoder.java | 115 ------
 .../keys/impl/AbstractKeyEntryResolver.java     |  83 ++++
 .../impl/AbstractPrivateKeyEntryDecoder.java    |  40 ++
 .../impl/AbstractPublicKeyEntryDecoder.java     |  41 ++
 .../keys/impl/DSSPublicKeyEntryDecoder.java     | 119 ++++++
 .../keys/impl/ECDSAPublicKeyEntryDecoder.java   | 382 +++++++++++++++++++
 .../config/keys/impl/RSAPublicKeyDecoder.java   | 117 ++++++
 .../loader/AbstractKeyPairResourceParser.java   | 186 +++++++++
 .../keys/loader/KeyPairResourceLoader.java      | 129 +++++++
 .../keys/loader/KeyPairResourceParser.java      | 168 ++++++++
 .../OpenSSHDSSPrivateKeyEntryDecoder.java       | 146 +++++++
 .../OpenSSHECDSAPrivateKeyEntryDecoder.java     | 164 ++++++++
 .../openssh/OpenSSHKeyPairResourceParser.java   | 355 +++++++++++++++++
 .../loader/openssh/OpenSSHParserContext.java    |  83 ++++
 .../openssh/OpenSSHRSAPrivateKeyDecoder.java    | 152 ++++++++
 .../java/org/apache/sshd/common/kex/ECDH.java   |   2 +-
 ...actClassLoadableResourceKeyPairProvider.java |  90 -----
 .../AbstractFileKeyPairProvider.java            |  77 ----
 .../AbstractResourceKeyPairProvider.java        |   6 +-
 .../ClassLoadableResourceKeyPairProvider.java   | 113 ++++++
 .../common/keyprovider/FileKeyPairProvider.java |  92 +++++
 .../apache/sshd/common/util/buffer/Buffer.java  |   2 +-
 .../sshd/common/util/buffer/BufferUtils.java    |  18 +
 .../buffer/keys/ECBufferPublicKeyParser.java    |   2 +-
 .../org/apache/sshd/common/util/io/IoUtils.java |  71 ++++
 .../common/util/security/SecurityUtils.java     |  79 ++--
 ...tleClassLoadableResourceKeyPairProvider.java |  42 --
 .../BouncyCastleFileKeyPairProvider.java        |  42 --
 .../BouncyCastleGeneratorHostKeyProvider.java   |   6 -
 .../BouncyCastleInputStreamLoader.java          |  67 ----
 .../BouncyCastleKeyPairResourceParser.java      | 119 ++++++
 .../security/eddsa/Ed25519PublicKeyDecoder.java |  20 +-
 .../security/eddsa/EdDSASecurityProvider.java   |   6 +
 .../OpenSSHEd25519PrivateKeyEntryDecoder.java   | 137 +++++++
 .../AbstractGeneratorHostKeyProvider.java       |   4 +-
 .../OpenSSHKeyPairResourceParserTest.java       | 102 +++++
 .../sshd/common/random/RandomFactoryTest.java   |   2 -
 .../signature/SignatureFactoriesTest.java       |  51 ++-
 .../sshd/common/util/SecurityUtilsTest.java     |  13 +-
 .../java/org/apache/sshd/util/test/Utils.java   |  12 +-
 ...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 +
 61 files changed, 3495 insertions(+), 1296 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/0d7af8c8/README.md
----------------------------------------------------------------------
diff --git a/README.md b/README.md
index 96c6144..16b0bde 100644
--- a/README.md
+++ b/README.md
@@ -34,8 +34,7 @@ Required only for reading/writing keys from/to PEM files or 
for special keys/cip
 
 ```
 
-**Caveat**: If _Bouncy Castle_ modules are available, then the code will use 
its implementation of the ciphers, keys, signatures, etc. rather than
-the default JCE provided in the JVM.
+**Caveat**: If _Bouncy Castle_ modules are available, then the code will use 
its implementation of the ciphers, keys, signatures, etc. rather than the 
default JCE provided in the JVM.
 
 
 * [MINA core](https://mina.apache.org/mina-project/)
@@ -46,7 +45,7 @@ Optional dependency to enable choosing between NIO 
asynchronous sockets (the def
 
 ```xml
 
-    <dependency>
+    <dependency>    <!-- For async. sockets I/O -->
         <groupId>org.apache.mina</groupId>
         <artifactId>mina-core</artifactId>
     </dependency>
@@ -69,6 +68,7 @@ Required for supporting 
[ssh-ed25519](https://tools.ietf.org/html/draft-bjh21-ss
 
 ```
 
+The code contains support for reading _ed25519_ [OpenSSH formatted private 
keys](https://issues.apache.org/jira/browse/SSHD-703).
 
 # Set up an SSH client in 5 minutes
 
@@ -107,7 +107,10 @@ Of course, one can implement the verifier in whatever 
other manner is suitable f
 
 ### ClientIdentityLoader/KeyPairProvider
 
-One can set up the public/private keys to be used in case a password-less 
authentication is needed. By default, the client is configured to automatically 
detect and use the identity files residing in the user's *~/.ssh* folder (e.g., 
*id_rsa*, *id_ecdsa*) and present them as part of the authentication process. 
**Note:** if the identity files are encrypted via a password, one must 
configure a `FilePasswordProvider` so that the code can decrypt them before 
using and presenting them to the server as part of the authentication process. 
Reading key files in PEM format (including encrypted ones) requires that the 
[Bouncy Castle](https://www.bouncycastle.org/) supporting artifacts be 
available in the code's classpath.
+One can set up the public/private keys to be used in case a password-less 
authentication is needed. By default, the client is configured to automatically 
detect and use the identity files residing in the user's *~/.ssh* folder (e.g., 
*id_rsa*, *id_ecdsa*) and present them as part of the authentication process. 
**Note:** if the identity files are encrypted via a password, one must 
configure a `FilePasswordProvider` so that the code can decrypt them before 
using and presenting them to the server as part of the authentication process. 
Reading key files in PEM format (including encrypted ones) requires that the 
[Bouncy Castle](https://www.bouncycastle.org/) supporting artifacts be 
available in the code's classpath. One can read files in
+[OpenSSH](http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.key?rev=1.1&content-type=text/x-cvsweb-markup)
+format without any specific extra artifacts (although for reading _ed25519_ 
keys one needs to add the _EdDSA_ support
+artifacts). **Note:** for the time being, password encrypted _ed25519_ private 
key files are not supported.
 
 ### UserInteraction
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/0d7af8c8/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 49e3712..73e613f 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
@@ -106,7 +106,7 @@ import 
org.apache.sshd.common.helpers.AbstractFactoryManager;
 import org.apache.sshd.common.io.IoConnectFuture;
 import org.apache.sshd.common.io.IoConnector;
 import org.apache.sshd.common.io.IoSession;
-import org.apache.sshd.common.keyprovider.AbstractFileKeyPairProvider;
+import org.apache.sshd.common.keyprovider.FileKeyPairProvider;
 import org.apache.sshd.common.keyprovider.KeyPairProvider;
 import org.apache.sshd.common.mac.BuiltinMacs;
 import org.apache.sshd.common.mac.Mac;
@@ -119,7 +119,6 @@ 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.net.SshdSocketAddress;
-import org.apache.sshd.common.util.security.SecurityUtils;
 
 /**
  * <P>
@@ -988,8 +987,8 @@ public class SshClient extends AbstractFactoryManager 
implements ClientFactoryMa
         }
     }
 
-    public static AbstractFileKeyPairProvider 
setupSessionIdentities(ClientFactoryManager client, Collection<File> identities,
-            final BufferedReader stdin, final PrintStream stdout, final 
PrintStream stderr)
+    public static FileKeyPairProvider 
setupSessionIdentities(ClientFactoryManager client, Collection<File> identities,
+            BufferedReader stdin, PrintStream stdout, PrintStream stderr)
                 throws Throwable {
         client.setFilePasswordProvider(file -> {
             stdout.print("Enter password for private key file=" + file + ": ");
@@ -1000,7 +999,12 @@ public class SshClient extends AbstractFactoryManager 
implements ClientFactoryMa
             return null;
         }
 
-        AbstractFileKeyPairProvider provider = 
SecurityUtils.createFileKeyPairProvider();
+        FileKeyPairProvider provider = new FileKeyPairProvider() {
+            @Override
+            public String toString() {
+                return FileKeyPairProvider.class.getSimpleName() + 
"[clientIdentitiesProvider]";
+            }
+        };
         provider.setFiles(identities);
         client.setKeyPairProvider(provider);
         return provider;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/0d7af8c8/sshd-core/src/main/java/org/apache/sshd/common/cipher/ECCurves.java
----------------------------------------------------------------------
diff --git 
a/sshd-core/src/main/java/org/apache/sshd/common/cipher/ECCurves.java 
b/sshd-core/src/main/java/org/apache/sshd/common/cipher/ECCurves.java
index f452fee..0e99ec0 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/cipher/ECCurves.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/cipher/ECCurves.java
@@ -19,6 +19,7 @@
 package org.apache.sshd.common.cipher;
 
 import java.math.BigInteger;
+import java.security.interfaces.ECKey;
 import java.security.spec.ECField;
 import java.security.spec.ECFieldFp;
 import java.security.spec.ECParameterSpec;
@@ -216,6 +217,15 @@ public enum ECCurves implements NamedResource, 
OptionalFeature {
     }
 
     /**
+     * @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)

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/0d7af8c8/sshd-core/src/main/java/org/apache/sshd/common/config/keys/AbstractPublicKeyEntryDecoder.java
----------------------------------------------------------------------
diff --git 
a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/AbstractPublicKeyEntryDecoder.java
 
b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/AbstractPublicKeyEntryDecoder.java
deleted file mode 100644
index 23da5bb..0000000
--- 
a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/AbstractPublicKeyEntryDecoder.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.common.config.keys;
-
-import java.security.GeneralSecurityException;
-import java.security.InvalidKeyException;
-import java.security.KeyFactory;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.spec.KeySpec;
-import java.util.Collection;
-import java.util.Objects;
-
-import org.apache.sshd.common.util.ValidateUtils;
-
-/**
- * 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:[email protected]";>Apache MINA SSHD Project</a>
- */
-public abstract class AbstractPublicKeyEntryDecoder<PUB extends PublicKey, PRV 
extends PrivateKey>
-        implements PublicKeyEntryDecoder<PUB, PRV> {
-    private final Class<PUB> pubType;
-    private final Class<PRV> prvType;
-    private final Collection<String> names;
-
-    protected AbstractPublicKeyEntryDecoder(Class<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 KeyPair cloneKeyPair(KeyPair kp) throws GeneralSecurityException {
-        if (kp == null) {
-            return null;
-        }
-
-        PUB pubCloned = null;
-        PublicKey pubOriginal = kp.getPublic();
-        Class<PUB> pubExpected = getPublicKeyType();
-        if (pubOriginal != null) {
-            Class<?> orgType = pubOriginal.getClass();
-            if (!pubExpected.isAssignableFrom(orgType)) {
-                throw new InvalidKeyException("Mismatched public key types: 
expected=" + pubExpected.getSimpleName() + ", actual=" + 
orgType.getSimpleName());
-            }
-
-            pubCloned = clonePublicKey(pubExpected.cast(pubOriginal));
-        }
-
-        PRV prvCloned = null;
-        PrivateKey prvOriginal = kp.getPrivate();
-        Class<PRV> prvExpected = getPrivateKeyType();
-        if (prvOriginal != null) {
-            Class<?> orgType = prvOriginal.getClass();
-            if (!prvExpected.isAssignableFrom(orgType)) {
-                throw new InvalidKeyException("Mismatched private key types: 
expected=" + prvExpected.getSimpleName() + ", actual=" + 
orgType.getSimpleName());
-            }
-
-            prvCloned = clonePrivateKey(prvExpected.cast(prvOriginal));
-        }
-
-        return new KeyPair(pubCloned, prvCloned);
-    }
-
-    @Override
-    public Collection<String> getSupportedTypeNames() {
-        return 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 KeyPair generateKeyPair(int keySize) throws 
GeneralSecurityException {
-        KeyPairGenerator gen = getKeyPairGenerator();
-        gen.initialize(keySize);
-        return gen.generateKeyPair();
-    }
-
-    @Override
-    public String toString() {
-        return getPublicKeyType().getSimpleName() + ": " + 
getSupportedTypeNames();
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/0d7af8c8/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
index dd60cf1..8dddfe7 100644
--- 
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
@@ -181,6 +181,13 @@ public enum BuiltinIdentities implements Identity {
         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;
             }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/0d7af8c8/sshd-core/src/main/java/org/apache/sshd/common/config/keys/DSSPublicKeyEntryDecoder.java
----------------------------------------------------------------------
diff --git 
a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/DSSPublicKeyEntryDecoder.java
 
b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/DSSPublicKeyEntryDecoder.java
deleted file mode 100644
index 8384cac..0000000
--- 
a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/DSSPublicKeyEntryDecoder.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.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.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * @author <a href="mailto:[email protected]";>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 = PublicKeyEntryDecoder.decodeBigInt(keyData);
-        BigInteger q = PublicKeyEntryDecoder.decodeBigInt(keyData);
-        BigInteger g = PublicKeyEntryDecoder.decodeBigInt(keyData);
-        BigInteger y = PublicKeyEntryDecoder.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");
-        PublicKeyEntryDecoder.encodeString(s, KeyPairProvider.SSH_DSS);
-        PublicKeyEntryDecoder.encodeBigInt(s, keyParams.getP());
-        PublicKeyEntryDecoder.encodeBigInt(s, keyParams.getQ());
-        PublicKeyEntryDecoder.encodeBigInt(s, keyParams.getG());
-        PublicKeyEntryDecoder.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/0d7af8c8/sshd-core/src/main/java/org/apache/sshd/common/config/keys/ECDSAPublicKeyEntryDecoder.java
----------------------------------------------------------------------
diff --git 
a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/ECDSAPublicKeyEntryDecoder.java
 
b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/ECDSAPublicKeyEntryDecoder.java
deleted file mode 100644
index 9ffa705..0000000
--- 
a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/ECDSAPublicKeyEntryDecoder.java
+++ /dev/null
@@ -1,379 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * 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.EOFException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.StreamCorruptedException;
-import java.math.BigInteger;
-import java.security.GeneralSecurityException;
-import java.security.InvalidKeyException;
-import java.security.KeyFactory;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.security.NoSuchProviderException;
-import java.security.interfaces.ECPrivateKey;
-import java.security.interfaces.ECPublicKey;
-import java.security.spec.ECParameterSpec;
-import java.security.spec.ECPoint;
-import java.security.spec.ECPrivateKeySpec;
-import java.security.spec.ECPublicKeySpec;
-import java.security.spec.InvalidKeySpecException;
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.Objects;
-import java.util.Set;
-
-import org.apache.sshd.common.cipher.ECCurves;
-import org.apache.sshd.common.util.NumberUtils;
-import org.apache.sshd.common.util.buffer.BufferUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * @author <a href="mailto:[email protected]";>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.hasEcc()) {
-            throw new NoSuchProviderException("ECC not supported");
-        }
-
-        String keyCurveName = curve.getName();
-        ECParameterSpec paramSpec = curve.getParameters();
-        // see rfc5656 section 3.1
-        String encCurveName = PublicKeyEntryDecoder.decodeString(keyData);
-        if (!keyCurveName.equals(encCurveName)) {
-            throw new InvalidKeySpecException("Mismatched key curve name (" + 
keyCurveName + ") vs. encoded one (" + encCurveName + ")");
-        }
-
-        byte[] octets = PublicKeyEntryDecoder.readRLEBytes(keyData);
-        final ECPoint w;
-        try {
-            w = 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());
-        }
-
-        return generatePublicKey(new ECPublicKeySpec(w, paramSpec));
-    }
-
-    @Override
-    public ECPublicKey clonePublicKey(ECPublicKey key) throws 
GeneralSecurityException {
-        if (!SecurityUtils.hasEcc()) {
-            throw new NoSuchProviderException("ECC not supported");
-        }
-
-        if (key == null) {
-            return null;
-        }
-
-        ECParameterSpec params = key.getParams();
-        if (params == null) {
-            throw new InvalidKeyException("Missing parameters in key");
-        }
-
-        return generatePublicKey(new ECPublicKeySpec(key.getW(), params));
-    }
-
-    @Override
-    public ECPrivateKey clonePrivateKey(ECPrivateKey key) throws 
GeneralSecurityException {
-        if (!SecurityUtils.hasEcc()) {
-            throw new NoSuchProviderException("ECC not supported");
-        }
-
-        if (key == null) {
-            return null;
-        }
-
-        ECParameterSpec params = key.getParams();
-        if (params == null) {
-            throw new InvalidKeyException("Missing parameters in key");
-        }
-
-        return generatePrivateKey(new ECPrivateKeySpec(key.getS(), params));
-    }
-
-    @Override
-    public String encodePublicKey(OutputStream s, ECPublicKey key) throws 
IOException {
-        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();
-        PublicKeyEntryDecoder.encodeString(s, keyType);
-        // see rfc5656 section 3.1
-        PublicKeyEntryDecoder.encodeString(s, curveName);
-        ECPointCompression.UNCOMPRESSED.writeECPoint(s, curveName, key.getW());
-        return keyType;
-    }
-
-    @Override
-    public KeyFactory getKeyFactoryInstance() throws GeneralSecurityException {
-        if (SecurityUtils.hasEcc()) {
-            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.hasEcc()) {
-            return SecurityUtils.getKeyPairGenerator(KeyUtils.EC_ALGORITHM);
-        } else {
-            throw new NoSuchProviderException("ECC not supported");
-        }
-    }
-
-    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];
-        ECPointCompression compression = 
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
-    }
-
-    /**
-     * The various {@link ECPoint} representation compression indicators
-     *
-     * @author <a href="mailto:[email protected]";>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 = ECCurves.fromCurveName(curveName);
-                if (curve == null) {
-                    throw new StreamCorruptedException("writeECPoint(" + 
name() + ")[" + curveName + "] cannot determine octets count");
-                }
-
-                int numElements = curve.getNumPointOctets();
-                PublicKeyEntryDecoder.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;
-        }
-
-        /**
-         * 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);
-            }
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/0d7af8c8/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
index 647b03f..35f7558 100644
--- 
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
@@ -29,17 +29,7 @@ public interface FilePasswordProvider {
     /**
      * An &quot;empty&quot; provider that returns {@code null} - i.e., 
unprotected key file
      */
-    FilePasswordProvider EMPTY = new FilePasswordProvider() {
-        @Override
-        public String getPassword(String resourceKey) throws IOException {
-            return null;
-        }
-
-        @Override
-        public String toString() {
-            return "EMPTY";
-        }
-    };
+    FilePasswordProvider EMPTY = resourceKey -> null;
 
     /**
      * @param resourceKey The resource key representing the <U>private</U>

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/0d7af8c8/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
new file mode 100644
index 0000000..8cf8fa6
--- /dev/null
+++ 
b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/KeyEntryResolver.java
@@ -0,0 +1,210 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * 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 java.util.Collection;
+
+import org.apache.sshd.common.util.io.IoUtils;
+
+/**
+ * @param <PUB> Type of {@link PublicKey}
+ * @param <PRV> Type of {@link PrivateKey}
+ * @author <a href="mailto:[email protected]";>Apache MINA SSHD Project</a>
+ */
+public interface KeyEntryResolver<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();
+
+    /**
+     * @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/0d7af8c8/sshd-core/src/main/java/org/apache/sshd/common/config/keys/KeyUtils.java
----------------------------------------------------------------------
diff --git 
a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/KeyUtils.java 
b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/KeyUtils.java
index 56f6318..a4787cb 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/KeyUtils.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/KeyUtils.java
@@ -57,6 +57,9 @@ 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;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/0d7af8c8/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
new file mode 100644
index 0000000..6dbeee9
--- /dev/null
+++ 
b/sshd-core/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:[email protected]";>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/0d7af8c8/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
new file mode 100644
index 0000000..1e4c91e
--- /dev/null
+++ 
b/sshd-core/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:[email protected]";>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/0d7af8c8/sshd-core/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntryDecoder.java
----------------------------------------------------------------------
diff --git 
a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntryDecoder.java
 
b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntryDecoder.java
index 8d8f132..7462e4a 100644
--- 
a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntryDecoder.java
+++ 
b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntryDecoder.java
@@ -24,13 +24,7 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.StreamCorruptedException;
-import java.math.BigInteger;
-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.KeyPairGenerator;
 import java.security.PrivateKey;
 import java.security.PublicKey;
 import java.security.spec.InvalidKeySpecException;
@@ -39,7 +33,6 @@ import java.util.Collection;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.NumberUtils;
 import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.io.IoUtils;
 
 /**
  * Represents a decoder of an {@code OpenSSH} encoded key data
@@ -48,62 +41,8 @@ import org.apache.sshd.common.util.io.IoUtils;
  * @param <PRV> Type of {@link PrivateKey}
  * @author <a href="mailto:[email protected]";>Apache MINA SSHD Project</a>
  */
-public interface PublicKeyEntryDecoder<PUB extends PublicKey, PRV extends 
PrivateKey> extends PublicKeyEntryResolver {
-    /**
-     * @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();
-
-    /**
-     * @param keySize Key size in bits
-     * @return A {@link KeyPair} with the specified key size
-     * @throws GeneralSecurityException if unable to generate the pair
-     */
-    KeyPair generateKeyPair(int keySize) throws GeneralSecurityException;
-
-    /**
-     * @param kp The {@link KeyPair} to be cloned - ignored if {@code null}
-     * @return A cloned pair (or {@code null} if no original pair)
-     * @throws GeneralSecurityException If failed to clone - e.g., provided key
-     *                                  pair does not contain keys of the 
expected type
-     * @see #getPublicKeyType()
-     * @see #getPrivateKeyType()
-     */
-    KeyPair cloneKeyPair(KeyPair kp) throws GeneralSecurityException;
-
-    /**
-     * @param key The {@link PublicKey} to clone - ignored if {@code null}
-     * @return The cloned key (or {@code null} if no original key)
-     * @throws GeneralSecurityException If failed to clone the key
-     */
-    PUB clonePublicKey(PUB key) throws GeneralSecurityException;
-
-    /**
-     * @param key The {@link PrivateKey} to clone - ignored if {@code null}
-     * @return The cloned key (or {@code null} if no original key)
-     * @throws GeneralSecurityException If failed to clone the key
-     */
-    PRV clonePrivateKey(PRV key) throws GeneralSecurityException;
-
-    /**
-     * @return A {@link KeyPairGenerator} suitable for this decoder
-     * @throws GeneralSecurityException If failed to create the generator
-     */
-    KeyPairGenerator getKeyPairGenerator() throws GeneralSecurityException;
+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 {
@@ -139,7 +78,7 @@ public interface PublicKeyEntryDecoder<PUB extends 
PublicKey, PRV extends Privat
 
     default PUB decodePublicKey(InputStream keyData) throws IOException, 
GeneralSecurityException {
         // the actual data is preceded by a string that repeats the key type
-        String type = decodeString(keyData);
+        String type = KeyEntryResolver.decodeString(keyData);
         if (GenericUtils.isEmpty(type)) {
             throw new StreamCorruptedException("Missing key type string");
         }
@@ -172,80 +111,4 @@ public interface PublicKeyEntryDecoder<PUB extends 
PublicKey, PRV extends Privat
      * @throws IOException If failed to generate the encoding
      */
     String encodePublicKey(OutputStream s, PUB key) throws IOException;
-
-    /**
-     * @return A {@link KeyFactory} suitable for the specific decoder type
-     * @throws GeneralSecurityException If failed to create one
-     */
-    KeyFactory getKeyFactoryInstance() throws GeneralSecurityException;
-
-    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/0d7af8c8/sshd-core/src/main/java/org/apache/sshd/common/config/keys/RSAPublicKeyDecoder.java
----------------------------------------------------------------------
diff --git 
a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/RSAPublicKeyDecoder.java
 
b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/RSAPublicKeyDecoder.java
deleted file mode 100644
index a76b4b3..0000000
--- 
a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/RSAPublicKeyDecoder.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.config.keys;
-
-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.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * @author <a href="mailto:[email protected]";>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 = PublicKeyEntryDecoder.decodeBigInt(keyData);
-        BigInteger n = PublicKeyEntryDecoder.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");
-        PublicKeyEntryDecoder.encodeString(s, KeyPairProvider.SSH_RSA);
-        PublicKeyEntryDecoder.encodeBigInt(s, key.getPublicExponent());
-        PublicKeyEntryDecoder.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/0d7af8c8/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
new file mode 100644
index 0000000..e8a78f3
--- /dev/null
+++ 
b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractKeyEntryResolver.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.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 java.util.Objects;
+
+import org.apache.sshd.common.config.keys.KeyEntryResolver;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
+
+/**
+ * @param <PUB> Type of {@link PublicKey}
+ * @param <PRV> Type of {@link PrivateKey}
+ * @author <a href="mailto:[email protected]";>Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractKeyEntryResolver<PUB extends PublicKey, PRV 
extends PrivateKey>
+            extends AbstractLoggingBean
+            implements KeyEntryResolver<PUB, PRV>  {
+    private final Class<PUB> pubType;
+    private final Class<PRV> prvType;
+    private final Collection<String> names;
+
+    protected AbstractKeyEntryResolver(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;
+    }
+
+    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/0d7af8c8/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
new file mode 100644
index 0000000..574412e
--- /dev/null
+++ 
b/sshd-core/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:[email protected]";>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/0d7af8c8/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
new file mode 100644
index 0000000..59cbf3a
--- /dev/null
+++ 
b/sshd-core/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:[email protected]";>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/0d7af8c8/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
new file mode 100644
index 0000000..464d2b0
--- /dev/null
+++ 
b/sshd-core/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:[email protected]";>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);
+    }
+}

Reply via email to