You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by lg...@apache.org on 2018/09/06 16:03:12 UTC

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

[SSHD-842] Split Putty key files support code to separate artifact


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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-putty/pom.xml
----------------------------------------------------------------------
diff --git a/sshd-putty/pom.xml b/sshd-putty/pom.xml
new file mode 100644
index 0000000..8e11385
--- /dev/null
+++ b/sshd-putty/pom.xml
@@ -0,0 +1,107 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+
+    <!--
+
+        Licensed to the Apache Software Foundation (ASF) under one or more
+        contributor license agreements.  See the NOTICE file distributed with
+        this work for additional information regarding copyright ownership.
+        The ASF licenses this file to You under the Apache License, Version 2.0
+        (the "License"); you may not use this file except in compliance with
+        the License.  You may obtain a copy of the License at
+
+           http://www.apache.org/licenses/LICENSE-2.0
+
+        Unless required by applicable law or agreed to in writing, software
+        distributed under the License is distributed on an "AS IS" BASIS,
+        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+        See the License for the specific language governing permissions and
+        limitations under the License.
+    -->
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.sshd</groupId>
+        <artifactId>sshd</artifactId>
+        <version>2.0.1-SNAPSHOT</version>
+        <relativePath>..</relativePath>
+    </parent>
+
+    <artifactId>sshd-putty</artifactId>
+    <name>Apache Mina SSHD :: Putty key files support utilities</name>
+    <packaging>jar</packaging>
+    <inceptionYear>2018</inceptionYear>
+
+    <properties>
+        <projectRoot>${project.basedir}/..</projectRoot>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.sshd</groupId>
+            <artifactId>sshd-common</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.bouncycastle</groupId>
+            <artifactId>bcpg-jdk15on</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.bouncycastle</groupId>
+            <artifactId>bcpkix-jdk15on</artifactId>
+            <optional>true</optional>
+        </dependency>
+
+            <!-- For ed25519 support -->
+        <dependency>
+            <groupId>net.i2p.crypto</groupId>
+            <artifactId>eddsa</artifactId>
+            <optional>true</optional>
+        </dependency>
+
+            <!-- test dependencies -->
+        <dependency>
+            <groupId>org.apache.sshd</groupId>
+            <artifactId>sshd-common</artifactId>
+            <version>${project.version}</version>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>jcl-over-slf4j</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-log4j12</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <redirectTestOutputToFile>true</redirectTestOutputToFile>
+                    <reportsDirectory>${project.build.directory}/surefire-reports-putty</reportsDirectory>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>

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