You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by lg...@apache.org on 2020/05/22 16:46:35 UTC

[mina-sshd] branch master updated (34d20f8 -> d14a99b)

This is an automated email from the ASF dual-hosted git repository.

lgoldstein pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/mina-sshd.git.


    from 34d20f8  Do not re-format code comments - only Javadoc ones
     new bfb1ee8  Do not download sources or javadoc of our own modules
     new d14a99b  [SSHD-989] Fixed parsing of PKCS8 encoded PEM private key files

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 CHANGES.md                                         |   2 +-
 pom.xml                                            |   4 +
 .../loader/pem/ECDSAPEMResourceKeyPairParser.java  | 113 ++++++++++++++-------
 .../loader/pem/PKCS8PEMResourceKeyPairParser.java  |  15 ++-
 .../keys/loader/pem/PKCS8PrivateKeyInfo.java       |  39 ++++++-
 .../apache/sshd/common/util/io/der/DERParser.java  |   6 ++
 .../sshd/common/config/keys/KeyUtilsCloneTest.java |   1 -
 .../pem/PKCS8PEMResourceKeyPairParserTest.java     |  40 ++++----
 .../common/config/keys/loader/pem/pkcs8-ec-256.pem |   5 +
 .../common/config/keys/loader/pem/pkcs8-ec-384.pem |   6 ++
 .../common/config/keys/loader/pem/pkcs8-ec-521.pem |   8 ++
 .../config/keys/loader/pem/pkcs8-rsa-1024.pem      |  16 +++
 .../config/keys/loader/pem/pkcs8-rsa-2048.pem      |  28 +++++
 .../config/keys/loader/pem/pkcs8-rsa-3072.pem      |  40 ++++++++
 .../config/keys/loader/pem/pkcs8-rsa-4096.pem      |  52 ++++++++++
 15 files changed, 311 insertions(+), 64 deletions(-)
 create mode 100644 sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/pem/pkcs8-ec-256.pem
 create mode 100644 sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/pem/pkcs8-ec-384.pem
 create mode 100644 sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/pem/pkcs8-ec-521.pem
 create mode 100644 sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/pem/pkcs8-rsa-1024.pem
 create mode 100644 sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/pem/pkcs8-rsa-2048.pem
 create mode 100644 sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/pem/pkcs8-rsa-3072.pem
 create mode 100644 sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/pem/pkcs8-rsa-4096.pem


[mina-sshd] 01/02: Do not download sources or javadoc of our own modules

Posted by lg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

lgoldstein pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mina-sshd.git

commit bfb1ee80d89a64cbaa8b2a9f848a5366f793cb48
Author: Lyor Goldstein <lg...@apache.org>
AuthorDate: Fri May 22 19:45:24 2020 +0300

    Do not download sources or javadoc of our own modules
---
 pom.xml | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/pom.xml b/pom.xml
index c22a1e8..537336a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1250,6 +1250,8 @@
                             <goal>sources</goal>
                         </goals>
                         <configuration>
+                                <!-- no need to download our own sources - especially snapshot -->
+                            <excludeGroupIds>org.apache.sshd</excludeGroupIds>
                             <markersDirectory>${settings.localRepository}/org/apache/sshd/dependency-maven-plugin-markers</markersDirectory>
                             <silent>${dependency.download.silent}</silent>
                         </configuration>
@@ -1261,6 +1263,8 @@
                             <goal>resolve</goal>
                         </goals>
                         <configuration>
+                                <!-- no need to download our own documentation - especially snapshot -->
+                            <excludeGroupIds>org.apache.sshd</excludeGroupIds>
                             <markersDirectory>${settings.localRepository}/org/apache/sshd/dependency-maven-plugin-markers</markersDirectory>
                             <classifier>javadoc</classifier>
                             <silent>${dependency.download.silent}</silent>


[mina-sshd] 02/02: [SSHD-989] Fixed parsing of PKCS8 encoded PEM private key files

Posted by lg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

lgoldstein pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mina-sshd.git

commit d14a99bb5504198111cbe5514063747a98e8d2f5
Author: Lyor Goldstein <lg...@apache.org>
AuthorDate: Fri May 22 16:08:28 2020 +0300

    [SSHD-989] Fixed parsing of PKCS8 encoded PEM private key files
---
 CHANGES.md                                         |   2 +-
 .../loader/pem/ECDSAPEMResourceKeyPairParser.java  | 113 ++++++++++++++-------
 .../loader/pem/PKCS8PEMResourceKeyPairParser.java  |  15 ++-
 .../keys/loader/pem/PKCS8PrivateKeyInfo.java       |  39 ++++++-
 .../apache/sshd/common/util/io/der/DERParser.java  |   6 ++
 .../sshd/common/config/keys/KeyUtilsCloneTest.java |   1 -
 .../pem/PKCS8PEMResourceKeyPairParserTest.java     |  40 ++++----
 .../common/config/keys/loader/pem/pkcs8-ec-256.pem |   5 +
 .../common/config/keys/loader/pem/pkcs8-ec-384.pem |   6 ++
 .../common/config/keys/loader/pem/pkcs8-ec-521.pem |   8 ++
 .../config/keys/loader/pem/pkcs8-rsa-1024.pem      |  16 +++
 .../config/keys/loader/pem/pkcs8-rsa-2048.pem      |  28 +++++
 .../config/keys/loader/pem/pkcs8-rsa-3072.pem      |  40 ++++++++
 .../config/keys/loader/pem/pkcs8-rsa-4096.pem      |  52 ++++++++++
 14 files changed, 307 insertions(+), 64 deletions(-)

