http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/0d7af8c8/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/ECDSAPublicKeyEntryDecoder.java
----------------------------------------------------------------------
diff --git 
a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/ECDSAPublicKeyEntryDecoder.java
 
b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/ECDSAPublicKeyEntryDecoder.java
new file mode 100644
index 0000000..d180e9e
--- /dev/null
+++ 
b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/ECDSAPublicKeyEntryDecoder.java
@@ -0,0 +1,382 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * 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.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.config.keys.KeyEntryResolver;
+import org.apache.sshd.common.config.keys.KeyUtils;
+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 = KeyEntryResolver.decodeString(keyData);
+        if (!keyCurveName.equals(encCurveName)) {
+            throw new InvalidKeySpecException("Mismatched key curve name (" + 
keyCurveName + ") vs. encoded one (" + encCurveName + ")");
+        }
+
+        byte[] octets = KeyEntryResolver.readRLEBytes(keyData);
+        ECPoint w;
+        try {
+            w = 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();
+        KeyEntryResolver.encodeString(s, keyType);
+        // see rfc5656 section 3.1
+        KeyEntryResolver.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();
+                KeyEntryResolver.encodeInt(s, 1 /* the indicator */ + 2 * 
numElements);
+                s.write(getIndicatorValue());
+                writeCoordinate(s, "X", p.getAffineX(), numElements);
+                writeCoordinate(s, "Y", p.getAffineY(), numElements);
+            }
+
+        };
+
+        public static final Set<ECPointCompression> VALUES =
+                
Collections.unmodifiableSet(EnumSet.allOf(ECPointCompression.class));
+
+        private final byte indicatorValue;
+
+        ECPointCompression(byte indicator) {
+            indicatorValue = indicator;
+        }
+
+        public final byte getIndicatorValue() {
+            return indicatorValue;
+        }
+
+        public abstract ECPoint octetStringToEcPoint(byte[] octets, int 
startIndex, int len);
+
+        public byte[] ecPointToOctetString(String curveName, ECPoint p) {
+            try (ByteArrayOutputStream baos = new ByteArrayOutputStream((2 * 
66) + Long.SIZE)) {
+                writeECPoint(baos, curveName, p);
+                return baos.toByteArray();
+            } catch (IOException e) {
+                throw new RuntimeException("ecPointToOctetString(" + curveName 
+ ")"
+                        + " failed (" + e.getClass().getSimpleName() + ")"
+                        + " to write data: " + e.getMessage(),
+                        e);
+            }
+        }
+
+        public void writeECPoint(OutputStream s, String curveName, ECPoint p) 
throws IOException {
+            if (s == null) {
+                throw new EOFException("No output stream");
+            }
+
+            throw new StreamCorruptedException("writeECPoint(" + name() + ")[" 
+ p + "] N/A");
+        }
+
+        protected void writeCoordinate(OutputStream s, String n, BigInteger v, 
int numElements) throws IOException {
+            byte[] vp = v.toByteArray();
+            int startIndex = 0;
+            int vLen = vp.length;
+            if (vLen > numElements) {
+                if (vp[0] == 0) {   // skip artificial positive sign
+                    startIndex++;
+                    vLen--;
+                }
+            }
+
+            if (vLen > numElements) {
+                throw new StreamCorruptedException("writeCoordinate(" + name() 
+ ")[" + n + "]"
+                        + " value length (" + vLen + ") exceeds max. (" + 
numElements + ")"
+                        + " for " + v);
+            }
+
+            if (vLen < numElements) {
+                byte[] tmp = new byte[numElements];
+                System.arraycopy(vp, startIndex, tmp, numElements - vLen, 
vLen);
+                vp = tmp;
+            }
+
+            s.write(vp, startIndex, vLen);
+        }
+
+        public static ECPointCompression fromIndicatorValue(int value) {
+            if ((value < 0) || (value > 0xFF)) {
+                return null;    // must be a byte value
+            }
+
+            for (ECPointCompression c : VALUES) {
+                if (value == c.getIndicatorValue()) {
+                    return c;
+                }
+            }
+
+            return null;
+        }
+
+        /**
+         * 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/impl/RSAPublicKeyDecoder.java
----------------------------------------------------------------------
diff --git 
a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/RSAPublicKeyDecoder.java
 
b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/RSAPublicKeyDecoder.java
new file mode 100644
index 0000000..4550815
--- /dev/null
+++ 
b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/RSAPublicKeyDecoder.java
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config.keys.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.KeyPairGenerator;
+import java.security.interfaces.RSAPrivateCrtKey;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.RSAPrivateCrtKeySpec;
+import java.security.spec.RSAPublicKeySpec;
+import java.util.Collections;
+import java.util.Objects;
+
+import org.apache.sshd.common.config.keys.KeyEntryResolver;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * @author <a href="mailto:[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 = KeyEntryResolver.decodeBigInt(keyData);
+        BigInteger n = KeyEntryResolver.decodeBigInt(keyData);
+
+        return generatePublicKey(new RSAPublicKeySpec(n, e));
+    }
+
+    @Override
+    public String encodePublicKey(OutputStream s, RSAPublicKey key) throws 
IOException {
+        Objects.requireNonNull(key, "No public key provided");
+        KeyEntryResolver.encodeString(s, KeyPairProvider.SSH_RSA);
+        KeyEntryResolver.encodeBigInt(s, key.getPublicExponent());
+        KeyEntryResolver.encodeBigInt(s, key.getModulus());
+
+        return KeyPairProvider.SSH_RSA;
+    }
+
+    @Override
+    public RSAPublicKey clonePublicKey(RSAPublicKey key) throws 
GeneralSecurityException {
+        if (key == null) {
+            return null;
+        } else {
+            return generatePublicKey(new RSAPublicKeySpec(key.getModulus(), 
key.getPublicExponent()));
+        }
+    }
+
+    @Override
+    public RSAPrivateKey clonePrivateKey(RSAPrivateKey key) throws 
GeneralSecurityException {
+        if (key == null) {
+            return null;
+        }
+
+        if (!(key instanceof RSAPrivateCrtKey)) {
+            throw new InvalidKeyException("Cannot clone a 
non-RSAPrivateCrtKey: " + key.getClass().getSimpleName());
+        }
+
+        RSAPrivateCrtKey rsaPrv = (RSAPrivateCrtKey) key;
+        return generatePrivateKey(
+                new RSAPrivateCrtKeySpec(
+                        rsaPrv.getModulus(),
+                        rsaPrv.getPublicExponent(),
+                        rsaPrv.getPrivateExponent(),
+                        rsaPrv.getPrimeP(),
+                        rsaPrv.getPrimeQ(),
+                        rsaPrv.getPrimeExponentP(),
+                        rsaPrv.getPrimeExponentQ(),
+                        rsaPrv.getCrtCoefficient()));
+    }
+
+    @Override
+    public KeyPairGenerator getKeyPairGenerator() throws 
GeneralSecurityException {
+        return SecurityUtils.getKeyPairGenerator(KeyUtils.RSA_ALGORITHM);
+    }
+
+    @Override
+    public KeyFactory getKeyFactoryInstance() throws GeneralSecurityException {
+        return SecurityUtils.getKeyFactory(KeyUtils.RSA_ALGORITHM);
+    }
+}

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

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

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

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/0d7af8c8/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHDSSPrivateKeyEntryDecoder.java
----------------------------------------------------------------------
diff --git 
a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHDSSPrivateKeyEntryDecoder.java
 
b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHDSSPrivateKeyEntryDecoder.java
new file mode 100644
index 0000000..02cae65
--- /dev/null
+++ 
b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHDSSPrivateKeyEntryDecoder.java
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config.keys.loader.openssh;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.KeyPairGenerator;
+import java.security.interfaces.DSAParams;
+import java.security.interfaces.DSAPrivateKey;
+import java.security.interfaces.DSAPublicKey;
+import java.security.spec.DSAPrivateKeySpec;
+import java.security.spec.DSAPublicKeySpec;
+import java.security.spec.InvalidKeySpecException;
+import java.util.Collections;
+import java.util.Objects;
+
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.KeyEntryResolver;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.config.keys.impl.AbstractPrivateKeyEntryDecoder;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * @author <a href="mailto:[email protected]";>Apache MINA SSHD Project</a>
+ */
+public class OpenSSHDSSPrivateKeyEntryDecoder extends 
AbstractPrivateKeyEntryDecoder<DSAPublicKey, DSAPrivateKey> {
+    public static final OpenSSHDSSPrivateKeyEntryDecoder INSTANCE = new 
OpenSSHDSSPrivateKeyEntryDecoder();
+
+    public OpenSSHDSSPrivateKeyEntryDecoder() {
+        super(DSAPublicKey.class, DSAPrivateKey.class, 
Collections.unmodifiableList(Collections.singletonList(KeyPairProvider.SSH_DSS)));
+    }
+
+    @Override
+    public DSAPrivateKey decodePrivateKey(String keyType, FilePasswordProvider 
passwordProvider, InputStream keyData)
+            throws IOException, GeneralSecurityException {
+        if (!KeyPairProvider.SSH_DSS.equals(keyType)) { // just in case we 
were invoked directly
+            throw new InvalidKeySpecException("Unexpected key type: " + 
keyType);
+        }
+
+        BigInteger p = KeyEntryResolver.decodeBigInt(keyData);
+        BigInteger q = KeyEntryResolver.decodeBigInt(keyData);
+        BigInteger g = KeyEntryResolver.decodeBigInt(keyData);
+        BigInteger y = KeyEntryResolver.decodeBigInt(keyData);
+        Objects.requireNonNull(y, "No public key data");   // TODO run some 
validation on it
+        BigInteger x = KeyEntryResolver.decodeBigInt(keyData);
+
+        return generatePrivateKey(new DSAPrivateKeySpec(x, p, q, g));
+    }
+
+    @Override
+    public String encodePrivateKey(OutputStream s, DSAPrivateKey key) throws 
IOException {
+        Objects.requireNonNull(key, "No private key provided");
+
+        DSAParams keyParams = Objects.requireNonNull(key.getParams(), "No DSA 
params available");
+        BigInteger p = keyParams.getP();
+        KeyEntryResolver.encodeBigInt(s, p);
+        KeyEntryResolver.encodeBigInt(s, keyParams.getQ());
+
+        BigInteger g = keyParams.getG();
+        KeyEntryResolver.encodeBigInt(s, g);
+
+        BigInteger x = key.getX();
+        BigInteger y = g.modPow(x, p);
+        KeyEntryResolver.encodeBigInt(s, y);
+        KeyEntryResolver.encodeBigInt(s, x);
+        return KeyPairProvider.SSH_DSS;
+    }
+
+    @Override
+    public boolean isPublicKeyRecoverySupported() {
+        return true;
+    }
+
+    @Override
+    public DSAPublicKey recoverPublicKey(DSAPrivateKey privateKey) throws 
GeneralSecurityException {
+        // 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
+        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);
+        return generatePublicKey(new DSAPublicKeySpec(y, p, q, g));
+    }
+
+    @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/loader/openssh/OpenSSHECDSAPrivateKeyEntryDecoder.java
----------------------------------------------------------------------
diff --git 
a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHECDSAPrivateKeyEntryDecoder.java
 
b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHECDSAPrivateKeyEntryDecoder.java
new file mode 100644
index 0000000..bdb6d28
--- /dev/null
+++ 
b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHECDSAPrivateKeyEntryDecoder.java
@@ -0,0 +1,164 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config.keys.loader.openssh;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchProviderException;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPrivateKeySpec;
+import java.security.spec.ECPublicKeySpec;
+import java.security.spec.InvalidKeySpecException;
+import java.util.Objects;
+
+import org.apache.sshd.common.cipher.ECCurves;
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.KeyEntryResolver;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.config.keys.impl.AbstractPrivateKeyEntryDecoder;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * @author <a href="mailto:[email protected]";>Apache MINA SSHD Project</a>
+ */
+public class OpenSSHECDSAPrivateKeyEntryDecoder extends 
AbstractPrivateKeyEntryDecoder<ECPublicKey, ECPrivateKey> {
+    public static final OpenSSHECDSAPrivateKeyEntryDecoder INSTANCE = new 
OpenSSHECDSAPrivateKeyEntryDecoder();
+
+    public OpenSSHECDSAPrivateKeyEntryDecoder() {
+        super(ECPublicKey.class, ECPrivateKey.class, ECCurves.KEY_TYPES);
+    }
+
+    @Override
+    public ECPrivateKey decodePrivateKey(String keyType, FilePasswordProvider 
passwordProvider, InputStream keyData)
+            throws IOException, GeneralSecurityException {
+        ECCurves curve = ECCurves.fromKeyType(keyType);
+        if (curve == null) {
+            throw new InvalidKeySpecException("Not an EC curve name: " + 
keyType);
+        }
+
+        if (!SecurityUtils.hasEcc()) {
+            throw new NoSuchProviderException("ECC not supported");
+        }
+
+        String keyCurveName = curve.getName();
+        // see rfc5656 section 3.1
+        String encCurveName = KeyEntryResolver.decodeString(keyData);
+        if (!keyCurveName.equals(encCurveName)) {
+            throw new InvalidKeySpecException("Mismatched key curve name (" + 
keyCurveName + ") vs. encoded one (" + encCurveName + ")");
+        }
+
+        byte[] pubKey = KeyEntryResolver.readRLEBytes(keyData);
+        Objects.requireNonNull(pubKey, "No public point");  // TODO validate 
it is a valid ECPoint
+        BigInteger s = KeyEntryResolver.decodeBigInt(keyData);
+        ECParameterSpec params = curve.getParameters();
+        return generatePrivateKey(new ECPrivateKeySpec(s, params));
+    }
+
+    @Override
+    public String encodePrivateKey(OutputStream s, ECPrivateKey key) throws 
IOException {
+        Objects.requireNonNull(key, "No private key provided");
+        return null;
+    }
+
+    @Override
+    public ECPublicKey recoverPublicKey(ECPrivateKey prvKey) throws 
GeneralSecurityException {
+        ECCurves curve = ECCurves.fromECKey(prvKey);
+        if (curve == null) {
+            throw new InvalidKeyException("Unknown curve");
+        }
+        // TODO see how we can figure out the public value
+        return super.recoverPublicKey(prvKey);
+    }
+
+    @Override
+    public ECPublicKey clonePublicKey(ECPublicKey key) throws 
GeneralSecurityException {
+        if (!SecurityUtils.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 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");
+        }
+    }
+}

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

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

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

Reply via email to