diff --git a/CHANGES.md b/CHANGES.md
index 1495950..001985f 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -57,4 +57,4 @@ as much of the available functionality as possible.
 
 * [SSHD-998](https://issues.apache.org/jira/browse/SSHD-998) - Take into account SFTP version preference when establishing initial channel
 
-* [SSHD-989](https://issues.apache.org/jira/browse/SSHD-989) - Implement ECDSA public key recovery for PKCS8 encoded data
+* [SSHD-989](https://issues.apache.org/jira/browse/SSHD-989) - Read correctly ECDSA key pair from PKCS8 encoded data
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/ECDSAPEMResourceKeyPairParser.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/ECDSAPEMResourceKeyPairParser.java
index 0fe9b15..08fe839 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/ECDSAPEMResourceKeyPairParser.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/ECDSAPEMResourceKeyPairParser.java
@@ -87,14 +87,23 @@ public class ECDSAPEMResourceKeyPairParser extends AbstractPEMResourceKeyPairPar
             InputStream inputStream, boolean okToClose)
             throws IOException, GeneralSecurityException {
         try (DERParser parser = new DERParser(NoCloseInputStream.resolveInputStream(inputStream, okToClose))) {
-            return parseECKeyPair(parser);
+            return parseECKeyPair(null, parser);
         }
     }
 
-    public static KeyPair parseECKeyPair(DERParser parser)
+    /**
+     * @param  curve                    The {@link ECCurves curve} represented by this data (in case it was optional and
+     *                                  somehow known externally) if {@code null} then it is assumed to be part of the
+     *                                  parsed data. then it is assumed to be part of the data.
+     * @param  parser                   The {@link DERParser} for the data
+     * @return                          The parsed {@link KeyPair}
+     * @throws IOException              If failed to parse the data
+     * @throws GeneralSecurityException If failed to generate the keys
+     */
+    public static KeyPair parseECKeyPair(ECCurves curve, DERParser parser)
             throws IOException, GeneralSecurityException {
         ASN1Object sequence = parser.readObject();
-        Map.Entry<ECPublicKeySpec, ECPrivateKeySpec> spec = decodeECPrivateKeySpec(sequence);
+        Map.Entry<ECPublicKeySpec, ECPrivateKeySpec> spec = decodeECPrivateKeySpec(curve, sequence);
         if (!SecurityUtils.isECCSupported()) {
             throw new NoSuchProviderException("ECC not supported");
         }
@@ -136,12 +145,14 @@ public class ECDSAPEMResourceKeyPairParser extends AbstractPEMResourceKeyPairPar
      * </CODE>
      * </PRE>
      *
+     * @param  curve       The {@link ECCurves curve} represented by this data (in case it was optional and somehow
+     *                     known externally) if {@code null} then it is assumed to be part of the parsed data.
      * @param  sequence    The {@link ASN1Object} sequence containing the DER encoded data
      * @return             The decoded {@link SimpleImmutableEntry} of {@link ECPublicKeySpec} and
      *                     {@link ECPrivateKeySpec}
      * @throws IOException If failed to to decode the DER stream
      */
-    public static SimpleImmutableEntry<ECPublicKeySpec, ECPrivateKeySpec> decodeECPrivateKeySpec(ASN1Object sequence)
+    public static Map.Entry<ECPublicKeySpec, ECPrivateKeySpec> decodeECPrivateKeySpec(ECCurves curve, ASN1Object sequence)
             throws IOException {
         ASN1Type objType = (sequence == null) ? null : sequence.getObjType();
         if (!ASN1Type.SEQUENCE.equals(objType)) {
@@ -149,13 +160,10 @@ public class ECDSAPEMResourceKeyPairParser extends AbstractPEMResourceKeyPairPar
         }
 
         try (DERParser parser = sequence.createParser()) {
-            ECPrivateKeySpec prvSpec = decodeECPrivateKeySpec(parser);
-            ECCurves curve = ECCurves.fromCurveParameters(prvSpec.getParams());
-            if (curve == null) {
-                throw new StreamCorruptedException("Unknown curve");
-            }
-
-            ECPoint w = decodeECPublicKeyValue(curve, parser);
+            Map.Entry<ECPrivateKeySpec, ASN1Object> result = decodeECPrivateKeySpec(curve, parser);
+            ECPrivateKeySpec prvSpec = result.getKey();
+            ASN1Object publicData = result.getValue();
+            ECPoint w = (publicData == null) ? decodeECPublicKeyValue(parser) : decodeECPointData(publicData);
             ECPublicKeySpec pubSpec = new ECPublicKeySpec(w, prvSpec.getParams());
             return new SimpleImmutableEntry<>(pubSpec, prvSpec);
         }
@@ -171,7 +179,8 @@ public class ECDSAPEMResourceKeyPairParser extends AbstractPEMResourceKeyPairPar
      *      publicKey [1] BIT STRING OPTIONAL
      * }
      */
-    public static final ECPrivateKeySpec decodeECPrivateKeySpec(DERParser parser) throws IOException {
+    public static Map.Entry<ECPrivateKeySpec, ASN1Object> decodeECPrivateKeySpec(ECCurves curve, DERParser parser)
+            throws IOException {
         // see openssl asn1parse -inform PEM -in ...file... -dump
         ASN1Object versionObject = parser.readObject();
         if (versionObject == null) {
@@ -208,12 +217,36 @@ public class ECDSAPEMResourceKeyPairParser extends AbstractPEMResourceKeyPairPar
          * curve domain parameters. Though the ASN.1 indicates that the parameters field is OPTIONAL, implementations
          * that conform to this document MUST always include the parameters field.
          */
-        ASN1Object paramsObject = parser.readObject();
+        Map.Entry<ECCurves, ASN1Object> result = parseCurveParameter(parser);
+        ECCurves namedParam = (result == null) ? null : result.getKey();
+        if (namedParam == null) {
+            if (curve == null) {
+                throw new StreamCorruptedException("Cannot determine curve type");
+            }
+        } else if (curve == null) {
+            curve = namedParam;
+        } else if (namedParam != curve) {
+            throw new StreamCorruptedException("Mismatched provide (" + curve + ") vs. parsed curve (" + namedParam + ")");
+        }
+
+        BigInteger s = ECCurves.octetStringToInteger(keyObject.getPureValueBytes());
+        ECPrivateKeySpec keySpec = new ECPrivateKeySpec(s, curve.getParameters());
+        return new SimpleImmutableEntry<>(keySpec, (result == null) ? null : result.getValue());
+    }
+
+    public static Map.Entry<ECCurves, ASN1Object> parseCurveParameter(DERParser parser) throws IOException {
+        return parseCurveParameter(parser.readObject());
+    }
+
+    public static Map.Entry<ECCurves, ASN1Object> parseCurveParameter(ASN1Object paramsObject) throws IOException {
         if (paramsObject == null) {
-            throw new StreamCorruptedException("No parameters value");
+            return null;
         }
 
-        // TODO make sure params object tag is 0xA0
+        ASN1Type objType = paramsObject.getObjType();
+        if (objType == ASN1Type.NULL) {
+            return null;
+        }
 
         List<Integer> curveOID;
         try (DERParser paramsParser = paramsObject.createParser()) {
@@ -223,10 +256,13 @@ public class ECDSAPEMResourceKeyPairParser extends AbstractPEMResourceKeyPairPar
             }
 
             /*
-             * SSHD-989 - if object type is BIT STRING then this is the public
-             * key - in which case we need to figure out some other way to recover
-             * the curve parameters
+             * The curve OID is OPTIONAL - if it is not there then the
+             * public key data replaces it
              */
+            objType = namedCurve.getObjType();
+            if (objType == ASN1Type.BIT_STRING) {
+                return new SimpleImmutableEntry<>(null, namedCurve);
+            }
 
             curveOID = namedCurve.asOID();
         }
@@ -236,8 +272,7 @@ public class ECDSAPEMResourceKeyPairParser extends AbstractPEMResourceKeyPairPar
             throw new StreamCorruptedException("Unknown curve OID: " + curveOID);
         }
 
-        BigInteger s = ECCurves.octetStringToInteger(keyObject.getPureValueBytes());
-        return new ECPrivateKeySpec(s, curve.getParameters());
+        return new SimpleImmutableEntry<>(curve, null);
     }
 
     /**
@@ -252,14 +287,16 @@ public class ECDSAPEMResourceKeyPairParser extends AbstractPEMResourceKeyPairPar
      * </code>
      * </pre>
      *
-     * @param  curve       The {@link ECCurves} curve
      * @param  parser      The {@link DERParser} assumed to be positioned at the start of the data
      * @return             The encoded {@link ECPoint}
      * @throws IOException If failed to create the point
      */
-    public static final ECPoint decodeECPublicKeyValue(ECCurves curve, DERParser parser) throws IOException {
+    public static final ECPoint decodeECPublicKeyValue(DERParser parser) throws IOException {
+        return decodeECPublicKeyValue(parser.readObject());
+    }
+
+    public static final ECPoint decodeECPublicKeyValue(ASN1Object dataObject) throws IOException {
         // see openssl asn1parse -inform PEM -in ...file... -dump
-        ASN1Object dataObject = parser.readObject();
         if (dataObject == null) {
             throw new StreamCorruptedException("No public key data bytes");
         }
@@ -267,24 +304,26 @@ public class ECDSAPEMResourceKeyPairParser extends AbstractPEMResourceKeyPairPar
         /*
          * According to https://tools.ietf.org/html/rfc5915
          *
-         * Though the ASN.1 indicates publicKey is OPTIONAL, implementations that conform to this document SHOULD always
-         * include the publicKey field
+         * Though the ASN.1 indicates publicKey is OPTIONAL, implementations
+         * that conform to this document SHOULD always include the publicKey field
          */
         try (DERParser dataParser = dataObject.createParser()) {
-            ASN1Object pointData = dataParser.readObject();
-            if (pointData == null) {
-                throw new StreamCorruptedException("Missing public key data parameter");
-            }
+            return decodeECPointData(dataParser.readObject());
+        }
+    }
 
-            ASN1Type objType = pointData.getObjType();
-            if (!ASN1Type.BIT_STRING.equals(objType)) {
-                throw new StreamCorruptedException("Non-matching public key object type: " + objType);
-            }
+    public static final ECPoint decodeECPointData(ASN1Object pointData) throws IOException {
+        if (pointData == null) {
+            throw new StreamCorruptedException("Missing public key data parameter");
+        }
 
-            // see https://tools.ietf.org/html/rfc5480#section-2.2
-            byte[] octets = pointData.getValue();
-            return ECCurves.octetStringToEcPoint(octets);
+        ASN1Type objType = pointData.getObjType();
+        if (!ASN1Type.BIT_STRING.equals(objType)) {
+            throw new StreamCorruptedException("Non-matching public key object type: " + objType);
         }
-    }
 
+        // see https://tools.ietf.org/html/rfc5480#section-2.2
+        byte[] octets = pointData.getValue();
+        return ECCurves.octetStringToEcPoint(octets);
+    }
 }
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParser.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParser.java
index f871a3a..3207c38 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParser.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParser.java
@@ -34,6 +34,7 @@ import java.util.List;
 import java.util.Map;
 
 import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.cipher.ECCurves;
 import org.apache.sshd.common.config.keys.FilePasswordProvider;
 import org.apache.sshd.common.config.keys.KeyUtils;
 import org.apache.sshd.common.session.SessionContext;
@@ -41,6 +42,7 @@ import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.ValidateUtils;
 import org.apache.sshd.common.util.io.IoUtils;
 import org.apache.sshd.common.util.io.der.ASN1Object;
+import org.apache.sshd.common.util.io.der.ASN1Type;
 import org.apache.sshd.common.util.io.der.DERParser;
 import org.apache.sshd.common.util.security.SecurityUtils;
 
@@ -91,8 +93,19 @@ public class PKCS8PEMResourceKeyPairParser extends AbstractPEMResourceKeyPairPar
         if (SecurityUtils.isECCSupported()
                 && ECDSAPEMResourceKeyPairParser.ECDSA_OID.equals(oid)) {
             ASN1Object privateKeyBytes = pkcs8Info.getPrivateKeyBytes();
+            ASN1Object extraInfo = pkcs8Info.getAlgorithmParameter();
+            ASN1Type objType = (extraInfo == null) ? ASN1Type.NULL : extraInfo.getObjType();
+            List<Integer> oidCurve = (objType == ASN1Type.NULL) ? Collections.emptyList() : extraInfo.asOID();
+            ECCurves curve = null;
+            if (GenericUtils.isNotEmpty(oidCurve)) {
+                curve = ECCurves.fromOIDValue(oidCurve);
+                if (curve == null) {
+                    throw new NoSuchAlgorithmException("Cannot match EC curve OID=" + oidCurve);
+                }
+            }
+
             try (DERParser parser = privateKeyBytes.createParser()) {
-                kp = ECDSAPEMResourceKeyPairParser.parseECKeyPair(parser);
+                kp = ECDSAPEMResourceKeyPairParser.parseECKeyPair(curve, parser);
             }
         } else {
             PrivateKey prvKey = decodePEMPrivateKeyPKCS8(oidAlgorithm, encBytes);
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PrivateKeyInfo.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PrivateKeyInfo.java
index 48917a5..b8732b5 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PrivateKeyInfo.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PrivateKeyInfo.java
@@ -55,6 +55,7 @@ import org.apache.sshd.common.util.io.der.DERParser;
 public class PKCS8PrivateKeyInfo /* TODO Cloneable */ {
     private BigInteger version;
     private List<Integer> algorithmIdentifier;
+    private ASN1Object algorithmParameter;
     private ASN1Object privateKeyBytes;
 
     public PKCS8PrivateKeyInfo() {
@@ -89,6 +90,14 @@ public class PKCS8PrivateKeyInfo /* TODO Cloneable */ {
         this.algorithmIdentifier = algorithmIdentifier;
     }
 
+    public ASN1Object getAlgorithmParameter() {
+        return algorithmParameter;
+    }
+
+    public void setAlgorithmParameter(ASN1Object algorithmParameter) {
+        this.algorithmParameter = algorithmParameter;
+    }
+
     public ASN1Object getPrivateKeyBytes() {
         return privateKeyBytes;
     }
@@ -115,6 +124,21 @@ public class PKCS8PrivateKeyInfo /* TODO Cloneable */ {
      * @throws IOException    If failed to parse the encoding
      */
     public void decode(ASN1Object privateKeyInfo) throws IOException {
+        /*
+         * SEQUENCE {
+         *      INTEGER 0x00 (0 decimal)
+         *      SEQUENCE {
+         *         OBJECTIDENTIFIER encryption type
+         *         OBJECTIDENTIFIER extra info - may be NULL
+         *      }
+         *      OCTETSTRING private key
+         * }
+         */
+        ASN1Type objType = privateKeyInfo.getObjType();
+        if (objType != ASN1Type.SEQUENCE) {
+            throw new StreamCorruptedException("Not a top level sequence: " + objType);
+        }
+
         try (DERParser parser = privateKeyInfo.createParser()) {
             ASN1Object versionObject = parser.readObject();
             if (versionObject == null) {
@@ -128,10 +152,21 @@ public class PKCS8PrivateKeyInfo /* TODO Cloneable */ {
                 throw new StreamCorruptedException("No private key algorithm");
             }
 
+            objType = privateKeyInfo.getObjType();
+            if (objType != ASN1Type.SEQUENCE) {
+                throw new StreamCorruptedException("Not an algorithm parameters sequence: " + objType);
+            }
+
             try (DERParser oidParser = privateKeyAlgorithm.createParser()) {
                 ASN1Object oid = oidParser.readObject();
                 setAlgorithmIdentifier(oid.asOID());
-                // TODO add optional algorithm identifier parameters parsing
+
+                // Extra information is OPTIONAL
+                ASN1Object extraInfo = oidParser.readObject();
+                objType = (extraInfo == null) ? ASN1Type.NULL : extraInfo.getObjType();
+                if (objType != ASN1Type.NULL) {
+                    setAlgorithmParameter(extraInfo);
+                }
             }
 
             ASN1Object privateKeyData = parser.readObject();
@@ -139,7 +174,7 @@ public class PKCS8PrivateKeyInfo /* TODO Cloneable */ {
                 throw new StreamCorruptedException("No private key data");
             }
 
-            ASN1Type objType = privateKeyData.getObjType();
+            objType = privateKeyData.getObjType();
             if (objType != ASN1Type.OCTET_STRING) {
                 throw new StreamCorruptedException("Private key data not an " + ASN1Type.OCTET_STRING + ": " + objType);
             }
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/der/DERParser.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/der/DERParser.java
index 4a4536e..a9fde49 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/util/io/der/DERParser.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/der/DERParser.java
@@ -27,6 +27,7 @@ import java.io.StreamCorruptedException;
 import java.math.BigInteger;
 import java.util.Arrays;
 
+import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.NumberUtils;
 import org.apache.sshd.common.util.buffer.BufferUtils;
 
@@ -122,6 +123,11 @@ public class DERParser extends FilterInputStream {
             return null;
         }
 
+        ASN1Type objType = ASN1Type.fromDERValue(tag);
+        if (objType == ASN1Type.NULL) {
+            return new ASN1Object((byte) tag, 0, GenericUtils.EMPTY_BYTE_ARRAY);
+        }
+
         int length = readLength();
         byte[] value = new byte[length];
         int n = read(value);
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsCloneTest.java b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsCloneTest.java
index 0b64d08..5b3e9e6 100644
--- a/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsCloneTest.java
+++ b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsCloneTest.java
@@ -116,5 +116,4 @@ public class KeyUtilsCloneTest extends JUnitTestSupport {
             assertTrue(prefix + ": Cloned private key not equals", KeyUtils.compareKeys(k1, k2));
         }
     }
-
 }
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParserTest.java b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParserTest.java
index 6d62616..76b9224 100644
--- a/sshd-common/src/test/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParserTest.java
+++ b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParserTest.java
@@ -26,7 +26,6 @@ import java.security.GeneralSecurityException;
 import java.security.KeyPair;
 import java.security.KeyPairGenerator;
 import java.security.PrivateKey;
-import java.security.PublicKey;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
@@ -43,7 +42,6 @@ import org.apache.sshd.util.test.JUnitTestSupport;
 import org.apache.sshd.util.test.NoIoTestCase;
 import org.junit.Assume;
 import org.junit.FixMethodOrder;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
 import org.junit.runner.RunWith;
@@ -81,6 +79,11 @@ public class PKCS8PEMResourceKeyPairParserTest extends JUnitTestSupport {
         }
         if (SecurityUtils.isECCSupported()) {
             for (ECCurves curve : ECCurves.VALUES) {
+                if (!curve.isSupported()) {
+                    outputDebugMessage("Skip unsupported curve=%s", curve);
+                    continue;
+                }
+
                 params.add(new Object[] { KeyUtils.EC_ALGORITHM, curve.getKeySize() });
             }
         }
@@ -88,7 +91,7 @@ public class PKCS8PEMResourceKeyPairParserTest extends JUnitTestSupport {
     }
 
     @Test // see SSHD-760
-    public void testPkcs8() throws IOException, GeneralSecurityException {
+    public void testLocallyGeneratedPkcs8() throws IOException, GeneralSecurityException {
         KeyPairGenerator generator = SecurityUtils.getKeyPairGenerator(algorithm);
         if (keySize > 0) {
             generator.initialize(keySize);
@@ -114,14 +117,18 @@ public class PKCS8PEMResourceKeyPairParserTest extends JUnitTestSupport {
         }
     }
 
-    // see https://gist.github.com/briansmith/2ee42439923d8e65a266994d0f70180b
-    // openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -pkeyopt ec_param_enc:named_curve -out pkcs8-ecdsa-256.pem
-    // openssl ecparam -genkey -name prime256v1 -noout -out pkcs8-ec-256.key
-    // openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in pkcs8-ec-256.key -out pkcs8-ec-256.pem
-    // openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:1024 -out pkcs8-rsa-1024.pem
-    // openssl asn1parse -inform PEM -in ...file... -dump
+    /*
+     * See https://gist.github.com/briansmith/2ee42439923d8e65a266994d0f70180b
+     *
+     * openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:1024 -out pkcs8-rsa-1024.pem
+     * openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -pkeyopt ec_param_enc:named_curve -out pkcs8-ecdsa-256.pem
+     *
+     * openssl ecparam -genkey -name prime256v1 -noout -out pkcs8-ec-256.key
+     * openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in pkcs8-ec-256.key -out pkcs8-ec-256.pem
+     *
+     * openssl asn1parse -inform PEM -in ...file... -dump
+     */
     @Test // see SSHD-989
-    @Ignore("WIP")
     public void testPKCS8FileParsing() throws Exception {
         String resourceKey = "pkcs8-" + algorithm.toLowerCase() + "-" + keySize + ".pem";
         URL url = getClass().getResource(resourceKey);
@@ -129,18 +136,7 @@ public class PKCS8PEMResourceKeyPairParserTest extends JUnitTestSupport {
 
         Collection<KeyPair> pairs = PKCS8PEMResourceKeyPairParser.INSTANCE.loadKeyPairs(null, url, null);
         assertEquals("Mismatched extract keys count", 1, GenericUtils.size(pairs));
-
-        assertSignatureMatch("Cannot sign with recovered key pair", GenericUtils.head(pairs));
-    }
-
-    private static void assertSignatureMatch(String message, KeyPair kp) throws GeneralSecurityException {
-        assertSignatureMatch(message, kp.getPrivate(), kp.getPublic());
-    }
-
-    private static void assertSignatureMatch(
-            String message, PrivateKey privateKey, PublicKey publicKey)
-            throws GeneralSecurityException {
-        // TODO
+        validateKeyPairSignable(algorithm + "/" + keySize, GenericUtils.head(pairs));
     }
 
     @Override
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/pem/pkcs8-ec-256.pem b/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/pem/pkcs8-ec-256.pem
new file mode 100644
index 0000000..d7727e1
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/pem/pkcs8-ec-256.pem
@@ -0,0 +1,5 @@
+-----BEGIN PRIVATE KEY-----
+MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgs6QE06EskVFA/o7i
+HrNnF9In14QcJC9EgkXsVFk+SWWhRANCAAT8lGjPLQVdmwglhBP9refqp9Mrr7AN
+pGSOy3cCDtG4JeRr25s+EXavossaZ9U8MWe39wWUV7yvz5BT5hA3HSig
+-----END PRIVATE KEY-----
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/pem/pkcs8-ec-384.pem b/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/pem/pkcs8-ec-384.pem
new file mode 100644
index 0000000..e552dfa
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/pem/pkcs8-ec-384.pem
@@ -0,0 +1,6 @@
+-----BEGIN PRIVATE KEY-----
+MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDAKagn5+JY4/pjeHLUX
+IORdDtAd8l//84hnzxiWR80AHLnyI8N4YUp7zPGUY0n5/VehZANiAAQacLGO21zt
+XkO/jijS+1BMxfuZyvtDE0fyENi6FNsYz92s+szssUxLl1XPO1Bv7+xdX/nkqjbi
+V26a3G8VzoNUl8KNrYUfH+fcukhVKCU3A9VP8u1HZBhOIn+ouKUSj5E=
+-----END PRIVATE KEY-----
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/pem/pkcs8-ec-521.pem b/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/pem/pkcs8-ec-521.pem
new file mode 100644
index 0000000..4281d87
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/pem/pkcs8-ec-521.pem
@@ -0,0 +1,8 @@
+-----BEGIN PRIVATE KEY-----
+MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIAliKmTYJEBwcWV4a+
+nbS69ht7d3mvUrp60m7T+gAXxUpb5XNWwaxa3PxRY9Mm4Or8mOfPa8d6rSlNARFP
+mU/zOFuhgYkDgYYABAAmm+nrn29TxAonRop25S9DFRX30ci2E+b3qDBy94N+A06n
+Q+wLo+vK95KbG461R9JUXBlH2qLQnLhUle5KpNw9nwAy5vZgrwqmCB1cdDarkTGr
+FpkVsrovB6mD7nxY/13wws1Ll1or3Bsb6ZQfnZ9VloaEVnnc0QLeO8HaRxif865D
+tA==
+-----END PRIVATE KEY-----
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/pem/pkcs8-rsa-1024.pem b/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/pem/pkcs8-rsa-1024.pem
new file mode 100644
index 0000000..6f6df20
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/pem/pkcs8-rsa-1024.pem
@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALpizZejDUx5l83q
+I9Kg75KhYvj5mRVBdTRS8aN/bjiDNOn+yXeRVIB5Vw/r+V6N+1UzTcD+t4sC1b61
+mFgOqn5d9adDWw//zROovmCujqvJCDWTZPpuj/05u2BzlDzrkgTXQ/k4Owu9SjQQ
+hcrbkaHtvc1/Cfi1DfEv+n7FxABRAgMBAAECgYB9t9ku99cnhziittSU5OLTh7IH
+d+wOz0ksEupUOsbwrWeKkcX4tXlG8xGLdsKMSb6GWIWQsP7CcBYWfcyVUMckL4HP
+0jx+6hx7Y3uFtM1/MkucM0D+UAB6cQXHRr2xoRqpiQWBcOAL7dT7IBRMaB4iOGyI
+sDnsrucoX2hQU6kXQQJBAO8M4+XZgH2t/4HHA3Upmi5j0L7JmWusnq5Lhf8Va8mO
+fLIO0AuKuUtvhWlne3cyUZbaSBOE13DTwNn57LcGjikCQQDHmfpOXDrJCcOjKjQ5
+9g6oVWwU9SgZ51J3lVr83GaMCBdx0zMz5V7eGRsXudAHk6iVHdrznZBEdzWHAlmX
+8VXpAkEA1TqwRiQ+wtxj3wUABpA3YU3Ts3rsCOmPGXVwbtpSrRUWEVW5KbJyGeG+
+JQkTTn1p3Z+TTyXdblzT1xthlNiaEQJABHlaF/mPQ8RZQ0YF56qxR2qqwomAPZxm
+x9FsObDDB66CwAVo52fjyXysk8qRdCoGJFmH99/3ROGbLIyL75D0SQJBAI/EHfWS
+n3+A/S0R+rQ9GIKXa1Y5wEgVPYKk+YPLfxdOUEj5ZLI5jrE2mHn7ILFnyZpgci6l
+wxLeAa68VX4lp7g=
+-----END PRIVATE KEY-----
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/pem/pkcs8-rsa-2048.pem b/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/pem/pkcs8-rsa-2048.pem
new file mode 100644
index 0000000..4942fb1
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/pem/pkcs8-rsa-2048.pem
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDJqeUR4mteSX3c
+UuZdaYOwdVcck2X9Ce02wxrUM/xa3NokhjQ+tNJY9FU8/fEYRASHMtUm3lBtnJkV
+XWHlA2BKl8oUbkFzfDCk3B1iXGjAZTVkSzImmhhqtgWcKZmGoL5Irf+8w4jHf8rQ
+5mp9hK3nQkTiAlELhAP2cEdDQcmynxN7zFkY0sMZQUeEQO1hHo+plwn3H33Cxq87
+XiyFKAJArqPmOEVkl/6oUY4MX5DRCPaYXGnINsAFY0PfmSPk1iV8HlevaeeiOmx2
+MNKiOw+PtlCr97QymmcdxKJzwmVKTLXrK9NHC8A/SJD6WUdY1aF1mjSVqeo/1y3f
+GfUF9MKZAgMBAAECggEBAKFhon1DcqTLrzsH5G5QqCAoZwPpOS7cKMcwL2IuD/8u
+yit8cobT8ZlaPnRGzA+dLvp6xXULZ9WwAhnE1ziMERzgh8j9ysb+VXc45xL13KZK
+2AVg38tgebW74JVt/Pxt2pkTFZsb53OvYsD2A7Za3Ug6EiHDtNPAW+N1SrIaDa0w
+kB+VMVVnaua+OuNBkrXEbhB14CJG/DGNiMvm6PkVNd9oQkbxykp1c6PU2ZQ4jJos
+6oKeqMwXSSvwWhen9YoYtSaff2bJRy6HWaPiRAHKFw1WQMoAksmAvBsU/nwA6c/t
+AOpzKcvX8b/VFlZpY9vdylXS/2ysK5lDTmXX9FWjhRkCgYEA73V7fnXJ10avbd85
+2/oTFmTB4rOg4a5VBqSy/AZiOwlLUyadUkyLR8gF7vsVB7PSnwH5HlGRORC+COnH
+GVj+44gq3EbjwucqeYlSizW8hjLFOpYcm0AP1naQCiiBG0xUxFwc8xyE0zu+V9KX
+8uHQTGejMEnz7Nx7j+LWxJchsEcCgYEA15gNeCaPb6TMR7LtUxtZ5+Rt0zLdG44Q
+yMmrReT79via7HkFyxOZaO+8tk6APBNGx1mU8IXYpZWK0CPIHeKlqI8roqlfWQdp
+XzQ7Zz1A4f02/o6DQtZthhTSg5K/iZ6Tb3KrKTuh0EO7yUfG+ybzfszwz8GJGdGt
+uIOY4gNrRh8CgYA2qwmgm1+TSE3wtY/OCs+kwygIi53lKBm9RIigRQzUEZEi0KQG
+D/eUUbQZFTV95q3lI1wucczHzGy2ODj+LnUymPnABGcnLgNib9lqcsAxmxGwCGlL
+gFqdScAksY6YHtsTYTwyvIYOe4s/HZMXHjqh1t9IvPl1T/jdppoFk8NbLQKBgQDU
+JymiAXgGqgnnyFgn/vNC8ZNtUFEqq2sy2tky53lW+B8j8pfT1c6R59AxKiCgfWua
+AjpBUcT2dKjr1zo2xnCz5WdQIxHTvype6DxIhItTl2TFrKHYZL/UQKtDlGXtW+HD
+uvhZk/fQxMaG9J4HSbY1IiEaoF10zdQAjWclia3HiwKBgQDX1piVYGI4c5HtC50h
+NXdbPH+jxgdr3Hj43cki4OaXTg5drX8ZVFc0/fUhASAEG6WbFg9nDU0mH8o+5zoJ
+JehiL/RQOK2s70fitgBYF6RBc+znBp5OEqdQiWn1UlTZ/CdSPfR4vkMADzFEYGxZ
+YVsrUwMSVO65vmGmyfvmIL682g==
+-----END PRIVATE KEY-----
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/pem/pkcs8-rsa-3072.pem b/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/pem/pkcs8-rsa-3072.pem
new file mode 100644
index 0000000..9d6a219
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/pem/pkcs8-rsa-3072.pem
@@ -0,0 +1,40 @@
+-----BEGIN PRIVATE KEY-----
+MIIG/gIBADANBgkqhkiG9w0BAQEFAASCBugwggbkAgEAAoIBgQDMa+XSQ2CFodJv
+PLd4zJ6uBQiOhUTabN/5W88zDacgbzED+GFGmx8y9Chy1ew/7o0I1BS7LRsoRiqr
+GH/nKShijAfhAbkxzE3OZ3wuwV8yjt0nBASRYmC0aQT9WbXliWXbEqRSynuJjXPG
+Ji0DUY6z8VdzPIQGjHeVegnEDjdTRAJfi8ciVE0F8MJqtWfwpDt7zcB8ZTDMV+Td
+HxbaDyfraumUgg5EgWhogGGZuhZezLSrcx6B4eTRS4xhUWj8J8m3lZQjeayLLTtD
+UBdz2CucSUnZCOG2oblrrXE/i+Rh5d+WQ2GPJA1pWO49YkCfUU765KN0vu4d9muC
+wPByjXEzl1AQx+MooxNiGeANOdNTcPB2vHk+bmIoYgbHCMr7zJCsVstyaRYu7uON
+Kv8hMfC7Nv8PSw5mcHP9dusuGMXOfJ0FvC6ouQ/O2zXjZ8K1/U9f/uVToIz5VRUA
+JfoPAYPRILjZhNujTLK6jcdIlJpCTmqPJ+NPeAEGpjJRTBKRo8kCAwEAAQKCAYEA
+vbiGG5ccxelh/ItFXH/L5YYWYu+c73uMg0mKC7/oFFoeC6lB3t2pHwkrYSjZkpw7
+mK38b5t1UPOONi0Ox+OS76M2zMVks6sBq0awIlSlna6p4cQA2U2MouO1Fc1k3Bug
+xKmQiKYT8Z2ujYBw1lujLa4Xk4PepJVJhxk0Zxkqj8TWzwZTUrEaqyC/z3l9dgF2
+k3hp5QmlOIF6jx6Dfu4CBqO7FXF+/GV+GT7NVnc2u9UQ+O5mqfSVAQo0xz1fSmdB
+TIvftiych/uVtxox8HIivJGL0WdsTbLyZDITocMDFMgg1VUynpTzTN4i/CCID3Cs
+4SsWPWtHXfj3o4gDq4YoSWNDK6Ckaj5V5AOgX9EnDo6as8SDqeYXZD8Tm9AQcDSe
+kKEyCxOMgNIjDwIbHyS5V93ojKcNQEsxYq982PJ3pByoiFyYPMWVhHlPst+9LgE0
+5OrM/SObmXSoP2wvXUl38qjPT7V5etng87EFNd3k6FViZFWtbV1edAyd5RAh/5fh
+AoHBAP3Wx6QrFRIucVZVaZDp5/xq+XX7QzEEJXTrCWvQQrSv1hysAJnH+ti9lthj
+Hz5/mzKrVbq8+M/97jbkO7TaAkPM4Pudk3DGRP4OKRfKlVid4B3aIep0aIC4KoR0
+PXBA+LfLY6NrAixaQuUry1fQ6gDBUI3f6Qxcw1bOoKBtKED5Ie6ltG9QQulGnAMJ
+zGDS/fPUONLeS4TuwyBfoE1PQyHeKMxQS9n3zOMLwqB5NkegWEv3nDidYR2kJ7GC
+RRxszwKBwQDOKWqstfvTiS+Zvr2Zgs6OYAvjSsZ3vwvnQID/LJO82sPjSumLooEQ
+r1ewIqK1VH7gmgrr3i33q7I1RTlSfCnP7FJD2c3WW1dJcHkoeSVmt0exNM24vwMS
+rRfprHD9s7wHRD9irutrETGSMKfpka3AHpteo15Kyde33Aue4Qx4hL2i72qpnCwt
+RVJ5+eRcpaR2gUR1x5KUjeje+1HwU7dkEdUkYmqcxVe6I3dpbq5DxRc9D2hPRlJk
+zZLzXikce+cCgcAdlWiTE4pTIiKHY1D4WKp22qjUPUJpdgg/hh0E+bKsiEm18b3o
+Lkxn8kCgW30KtaiK0TkemGOPKhMXRqZGv5m/+SLHcGf8nr7vtQrJAQ1C1LOIByIo
+xwRe7BfYdAutB4V1NjkYlKIeNS7SsrXyOCDtkZonzs7EaBNEDLTfvZkRaXew7pMG
+3h3OPjJ0kDHHnw+F2Vf+C3ZVudX38e0m1XQHgHLUzQ7qCl1QoNBAD6Bp3KAtyl/k
+oULuR3Fw2LPhSjUCgcAe2nPshQ+7CLzm9XTKlJj1FcqxqW8qXJ0bbrvfdHxntxW5
+3mw1SYynQpaM9aIEITEby/H2ernGZxu0fTem8I4RX/yvytjTS7g0dXCsbfT6+lLw
+Ykanb262TNFXV0dRsKRjMgOKcUMqMtiIWF/IxNSL/AikkS57Ytm12miizmtfXf5D
+dDEyUP0LiWRefNeARgnm8lGcjtGRCevf8xzAKsc3YrPTTidGbwJCCSzFypqp6cUg
+jj2+H3gVPe7QHTdp2+ECgcEAsAPL7SryJe2vGMNJShqqfOaqGZ4jLrOIMjWUqx9Q
+r3Bx0DKxDNYm7i0yhIafqEPXRKrMfBAusSLFixleKL0r8uHCF9mH5Of3meJGPV9o
+st8SYZq/DqpkmTpSFPs8kki2jpVCPg3v5KGCCQOQDeuWZtrlAAlh24PlcZ1pNXpB
+gkQjMBb4NecXMHppny4BsNwpbK9frZa0Zow0HFIxvfHn4N5iTBH71t/v6Om+U2UW
+uvn49/VWU7/8Ppw/Wg7C+L0B
+-----END PRIVATE KEY-----
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/pem/pkcs8-rsa-4096.pem b/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/pem/pkcs8-rsa-4096.pem
new file mode 100644
index 0000000..f625c8e
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/pem/pkcs8-rsa-4096.pem
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+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
+18uzMDlMbqOoq+IErsvMbHs/fl8GtJ5S
+-----END PRIVATE KEY-----