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/11/18 04:55:06 UTC

[11/12] mina-sshd git commit: [SSHD-864] Use a 'NamedResource' instead of plain old string in order to provide key file(s) location information

[SSHD-864] Use a 'NamedResource' instead of plain old string in order to provide key file(s) location information


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

Branch: refs/heads/master
Commit: 895f30f135d4e730dca56d842cc110a04e31a404
Parents: 2b013cc
Author: Lyor Goldstein <lg...@apache.org>
Authored: Thu Nov 15 13:53:20 2018 +0200
Committer: Lyor Goldstein <lg...@apache.org>
Committed: Sun Nov 18 06:54:48 2018 +0200

----------------------------------------------------------------------
 CHANGES.md                                      | 10 ++-
 README.md                                       |  7 ++
 .../sshd/cli/server/SshServerCliSupport.java    |  6 +-
 .../config/keys/ClientIdentityFileWatcher.java  |  3 +-
 .../config/keys/ClientIdentityLoader.java       | 22 ++++---
 .../org/apache/sshd/common/NamedResource.java   | 20 ++++++
 .../config/keys/FilePasswordProvider.java       |  5 +-
 .../sshd/common/config/keys/IdentityUtils.java  |  7 +-
 .../loader/AbstractKeyPairResourceParser.java   | 11 ++--
 .../keys/loader/KeyPairResourceLoader.java      | 37 +++++++----
 .../keys/loader/KeyPairResourceParser.java      | 11 ++--
 .../openssh/OpenSSHKeyPairResourceParser.java   | 11 ++--
 .../pem/AbstractPEMResourceKeyPairParser.java   |  3 +-
 .../loader/pem/DSSPEMResourceKeyPairParser.java |  3 +-
 .../pem/ECDSAPEMResourceKeyPairParser.java      |  3 +-
 .../keys/loader/pem/PEMResourceParserUtils.java |  5 +-
 .../pem/PKCS8PEMResourceKeyPairParser.java      |  3 +-
 .../loader/pem/RSAPEMResourceKeyPairParser.java |  3 +-
 .../AbstractResourceKeyPairProvider.java        | 32 ++++++++--
 .../ClassLoadableResourceKeyPairProvider.java   | 22 ++-----
 .../common/keyprovider/FileKeyPairProvider.java | 17 ++---
 .../util/io/resource/AbstractIoResource.java    | 57 +++++++++++++++++
 .../util/io/resource/ClassLoaderResource.java   | 67 ++++++++++++++++++++
 .../common/util/io/resource/IoResource.java     | 63 ++++++++++++++++++
 .../common/util/io/resource/PathResource.java   | 60 ++++++++++++++++++
 .../io/resource/ResourceStreamProvider.java     | 35 ++++++++++
 .../common/util/io/resource/URIResource.java    | 45 +++++++++++++
 .../common/util/io/resource/URLResource.java    | 50 +++++++++++++++
 .../common/util/security/SecurityUtils.java     |  3 +-
 .../BouncyCastleGeneratorHostKeyProvider.java   |  3 +-
 .../BouncyCastleKeyPairResourceParser.java      | 10 ++-
 .../AbstractGeneratorHostKeyProvider.java       | 18 ++++--
 .../SimpleGeneratorHostKeyProvider.java         |  5 +-
 .../BuiltinClientIdentitiesWatcherTest.java     | 12 ++--
 .../keys/ClientIdentityFileWatcherTest.java     |  7 +-
 .../pem/PKCS8PEMResourceKeyPairParserTest.java  |  3 +-
 .../file/root/RootedFileSystemProviderTest.java |  3 +-
 .../common/util/security/SecurityUtilsTest.java | 12 ++--
 .../AbstractGeneratorHostKeyProviderTest.java   |  5 +-
 .../java/org/apache/sshd/client/SshClient.java  | 18 ++++--
 .../common/forward/DefaultForwardingFilter.java |  2 +-
 .../java/org/apache/sshd/server/SshServer.java  |  6 +-
 .../hosts/HostConfigEntryResolverTest.java      | 14 ++--
 .../sshd/common/auth/AuthenticationTest.java    | 20 ++++--
 .../apache/sshd/common/channel/WindowTest.java  | 34 +++++++---
 .../java/org/apache/sshd/server/ServerTest.java |  4 +-
 .../openpgp/PGPKeyPairResourceParser.java       | 29 +++++----
 .../openpgp/PGPKeyPairResourceParserTest.java   |  7 +-
 .../loader/putty/AbstractPuttyKeyDecoder.java   | 17 ++---
 .../keys/loader/putty/DSSPuttyKeyDecoder.java   |  3 +-
 .../keys/loader/putty/ECDSAPuttyKeyDecoder.java |  3 +-
 .../keys/loader/putty/EdDSAPuttyKeyDecoder.java |  3 +-
 .../putty/PuttyKeyPairResourceParser.java       |  3 +-
 .../keys/loader/putty/RSAPuttyKeyDecoder.java   |  3 +-
 .../keys/loader/putty/PuttyKeyUtilsTest.java    | 14 ++--
 .../helpers/AbstractCheckFileExtensionTest.java | 11 ++--
 .../sftp/ApacheSshdSftpSessionFactory.java      |  8 +--
 .../integration/sftp/SpringIoResource.java      | 51 +++++++++++++++
 58 files changed, 759 insertions(+), 190 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/CHANGES.md
----------------------------------------------------------------------
diff --git a/CHANGES.md b/CHANGES.md
index 9cc1cd4..1b1d0df 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -37,6 +37,8 @@ accept also an `AttributeRepository` connection context argument (propagated fro
 
     * The available session context (if any) is also provided as an argument to the interface methods.
 
+    * The interface methods use a `NamedResource` as the resource key instead of a plain string.
+
 * `SshAgent#getIdentities` returns an `Iterable` rather than a `List`
 
 * `SftpFileSystemProvider` and its associated helper classes have been moved to
@@ -48,7 +50,10 @@ accept also an `AttributeRepository` connection context argument (propagated fro
 
 * `ClientIdentityProvider` accepts a `SessionContext` argument in its `getClientIdentity` method.
 
-* `ClientIdentityLoader` accepts a `SessionContext` argument in its `loadClientIdentity` method.
+* `ClientIdentityLoader`
+    * Accepts a `SessionContext` argument in its `loadClientIdentity` method.
+
+    * Uses a `NamedResource` as the identity location indicator instead of a plain old string.
 
 * `ApacheSshdSftpSessionFactory#get/setPrivateKey` has been renamed to `get/setPrivateKeyLocation`.
 
@@ -77,3 +82,6 @@ pending packets have been sent to the peer channel when closing the tunnel grace
 
 * [SSHD-862](https://issues.apache.org/jira/browse/SSHD-862) - Provide session context argument (if available) when
 key loading methods are invoked.
+
+* [SSHD-864](https://issues.apache.org/jira/browse/SSHD-864) - Using a `NamedResource` instead of plain old string
+in order to provide key file(s) location information

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/README.md
----------------------------------------------------------------------
diff --git a/README.md b/README.md
index 1231528..3b291a0 100644
--- a/README.md
+++ b/README.md
@@ -257,6 +257,13 @@ it will start a loop where it prompts for the password, attempts to decode the k
 the outcome - success or failure. If failure is signaled, then the provider can decide whether to retry using a new password, abort (with exception)
 or ignore. If the provider chooses to ignore the failure, then the code will make a best effort to proceed without the (undecoded) key.
 
+The methods are provided with a `NamedResource` that provides an indication of the key source "name" that is being attempted. This name can
+be used in order to prompt the user interactively and provide a useful "hint" as to the password that needs to be provided. Furthermore, the
+vast majority of the provided `NamedResource`-s also implement `IoResource` - which means that the code can find out what type of resource
+is being attempted - e.g., a file [Path](https://docs.oracle.com/javase/8/docs/api/index.html?java/nio/file/Path.html),
+a [URL](https://docs.oracle.com/javase/8/docs/api/java/net/URL.html), a [URI](https://docs.oracle.com/javase/8/docs/api/java/net/URI.html),
+etc. - and modify it's behavior accordingly.
+
 ### UserInteraction
 
 This interface is required for full support of `keyboard-interactive` authentication protocol as described in [RFC 4256](https://www.ietf.org/rfc/rfc4256.txt).

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerCliSupport.java
----------------------------------------------------------------------
diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerCliSupport.java b/sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerCliSupport.java
index f716553..2521ea1 100644
--- a/sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerCliSupport.java
+++ b/sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerCliSupport.java
@@ -47,6 +47,7 @@ import org.apache.sshd.common.keyprovider.KeyPairProvider;
 import org.apache.sshd.common.keyprovider.MappedKeyPairProvider;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.io.resource.PathResource;
 import org.apache.sshd.common.util.security.SecurityUtils;
 import org.apache.sshd.common.util.threads.ThreadUtils;
 import org.apache.sshd.server.ServerAuthenticationManager;
@@ -115,8 +116,9 @@ public abstract class SshServerCliSupport extends CliSupport {
             List<KeyPair> pairs = new ArrayList<>(keyFiles.size());
             for (String keyFilePath : keyFiles) {
                 Path path = Paths.get(keyFilePath);
-                try (InputStream inputStream = Files.newInputStream(path)) {
-                    KeyPair kp = SecurityUtils.loadKeyPairIdentity(null, keyFilePath, inputStream, null);
+                PathResource location = new PathResource(path);
+                try (InputStream inputStream = location.openInputStream()) {
+                    KeyPair kp = SecurityUtils.loadKeyPairIdentity(null, location, inputStream, null);
                     pairs.add(kp);
                 } catch (Exception e) {
                     stderr.append("Failed (").append(e.getClass().getSimpleName()).append(')')

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcher.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcher.java b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcher.java
index e2e1b82..ca81e47 100644
--- a/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcher.java
+++ b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcher.java
@@ -35,6 +35,7 @@ import org.apache.sshd.common.session.SessionContext;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.io.IoUtils;
 import org.apache.sshd.common.util.io.ModifiableFileWatcher;
+import org.apache.sshd.common.util.io.resource.PathResource;
 
 /**
  * A {@link ClientIdentityProvider} that watches a given key file re-loading
@@ -120,7 +121,7 @@ public class ClientIdentityFileWatcher extends ModifiableFileWatcher implements
             }
         }
 
-        String location = path.toString();
+        PathResource location = new PathResource(path);
         ClientIdentityLoader idLoader = Objects.requireNonNull(getClientIdentityLoader(), "No client identity loader");
         if (idLoader.isValidLocation(location)) {
             KeyPair kp = idLoader.loadClientIdentity(session, location, getFilePasswordProvider());

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityLoader.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityLoader.java b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityLoader.java
index 542697a..1d941ac 100644
--- a/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityLoader.java
+++ b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityLoader.java
@@ -26,11 +26,14 @@ import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.security.GeneralSecurityException;
 import java.security.KeyPair;
+import java.util.Objects;
 
+import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.config.keys.FilePasswordProvider;
 import org.apache.sshd.common.session.SessionContext;
 import org.apache.sshd.common.util.ValidateUtils;
 import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.common.util.io.resource.PathResource;
 import org.apache.sshd.common.util.security.SecurityUtils;
 
 /**
@@ -46,18 +49,19 @@ public interface ClientIdentityLoader {
      */
     ClientIdentityLoader DEFAULT = new ClientIdentityLoader() {
         @Override
-        public boolean isValidLocation(String location) throws IOException {
+        public boolean isValidLocation(NamedResource location) throws IOException {
             Path path = toPath(location);
             return Files.exists(path, IoUtils.EMPTY_LINK_OPTIONS);
         }
 
         @Override
         public KeyPair loadClientIdentity(
-                SessionContext session, String location, FilePasswordProvider provider)
+                SessionContext session, NamedResource location, FilePasswordProvider provider)
                     throws IOException, GeneralSecurityException {
             Path path = toPath(location);
-            try (InputStream inputStream = Files.newInputStream(path, IoUtils.EMPTY_OPEN_OPTIONS)) {
-                return SecurityUtils.loadKeyPairIdentity(session, path.toString(), inputStream, provider);
+            PathResource resource = new PathResource(path);
+            try (InputStream inputStream = resource.openInputStream()) {
+                return SecurityUtils.loadKeyPairIdentity(session, resource, inputStream, provider);
             }
         }
 
@@ -66,8 +70,10 @@ public interface ClientIdentityLoader {
             return "DEFAULT";
         }
 
-        private Path toPath(String location) {
-            Path path = Paths.get(ValidateUtils.checkNotNullAndNotEmpty(location, "No location"));
+        private Path toPath(NamedResource location) {
+            Objects.requireNonNull(location, "No location provided");
+
+            Path path = Paths.get(ValidateUtils.checkNotNullAndNotEmpty(location.getName(), "No location value for %s", location));
             path = path.toAbsolutePath();
             path = path.normalize();
             return path;
@@ -81,7 +87,7 @@ public interface ClientIdentityLoader {
      * the validity depends on the implementation
      * @throws IOException If failed to validate the location
      */
-    boolean isValidLocation(String location) throws IOException;
+    boolean isValidLocation(NamedResource location) throws IOException;
 
     /**
      * @param session The {@link SessionContext} for invoking this load command - may
@@ -97,6 +103,6 @@ public interface ClientIdentityLoader {
      * a valid identity
      */
     KeyPair loadClientIdentity(
-        SessionContext session, String location, FilePasswordProvider provider)
+        SessionContext session, NamedResource location, FilePasswordProvider provider)
             throws IOException, GeneralSecurityException;
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/sshd-common/src/main/java/org/apache/sshd/common/NamedResource.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/NamedResource.java b/sshd-common/src/main/java/org/apache/sshd/common/NamedResource.java
index 813f53d..048d23f 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/NamedResource.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/NamedResource.java
@@ -101,4 +101,24 @@ public interface NamedResource {
                 .findFirst()
                 .orElse(null);
     }
+
+    /**
+     * Wraps a name value inside a {@link NamedResource}
+     *
+     * @param name The name value to wrap
+     * @return The wrapper instance
+     */
+    static NamedResource ofName(String name) {
+        return new NamedResource() {
+            @Override
+            public String getName() {
+                return name;
+            }
+
+            @Override
+            public String toString() {
+                return getName();
+            }
+        };
+    }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/sshd-common/src/main/java/org/apache/sshd/common/config/keys/FilePasswordProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/FilePasswordProvider.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/FilePasswordProvider.java
index 365de50..037cc61 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/FilePasswordProvider.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/FilePasswordProvider.java
@@ -22,6 +22,7 @@ package org.apache.sshd.common.config.keys;
 import java.io.IOException;
 import java.security.GeneralSecurityException;
 
+import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.session.SessionContext;
 
 /**
@@ -53,7 +54,7 @@ public interface FilePasswordProvider {
      * @throws IOException if cannot resolve password
      * @see #handleDecodeAttemptResult(String, int, String, Exception)
      */
-    String getPassword(SessionContext session, String resourceKey, int retryIndex) throws IOException;
+    String getPassword(SessionContext session, NamedResource resourceKey, int retryIndex) throws IOException;
 
     /**
      * Invoked to inform the password provide about the decoding result. <b>Note:</b>
@@ -74,7 +75,7 @@ public interface FilePasswordProvider {
      * @throws GeneralSecurityException If not attempting to resolve a new password
      */
     default ResourceDecodeResult handleDecodeAttemptResult(
-            SessionContext session, String resourceKey, int retryIndex, String password, Exception err)
+            SessionContext session, NamedResource resourceKey, int retryIndex, String password, Exception err)
                 throws IOException, GeneralSecurityException {
         return ResourceDecodeResult.TERMINATE;
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/sshd-common/src/main/java/org/apache/sshd/common/config/keys/IdentityUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/IdentityUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/IdentityUtils.java
index 6d9ab44..0212806 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/IdentityUtils.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/IdentityUtils.java
@@ -21,7 +21,6 @@ package org.apache.sshd.common.config.keys;
 
 import java.io.IOException;
 import java.io.InputStream;
-import java.nio.file.Files;
 import java.nio.file.OpenOption;
 import java.nio.file.Path;
 import java.nio.file.Paths;
@@ -36,6 +35,7 @@ import org.apache.sshd.common.keyprovider.MappedKeyPairProvider;
 import org.apache.sshd.common.session.SessionContext;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.io.resource.PathResource;
 import org.apache.sshd.common.util.security.SecurityUtils;
 
 /**
@@ -151,8 +151,9 @@ public final class IdentityUtils {
         for (Map.Entry<String, ? extends Path> pe : paths.entrySet()) {
             String type = pe.getKey();
             Path path = pe.getValue();
-            try (InputStream inputStream = Files.newInputStream(path, options)) {
-                KeyPair kp = SecurityUtils.loadKeyPairIdentity(session, path.toString(), inputStream, provider);
+            PathResource location = new PathResource(path);
+            try (InputStream inputStream = location.openInputStream()) {
+                KeyPair kp = SecurityUtils.loadKeyPairIdentity(session, location, inputStream, provider);
                 KeyPair prev = ids.put(type, kp);
                 ValidateUtils.checkTrue(prev == null, "Multiple keys for type=%s", type);
             }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/AbstractKeyPairResourceParser.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/AbstractKeyPairResourceParser.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/AbstractKeyPairResourceParser.java
index 0b69906..7c4bf89 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/AbstractKeyPairResourceParser.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/AbstractKeyPairResourceParser.java
@@ -33,6 +33,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.logging.Level;
 
+import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.config.keys.FilePasswordProvider;
 import org.apache.sshd.common.session.SessionContext;
 import org.apache.sshd.common.util.GenericUtils;
@@ -79,14 +80,14 @@ public abstract class AbstractKeyPairResourceParser extends AbstractLoggingBean
     }
 
     @Override
-    public boolean canExtractKeyPairs(String resourceKey, List<String> lines)
+    public boolean canExtractKeyPairs(NamedResource resourceKey, List<String> lines)
             throws IOException, GeneralSecurityException {
         return KeyPairResourceParser.containsMarkerLine(lines, getBeginners());
     }
 
     @Override
     public Collection<KeyPair> loadKeyPairs(
-            SessionContext session, String resourceKey, FilePasswordProvider passwordProvider, List<String> lines)
+            SessionContext session, NamedResource resourceKey, FilePasswordProvider passwordProvider, List<String> lines)
                 throws IOException, GeneralSecurityException {
         Collection<KeyPair> keyPairs = Collections.emptyList();
         List<String> beginMarkers = getBeginners();
@@ -140,7 +141,7 @@ public abstract class AbstractKeyPairResourceParser extends AbstractLoggingBean
      * @see #extractKeyPairs(String, String, String, FilePasswordProvider, byte[])
      */
     public Collection<KeyPair> extractKeyPairs(
-            SessionContext session, String resourceKey,
+            SessionContext session, NamedResource resourceKey,
             String beginMarker, String endMarker,
             FilePasswordProvider passwordProvider,
             List<String> lines)
@@ -163,7 +164,7 @@ public abstract class AbstractKeyPairResourceParser extends AbstractLoggingBean
      * @see #extractKeyPairs(String, String, String, FilePasswordProvider, InputStream)
      */
     public Collection<KeyPair> extractKeyPairs(
-            SessionContext session, String resourceKey,
+            SessionContext session, NamedResource resourceKey,
             String beginMarker, String endMarker,
             FilePasswordProvider passwordProvider,
             byte[] bytes)
@@ -191,7 +192,7 @@ public abstract class AbstractKeyPairResourceParser extends AbstractLoggingBean
      * @throws GeneralSecurityException If failed to generate the keys
      */
     public abstract Collection<KeyPair> extractKeyPairs(
-        SessionContext session, String resourceKey,
+        SessionContext session, NamedResource resourceKey,
         String beginMarker, String endMarker,
         FilePasswordProvider passwordProvider, InputStream stream)
             throws IOException, GeneralSecurityException;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/KeyPairResourceLoader.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/KeyPairResourceLoader.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/KeyPairResourceLoader.java
index 5bf2597..5f76cc6 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/KeyPairResourceLoader.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/KeyPairResourceLoader.java
@@ -28,7 +28,6 @@ import java.io.StringReader;
 import java.net.URL;
 import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
 import java.nio.file.OpenOption;
 import java.nio.file.Path;
 import java.security.GeneralSecurityException;
@@ -38,9 +37,13 @@ import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
 
+import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.config.keys.FilePasswordProvider;
 import org.apache.sshd.common.session.SessionContext;
 import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.common.util.io.resource.IoResource;
+import org.apache.sshd.common.util.io.resource.PathResource;
+import org.apache.sshd.common.util.io.resource.URLResource;
 
 /**
  * Loads {@link KeyPair}s from text resources
@@ -86,9 +89,7 @@ public interface KeyPairResourceLoader {
     default Collection<KeyPair> loadKeyPairs(
             SessionContext session, Path path, FilePasswordProvider passwordProvider, Charset cs, OpenOption... options)
                 throws IOException, GeneralSecurityException {
-        try (InputStream stream = Files.newInputStream(path, options)) {
-            return loadKeyPairs(session, path.toString(), passwordProvider, stream, cs);
-        }
+        return loadKeyPairs(session, new PathResource(path, options), passwordProvider, cs);
     }
 
     default Collection<KeyPair> loadKeyPairs(
@@ -100,13 +101,25 @@ public interface KeyPairResourceLoader {
     default Collection<KeyPair> loadKeyPairs(
             SessionContext session, URL url, FilePasswordProvider passwordProvider, Charset cs)
                 throws IOException, GeneralSecurityException {
-        try (InputStream stream = Objects.requireNonNull(url, "No URL").openStream()) {
-            return loadKeyPairs(session, url.toExternalForm(), passwordProvider, stream, cs);
+        return loadKeyPairs(session, new URLResource(url), passwordProvider, cs);
+    }
+
+    default Collection<KeyPair> loadKeyPairs(
+            SessionContext session, IoResource<?> resource, FilePasswordProvider passwordProvider)
+                throws IOException, GeneralSecurityException {
+        return loadKeyPairs(session, resource, passwordProvider, StandardCharsets.UTF_8);
+    }
+
+    default Collection<KeyPair> loadKeyPairs(
+            SessionContext session, IoResource<?> resource, FilePasswordProvider passwordProvider, Charset cs)
+                throws IOException, GeneralSecurityException {
+        try (InputStream stream = Objects.requireNonNull(resource, "No resource data").openInputStream()) {
+            return loadKeyPairs(session, resource, passwordProvider, stream, cs);
         }
     }
 
     default Collection<KeyPair> loadKeyPairs(
-            SessionContext session, String resourceKey, FilePasswordProvider passwordProvider, String data)
+            SessionContext session, NamedResource resourceKey, FilePasswordProvider passwordProvider, String data)
                 throws IOException, GeneralSecurityException {
         try (Reader reader = new StringReader((data == null) ? "" : data)) {
             return loadKeyPairs(session, resourceKey, passwordProvider, reader);
@@ -114,13 +127,13 @@ public interface KeyPairResourceLoader {
     }
 
     default Collection<KeyPair> loadKeyPairs(
-            SessionContext session, String resourceKey, FilePasswordProvider passwordProvider, InputStream stream)
+            SessionContext session, NamedResource resourceKey, FilePasswordProvider passwordProvider, InputStream stream)
                 throws IOException, GeneralSecurityException {
         return loadKeyPairs(session, resourceKey, passwordProvider, stream, StandardCharsets.UTF_8);
     }
 
     default Collection<KeyPair> loadKeyPairs(
-            SessionContext session, String resourceKey, FilePasswordProvider passwordProvider, InputStream stream, Charset cs)
+            SessionContext session, NamedResource resourceKey, FilePasswordProvider passwordProvider, InputStream stream, Charset cs)
                 throws IOException, GeneralSecurityException {
         try (Reader reader = new InputStreamReader(
                 Objects.requireNonNull(stream, "No stream instance"), Objects.requireNonNull(cs, "No charset"))) {
@@ -129,7 +142,7 @@ public interface KeyPairResourceLoader {
     }
 
     default Collection<KeyPair> loadKeyPairs(
-            SessionContext session, String resourceKey, FilePasswordProvider passwordProvider, Reader r)
+            SessionContext session, NamedResource resourceKey, FilePasswordProvider passwordProvider, Reader r)
                 throws IOException, GeneralSecurityException {
         try (BufferedReader br = new BufferedReader(Objects.requireNonNull(r, "No reader instance"), IoUtils.DEFAULT_COPY_SIZE)) {
             return loadKeyPairs(session, resourceKey, passwordProvider, br);
@@ -137,7 +150,7 @@ public interface KeyPairResourceLoader {
     }
 
     default Collection<KeyPair> loadKeyPairs(
-            SessionContext session, String resourceKey, FilePasswordProvider passwordProvider, BufferedReader r)
+            SessionContext session, NamedResource resourceKey, FilePasswordProvider passwordProvider, BufferedReader r)
                 throws IOException, GeneralSecurityException {
         return loadKeyPairs(session, resourceKey, passwordProvider, IoUtils.readAllLines(r));
     }
@@ -160,6 +173,6 @@ public interface KeyPairResourceLoader {
      * parsed data
      */
     Collection<KeyPair> loadKeyPairs(
-        SessionContext session, String resourceKey, FilePasswordProvider passwordProvider, List<String> lines)
+        SessionContext session, NamedResource resourceKey, FilePasswordProvider passwordProvider, List<String> lines)
             throws IOException, GeneralSecurityException;
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/KeyPairResourceParser.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/KeyPairResourceParser.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/KeyPairResourceParser.java
index c434f0a..8a725d1 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/KeyPairResourceParser.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/KeyPairResourceParser.java
@@ -30,6 +30,7 @@ import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
 
+import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.config.keys.FilePasswordProvider;
 import org.apache.sshd.common.session.SessionContext;
 import org.apache.sshd.common.util.GenericUtils;
@@ -46,13 +47,13 @@ public interface KeyPairResourceParser extends KeyPairResourceLoader {
     KeyPairResourceParser EMPTY = new KeyPairResourceParser() {
         @Override
         public Collection<KeyPair> loadKeyPairs(
-                SessionContext session, String resourceKey, FilePasswordProvider passwordProvider, List<String> lines)
+                SessionContext session, NamedResource resourceKey, FilePasswordProvider passwordProvider, List<String> lines)
                     throws IOException, GeneralSecurityException {
             return Collections.emptyList();
         }
 
         @Override
-        public boolean canExtractKeyPairs(String resourceKey, List<String> lines)
+        public boolean canExtractKeyPairs(NamedResource resourceKey, List<String> lines)
                 throws IOException, GeneralSecurityException {
             return false;
         }
@@ -71,7 +72,7 @@ public interface KeyPairResourceParser extends KeyPairResourceLoader {
      * @throws GeneralSecurityException If failed to extract information regarding
      * the possibility to extract the key pairs
      */
-    boolean canExtractKeyPairs(String resourceKey, List<String> lines)
+    boolean canExtractKeyPairs(NamedResource resourceKey, List<String> lines)
         throws IOException, GeneralSecurityException;
 
     /**
@@ -154,7 +155,7 @@ public interface KeyPairResourceParser extends KeyPairResourceLoader {
         return new KeyPairResourceParser() {
             @Override
             public Collection<KeyPair> loadKeyPairs(
-                    SessionContext session, String resourceKey, FilePasswordProvider passwordProvider, List<String> lines)
+                    SessionContext session, NamedResource resourceKey, FilePasswordProvider passwordProvider, List<String> lines)
                         throws IOException, GeneralSecurityException {
                 Collection<KeyPair> keyPairs = Collections.emptyList();
                 for (KeyPairResourceParser p : parsers) {
@@ -178,7 +179,7 @@ public interface KeyPairResourceParser extends KeyPairResourceLoader {
             }
 
             @Override
-            public boolean canExtractKeyPairs(String resourceKey, List<String> lines)
+            public boolean canExtractKeyPairs(NamedResource resourceKey, List<String> lines)
                     throws IOException, GeneralSecurityException {
                 for (KeyPairResourceParser p : parsers) {
                     if (p.canExtractKeyPairs(resourceKey, lines)) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParser.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParser.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParser.java
index c82957d..3c7871b 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParser.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParser.java
@@ -42,6 +42,7 @@ import java.util.Map;
 import java.util.Objects;
 import java.util.TreeMap;
 
+import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.config.keys.FilePasswordProvider;
 import org.apache.sshd.common.config.keys.KeyEntryResolver;
 import org.apache.sshd.common.config.keys.KeyUtils;
@@ -99,7 +100,7 @@ public class OpenSSHKeyPairResourceParser extends AbstractKeyPairResourceParser
 
     @Override
     public Collection<KeyPair> extractKeyPairs(
-            SessionContext session, String resourceKey,
+            SessionContext session, NamedResource resourceKey,
             String beginMarker, String endMarker,
             FilePasswordProvider passwordProvider,
             InputStream stream)
@@ -155,7 +156,7 @@ public class OpenSSHKeyPairResourceParser extends AbstractKeyPairResourceParser
     }
 
     protected PublicKey readPublicKey(
-            SessionContext session, String resourceKey, OpenSSHParserContext context, InputStream stream)
+            SessionContext session, NamedResource resourceKey, OpenSSHParserContext context, InputStream stream)
                 throws IOException, GeneralSecurityException {
         byte[] keyData = KeyEntryResolver.readRLEBytes(stream, MAX_PUBLIC_KEY_DATA_SIZE);
         try (InputStream bais = new ByteArrayInputStream(keyData)) {
@@ -170,7 +171,7 @@ public class OpenSSHKeyPairResourceParser extends AbstractKeyPairResourceParser
     }
 
     protected List<KeyPair> readPrivateKeys(
-            SessionContext session, String resourceKey,
+            SessionContext session, NamedResource resourceKey,
             OpenSSHParserContext context, Collection<? extends PublicKey> publicKeys,
             FilePasswordProvider passwordProvider, InputStream stream)
                 throws IOException, GeneralSecurityException {
@@ -216,7 +217,7 @@ public class OpenSSHKeyPairResourceParser extends AbstractKeyPairResourceParser
     }
 
     protected SimpleImmutableEntry<PrivateKey, String> readPrivateKey(
-            SessionContext session, String resourceKey,
+            SessionContext session, NamedResource resourceKey,
             OpenSSHParserContext context, String keyType,
             FilePasswordProvider passwordProvider, InputStream stream)
                 throws IOException, GeneralSecurityException {
@@ -242,7 +243,7 @@ public class OpenSSHKeyPairResourceParser extends AbstractKeyPairResourceParser
     }
 
     protected <S extends InputStream> S validateStreamMagicMarker(
-            SessionContext session, String resourceKey, S stream)
+            SessionContext session, NamedResource resourceKey, S stream)
                 throws IOException {
         byte[] actual = new byte[AUTH_MAGIC_BYTES.length];
         IoUtils.readFully(stream, actual);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/AbstractPEMResourceKeyPairParser.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/AbstractPEMResourceKeyPairParser.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/AbstractPEMResourceKeyPairParser.java
index a324339..39e546b 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/AbstractPEMResourceKeyPairParser.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/AbstractPEMResourceKeyPairParser.java
@@ -33,6 +33,7 @@ import java.util.List;
 import javax.security.auth.login.CredentialException;
 import javax.security.auth.login.FailedLoginException;
 
+import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.config.keys.FilePasswordProvider;
 import org.apache.sshd.common.config.keys.FilePasswordProvider.ResourceDecodeResult;
 import org.apache.sshd.common.config.keys.loader.AbstractKeyPairResourceParser;
@@ -74,7 +75,7 @@ public abstract class AbstractPEMResourceKeyPairParser
 
     @Override
     public Collection<KeyPair> extractKeyPairs(
-            SessionContext session, String resourceKey,
+            SessionContext session, NamedResource resourceKey,
             String beginMarker, String endMarker,
             FilePasswordProvider passwordProvider,
             List<String> lines)

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/DSSPEMResourceKeyPairParser.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/DSSPEMResourceKeyPairParser.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/DSSPEMResourceKeyPairParser.java
index 39c84a6..df645e1 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/DSSPEMResourceKeyPairParser.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/DSSPEMResourceKeyPairParser.java
@@ -34,6 +34,7 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 
+import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.config.keys.FilePasswordProvider;
 import org.apache.sshd.common.config.keys.KeyUtils;
 import org.apache.sshd.common.session.SessionContext;
@@ -69,7 +70,7 @@ public class DSSPEMResourceKeyPairParser extends AbstractPEMResourceKeyPairParse
 
     @Override
     public Collection<KeyPair> extractKeyPairs(
-            SessionContext session, String resourceKey,
+            SessionContext session, NamedResource resourceKey,
             String beginMarker, String endMarker,
             FilePasswordProvider passwordProvider,
             InputStream stream)

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/ECDSAPEMResourceKeyPairParser.java
----------------------------------------------------------------------
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 19c15e2..2669e19 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
@@ -38,6 +38,7 @@ import java.util.Collections;
 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;
@@ -73,7 +74,7 @@ public class ECDSAPEMResourceKeyPairParser extends AbstractPEMResourceKeyPairPar
 
     @Override
     public Collection<KeyPair> extractKeyPairs(
-            SessionContext session, String resourceKey,
+            SessionContext session, NamedResource resourceKey,
             String beginMarker, String endMarker,
             FilePasswordProvider passwordProvider,
             InputStream stream)

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PEMResourceParserUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PEMResourceParserUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PEMResourceParserUtils.java
index d3aba1e..a0f3f99 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PEMResourceParserUtils.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PEMResourceParserUtils.java
@@ -31,6 +31,7 @@ import java.util.Objects;
 import java.util.TreeMap;
 import java.util.concurrent.atomic.AtomicReference;
 
+import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.config.keys.FilePasswordProvider;
 import org.apache.sshd.common.config.keys.loader.KeyPairResourceParser;
 import org.apache.sshd.common.session.SessionContext;
@@ -44,7 +45,7 @@ public final class PEMResourceParserUtils {
     public static final KeyPairResourceParser PROXY = new KeyPairResourceParser() {
         @Override
         public Collection<KeyPair> loadKeyPairs(
-                SessionContext session, String resourceKey, FilePasswordProvider passwordProvider, List<String> lines)
+                SessionContext session, NamedResource resourceKey, FilePasswordProvider passwordProvider, List<String> lines)
                     throws IOException, GeneralSecurityException {
             @SuppressWarnings("synthetic-access")
             KeyPairResourceParser proxy = PROXY_HOLDER.get();
@@ -52,7 +53,7 @@ public final class PEMResourceParserUtils {
         }
 
         @Override
-        public boolean canExtractKeyPairs(String resourceKey, List<String> lines)
+        public boolean canExtractKeyPairs(NamedResource resourceKey, List<String> lines)
                 throws IOException, GeneralSecurityException {
             @SuppressWarnings("synthetic-access")
             KeyPairResourceParser proxy = PROXY_HOLDER.get();

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParser.java
----------------------------------------------------------------------
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 90f033b..c755ea8 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
@@ -33,6 +33,7 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 
+import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.config.keys.FilePasswordProvider;
 import org.apache.sshd.common.config.keys.KeyUtils;
 import org.apache.sshd.common.session.SessionContext;
@@ -66,7 +67,7 @@ public class PKCS8PEMResourceKeyPairParser extends AbstractPEMResourceKeyPairPar
 
     @Override
     public Collection<KeyPair> extractKeyPairs(
-            SessionContext session, String resourceKey,
+            SessionContext session, NamedResource resourceKey,
             String beginMarker, String endMarker,
             FilePasswordProvider passwordProvider,
             InputStream stream)

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/RSAPEMResourceKeyPairParser.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/RSAPEMResourceKeyPairParser.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/RSAPEMResourceKeyPairParser.java
index 8badd63..b830063 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/RSAPEMResourceKeyPairParser.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/RSAPEMResourceKeyPairParser.java
@@ -35,6 +35,7 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 
+import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.config.keys.FilePasswordProvider;
 import org.apache.sshd.common.config.keys.KeyUtils;
 import org.apache.sshd.common.session.SessionContext;
@@ -70,7 +71,7 @@ public class RSAPEMResourceKeyPairParser extends AbstractPEMResourceKeyPairParse
 
     @Override
     public Collection<KeyPair> extractKeyPairs(
-            SessionContext session, String resourceKey,
+            SessionContext session, NamedResource resourceKey,
             String beginMarker, String endMarker,
             FilePasswordProvider passwordProvider,
             InputStream stream)

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/AbstractResourceKeyPairProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/AbstractResourceKeyPairProvider.java b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/AbstractResourceKeyPairProvider.java
index 7fbee1b..fd33ec2 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/AbstractResourceKeyPairProvider.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/AbstractResourceKeyPairProvider.java
@@ -21,6 +21,7 @@ package org.apache.sshd.common.keyprovider;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.StreamCorruptedException;
 import java.security.GeneralSecurityException;
 import java.security.KeyPair;
 import java.security.PublicKey;
@@ -33,11 +34,14 @@ import java.util.Objects;
 import java.util.TreeMap;
 import java.util.TreeSet;
 
+import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.config.keys.FilePasswordProvider;
 import org.apache.sshd.common.config.keys.KeyUtils;
 import org.apache.sshd.common.session.SessionContext;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.io.resource.IoResource;
+import org.apache.sshd.common.util.io.resource.ResourceStreamProvider;
 import org.apache.sshd.common.util.security.SecurityUtils;
 
 /**
@@ -118,9 +122,16 @@ public abstract class AbstractResourceKeyPairProvider<R> extends AbstractKeyPair
         }
     }
 
+    protected IoResource<?> getIoResource(SessionContext session, R resource) {
+        return IoResource.forResource(resource);
+    }
+
     protected KeyPair doLoadKey(SessionContext session, R resource)
             throws IOException, GeneralSecurityException {
-        String resourceKey = ValidateUtils.checkNotNullAndNotEmpty(Objects.toString(resource, null), "No resource string value");
+        IoResource<?> ioResource =
+            ValidateUtils.checkNotNull(getIoResource(session, resource), "No I/O resource available for %s", resource);
+        String resourceKey =
+            ValidateUtils.checkNotNullAndNotEmpty(ioResource.getName(), "No resource string value for %s", resource);
         KeyPair kp;
         synchronized (cacheMap) {
             // check if lucky enough to have already loaded this file
@@ -136,7 +147,7 @@ public abstract class AbstractResourceKeyPairProvider<R> extends AbstractKeyPair
             return kp;
         }
 
-        kp = doLoadKey(session, resourceKey, resource, getPasswordFinder());
+        kp = doLoadKey(session, ioResource, resource, getPasswordFinder());
         if (kp != null) {
             boolean reusedKey;
             synchronized (cacheMap) {
@@ -164,17 +175,26 @@ public abstract class AbstractResourceKeyPairProvider<R> extends AbstractKeyPair
         return kp;
     }
 
-    protected KeyPair doLoadKey(SessionContext session, String resourceKey, R resource, FilePasswordProvider provider)
-            throws IOException, GeneralSecurityException {
+    protected KeyPair doLoadKey(
+            SessionContext session, NamedResource resourceKey, R resource, FilePasswordProvider provider)
+                throws IOException, GeneralSecurityException {
         try (InputStream inputStream = openKeyPairResource(session, resourceKey, resource)) {
             return doLoadKey(session, resourceKey, inputStream, provider);
         }
     }
 
-    protected abstract InputStream openKeyPairResource(SessionContext session, String resourceKey, R resource) throws IOException;
+    protected InputStream openKeyPairResource(
+            SessionContext session, NamedResource resourceKey, R resource)
+                throws IOException {
+        if (resourceKey instanceof ResourceStreamProvider) {
+            return ((ResourceStreamProvider) resourceKey).openInputStream();
+        }
+
+        throw new StreamCorruptedException("Cannot open resource data for " + resource);
+    }
 
     protected KeyPair doLoadKey(
-            SessionContext session, String resourceKey, InputStream inputStream, FilePasswordProvider provider)
+            SessionContext session, NamedResource resourceKey, InputStream inputStream, FilePasswordProvider provider)
                 throws IOException, GeneralSecurityException {
         return SecurityUtils.loadKeyPairIdentity(session, resourceKey, inputStream, provider);
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/ClassLoadableResourceKeyPairProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/ClassLoadableResourceKeyPairProvider.java b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/ClassLoadableResourceKeyPairProvider.java
index 54b4e30..0f8a70b 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/ClassLoadableResourceKeyPairProvider.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/ClassLoadableResourceKeyPairProvider.java
@@ -18,16 +18,14 @@
  */
 package org.apache.sshd.common.keyprovider;
 
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.StreamCorruptedException;
 import java.security.KeyPair;
 import java.util.Collection;
 import java.util.Collections;
 
 import org.apache.sshd.common.session.SessionContext;
 import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.io.resource.ClassLoaderResource;
+import org.apache.sshd.common.util.io.resource.IoResource;
 import org.apache.sshd.common.util.threads.ThreadUtils;
 
 /**
@@ -90,20 +88,8 @@ public class ClassLoadableResourceKeyPairProvider extends AbstractResourceKeyPai
     }
 
     @Override
-    protected InputStream openKeyPairResource(
-            SessionContext session, String resourceKey, String resource)
-                throws IOException {
-        ClassLoader cl = resolveClassLoader();
-        if (cl == null) {
-            throw new StreamCorruptedException("No resource loader for " + resource);
-        }
-
-        InputStream input = cl.getResourceAsStream(resource);
-        if (input == null) {
-            throw new FileNotFoundException("Cannot find resource " + resource);
-        }
-
-        return input;
+    protected IoResource<?> getIoResource(SessionContext session, String resource) {
+        return new ClassLoaderResource(resolveClassLoader(), resource);
     }
 
     protected ClassLoader resolveClassLoader() {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/FileKeyPairProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/FileKeyPairProvider.java b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/FileKeyPairProvider.java
index f7de669..613e0cf 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/FileKeyPairProvider.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/FileKeyPairProvider.java
@@ -19,8 +19,6 @@
 package org.apache.sshd.common.keyprovider;
 
 import java.io.IOException;
-import java.io.InputStream;
-import java.nio.file.Files;
 import java.nio.file.Path;
 import java.security.GeneralSecurityException;
 import java.security.KeyPair;
@@ -31,7 +29,8 @@ import java.util.Objects;
 
 import org.apache.sshd.common.session.SessionContext;
 import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.common.util.io.resource.IoResource;
+import org.apache.sshd.common.util.io.resource.PathResource;
 
 /**
  * This host key provider loads private keys from the specified files. The
@@ -77,15 +76,13 @@ public class FileKeyPairProvider extends AbstractResourceKeyPairProvider<Path> {
     }
 
     @Override
-    protected KeyPair doLoadKey(SessionContext session, Path resource)
-            throws IOException, GeneralSecurityException {
-        return super.doLoadKey(session, (resource == null) ? null : resource.toAbsolutePath());
+    protected IoResource<Path> getIoResource(SessionContext session, Path resource) {
+        return (resource == null) ? null : new PathResource(resource);
     }
 
     @Override
-    protected InputStream openKeyPairResource(
-            SessionContext session, String resourceKey, Path resource)
-                throws IOException {
-        return Files.newInputStream(resource, IoUtils.EMPTY_OPEN_OPTIONS);
+    protected KeyPair doLoadKey(SessionContext session, Path resource)
+            throws IOException, GeneralSecurityException {
+        return super.doLoadKey(session, (resource == null) ? null : resource.toAbsolutePath());
     }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/sshd-common/src/main/java/org/apache/sshd/common/util/io/resource/AbstractIoResource.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/resource/AbstractIoResource.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/resource/AbstractIoResource.java
new file mode 100644
index 0000000..50e2ccd
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/resource/AbstractIoResource.java
@@ -0,0 +1,57 @@
+/*
+ * 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.util.io.resource;
+
+import java.util.Objects;
+
+/**
+ * TODO Add javadoc
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractIoResource<T> implements IoResource<T> {
+    private final Class<T> resourceType;
+    private final T resourceValue;
+
+    protected AbstractIoResource(Class<T> resourceType, T resourceValue) {
+        this.resourceType = Objects.requireNonNull(resourceType, "No resource type specified");
+        this.resourceValue = Objects.requireNonNull(resourceValue, "No resource value provided");
+    }
+
+    @Override
+    public Class<T> getResourceType() {
+        return resourceType;
+    }
+
+    @Override
+    public T getResourceValue() {
+        return resourceValue;
+    }
+
+    @Override
+    public String getName() {
+        return Objects.toString(getResourceValue(), null);
+    }
+
+    @Override
+    public String toString() {
+        return getName();
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/sshd-common/src/main/java/org/apache/sshd/common/util/io/resource/ClassLoaderResource.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/resource/ClassLoaderResource.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/resource/ClassLoaderResource.java
new file mode 100644
index 0000000..7dec40a
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/resource/ClassLoaderResource.java
@@ -0,0 +1,67 @@
+/*
+ * 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.util.io.resource;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StreamCorruptedException;
+
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.threads.ThreadUtils;
+
+/**
+ * TODO Add javadoc
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class ClassLoaderResource extends AbstractIoResource<ClassLoader> {
+    private final String resourceName;
+
+    public ClassLoaderResource(ClassLoader loader, String resourceName) {
+        super(ClassLoader.class, (loader == null) ? ThreadUtils.resolveDefaultClassLoader(ClassLoaderResource.class) : loader);
+        this.resourceName = ValidateUtils.checkNotNullAndNotEmpty(resourceName, "No resource name provided");
+    }
+
+    public ClassLoader getResourceLoader() {
+        return getResourceValue();
+    }
+
+    @Override
+    public String getName() {
+        return resourceName;
+    }
+
+    @Override
+    public InputStream openInputStream() throws IOException {
+        String name = getName();
+        ClassLoader cl = getResourceLoader();
+        if (cl == null) {
+            throw new StreamCorruptedException("No resource loader for " + name);
+        }
+
+        InputStream input = cl.getResourceAsStream(name);
+        if (input == null) {
+            throw new FileNotFoundException("Cannot find resource " + name);
+        }
+
+        return input;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/sshd-common/src/main/java/org/apache/sshd/common/util/io/resource/IoResource.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/resource/IoResource.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/resource/IoResource.java
new file mode 100644
index 0000000..0031544
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/resource/IoResource.java
@@ -0,0 +1,63 @@
+/*
+ * 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.util.io.resource;
+
+import java.net.URI;
+import java.net.URL;
+import java.nio.file.Path;
+
+import org.apache.sshd.common.NamedResource;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface IoResource<T> extends NamedResource, ResourceStreamProvider {
+    /**
+     * @return The type of resource being represented
+     */
+    Class<T> getResourceType();
+
+    /**
+     * @return The resource value serving as basis for the provided data stream
+     */
+    T getResourceValue();
+
+    /**
+     * Attempts to find the best wrapper for the resource
+     *
+     * @param resource The resource object - ignored if {@code null}
+     * @return The best wrapper out of the supported ones ({@code null} if no initial resource)
+     * @throws UnsupportedOperationException if no match found
+     */
+    static IoResource<?> forResource(Object resource) {
+        if (resource == null) {
+            return null;
+        } else if (resource instanceof Path) {
+            return new PathResource((Path) resource);
+        } else if (resource instanceof URL) {
+            return new URLResource((URL) resource);
+        } else if (resource instanceof URI) {
+            return new URIResource((URI) resource);
+        } else {
+            throw new UnsupportedOperationException(
+                "Unsupported resource type " + resource.getClass().getSimpleName() + ": " + resource);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/sshd-common/src/main/java/org/apache/sshd/common/util/io/resource/PathResource.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/resource/PathResource.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/resource/PathResource.java
new file mode 100644
index 0000000..a11380b
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/resource/PathResource.java
@@ -0,0 +1,60 @@
+/*
+ * 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.util.io.resource;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.OpenOption;
+import java.nio.file.Path;
+
+import org.apache.sshd.common.util.io.IoUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class PathResource extends AbstractIoResource<Path> {
+    private final OpenOption[] openOptions;
+
+    public PathResource(Path path) {
+        super(Path.class, path);
+        this.openOptions = IoUtils.EMPTY_OPEN_OPTIONS;
+    }
+
+    public PathResource(Path path, OpenOption... openOptions) {
+        super(Path.class, path);
+        // Use a clone to avoid shared instance modification
+        this.openOptions = (openOptions == null) ? IoUtils.EMPTY_OPEN_OPTIONS : openOptions.clone();
+    }
+
+    public Path getPath() {
+        return getResourceValue();
+    }
+
+    public OpenOption[] getOpenOptions() {
+        // Use a clone to avoid shared instance modification
+        return (openOptions.length <= 0) ? openOptions : openOptions.clone();
+    }
+
+    @Override
+    public InputStream openInputStream() throws IOException {
+        return Files.newInputStream(getPath(), getOpenOptions());
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/sshd-common/src/main/java/org/apache/sshd/common/util/io/resource/ResourceStreamProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/resource/ResourceStreamProvider.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/resource/ResourceStreamProvider.java
new file mode 100644
index 0000000..6e17c5c
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/resource/ResourceStreamProvider.java
@@ -0,0 +1,35 @@
+/*
+ * 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.util.io.resource;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FunctionalInterface
+public interface ResourceStreamProvider {
+    /**
+     * Return an {@link InputStream} for the resource's data
+     */
+    InputStream openInputStream() throws IOException;
+
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/sshd-common/src/main/java/org/apache/sshd/common/util/io/resource/URIResource.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/resource/URIResource.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/resource/URIResource.java
new file mode 100644
index 0000000..a43f9e5
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/resource/URIResource.java
@@ -0,0 +1,45 @@
+/*
+ * 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.util.io.resource;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URL;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class URIResource extends AbstractIoResource<URI> {
+    public URIResource(URI uri) {
+        super(URI.class, uri);
+    }
+
+    public URI getURI() {
+        return getResourceValue();
+    }
+
+    @Override
+    public InputStream openInputStream() throws IOException {
+        URI uri = getURI();
+        URL url = uri.toURL();
+        return url.openStream();
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/sshd-common/src/main/java/org/apache/sshd/common/util/io/resource/URLResource.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/resource/URLResource.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/resource/URLResource.java
new file mode 100644
index 0000000..761012b
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/resource/URLResource.java
@@ -0,0 +1,50 @@
+/*
+ * 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.util.io.resource;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class URLResource extends AbstractIoResource<URL> {
+    public URLResource(URL url) {
+        super(URL.class, url);
+    }
+
+    public URL getURL() {
+        return getResourceValue();
+    }
+
+    @Override
+    public String getName() {
+        URL url = getURL();
+        // URL#toString() may involve a DNS lookup
+        return url.toExternalForm();
+    }
+
+    @Override
+    public InputStream openInputStream() throws IOException {
+        URL url = getURL();
+        return url.openStream();
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java
index 2949875..cac845e 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java
@@ -57,6 +57,7 @@ import javax.crypto.KeyAgreement;
 import javax.crypto.Mac;
 import javax.crypto.spec.DHParameterSpec;
 
+import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.config.keys.FilePasswordProvider;
 import org.apache.sshd.common.config.keys.KeyUtils;
 import org.apache.sshd.common.config.keys.PrivateKeyEntryDecoder;
@@ -481,7 +482,7 @@ public final class SecurityUtils {
      * @throws GeneralSecurityException If failed to generate the keys
      */
     public static KeyPair loadKeyPairIdentity(
-            SessionContext session, String resourceKey, InputStream inputStream, FilePasswordProvider provider)
+            SessionContext session, NamedResource resourceKey, InputStream inputStream, FilePasswordProvider provider)
                 throws IOException, GeneralSecurityException {
         KeyPairResourceParser parser = getKeyPairResourceParser();
         if (parser == null) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleGeneratorHostKeyProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleGeneratorHostKeyProvider.java b/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleGeneratorHostKeyProvider.java
index 3716719..534b033 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleGeneratorHostKeyProvider.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleGeneratorHostKeyProvider.java
@@ -26,6 +26,7 @@ import java.nio.file.Path;
 import java.security.GeneralSecurityException;
 import java.security.KeyPair;
 
+import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.server.keyprovider.AbstractGeneratorHostKeyProvider;
 
 /**
@@ -38,7 +39,7 @@ public class BouncyCastleGeneratorHostKeyProvider extends AbstractGeneratorHostK
 
     @SuppressWarnings("deprecation")
     @Override
-    protected void doWriteKeyPair(String resourceKey, KeyPair kp, OutputStream outputStream) throws IOException, GeneralSecurityException {
+    protected void doWriteKeyPair(NamedResource resourceKey, KeyPair kp, OutputStream outputStream) throws IOException, GeneralSecurityException {
         try (org.bouncycastle.openssl.PEMWriter w =
                      new org.bouncycastle.openssl.PEMWriter(new OutputStreamWriter(outputStream, StandardCharsets.UTF_8))) {
             w.writeObject(kp);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleKeyPairResourceParser.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleKeyPairResourceParser.java b/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleKeyPairResourceParser.java
index 881adff..7709606 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleKeyPairResourceParser.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleKeyPairResourceParser.java
@@ -36,6 +36,7 @@ import java.util.List;
 import javax.security.auth.login.CredentialException;
 import javax.security.auth.login.FailedLoginException;
 
+import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.config.keys.FilePasswordProvider;
 import org.apache.sshd.common.config.keys.FilePasswordProvider.ResourceDecodeResult;
 import org.apache.sshd.common.config.keys.loader.AbstractKeyPairResourceParser;
@@ -77,7 +78,10 @@ public class BouncyCastleKeyPairResourceParser extends AbstractKeyPairResourcePa
 
     @Override
     public Collection<KeyPair> extractKeyPairs(
-            SessionContext session, String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, List<String> lines)
+            SessionContext session, NamedResource resourceKey,
+            String beginMarker, String endMarker,
+            FilePasswordProvider passwordProvider,
+            List<String> lines)
                 throws IOException, GeneralSecurityException {
         StringBuilder writer = new StringBuilder(beginMarker.length() + endMarker.length() + lines.size() * 80);
         writer.append(beginMarker).append(IoUtils.EOL);
@@ -93,7 +97,7 @@ public class BouncyCastleKeyPairResourceParser extends AbstractKeyPairResourcePa
 
     @Override
     public Collection<KeyPair> extractKeyPairs(
-            SessionContext session, String resourceKey,
+            SessionContext session, NamedResource resourceKey,
             String beginMarker, String endMarker,
             FilePasswordProvider passwordProvider,
             InputStream stream)
@@ -103,7 +107,7 @@ public class BouncyCastleKeyPairResourceParser extends AbstractKeyPairResourcePa
     }
 
     public static KeyPair loadKeyPair(
-            SessionContext session, String resourceKey, InputStream inputStream, FilePasswordProvider provider)
+            SessionContext session, NamedResource resourceKey, InputStream inputStream, FilePasswordProvider provider)
                 throws IOException, GeneralSecurityException {
         try (PEMParser r = new PEMParser(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
             Object o = r.readObject();

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/sshd-common/src/main/java/org/apache/sshd/server/keyprovider/AbstractGeneratorHostKeyProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/server/keyprovider/AbstractGeneratorHostKeyProvider.java b/sshd-common/src/main/java/org/apache/sshd/server/keyprovider/AbstractGeneratorHostKeyProvider.java
index 71e3a4f..be4ceb5 100644
--- a/sshd-common/src/main/java/org/apache/sshd/server/keyprovider/AbstractGeneratorHostKeyProvider.java
+++ b/sshd-common/src/main/java/org/apache/sshd/server/keyprovider/AbstractGeneratorHostKeyProvider.java
@@ -35,12 +35,14 @@ import java.util.List;
 import java.util.Objects;
 import java.util.concurrent.atomic.AtomicReference;
 
+import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.cipher.ECCurves;
 import org.apache.sshd.common.config.keys.BuiltinIdentities;
 import org.apache.sshd.common.config.keys.KeyUtils;
 import org.apache.sshd.common.keyprovider.AbstractKeyPairProvider;
 import org.apache.sshd.common.session.SessionContext;
 import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.common.util.io.resource.PathResource;
 import org.apache.sshd.common.util.security.SecurityUtils;
 
 /**
@@ -242,12 +244,13 @@ public abstract class AbstractGeneratorHostKeyProvider extends AbstractKeyPairPr
 
     protected KeyPair readKeyPair(SessionContext session, Path keyPath, OpenOption... options)
             throws IOException, GeneralSecurityException {
-        try (InputStream inputStream = Files.newInputStream(keyPath, options)) {
-            return doReadKeyPair(session, keyPath.toString(), inputStream);
+        PathResource location = new PathResource(keyPath, options);
+        try (InputStream inputStream = location.openInputStream()) {
+            return doReadKeyPair(session, location, inputStream);
         }
     }
 
-    protected KeyPair doReadKeyPair(SessionContext session, String resourceKey, InputStream inputStream)
+    protected KeyPair doReadKeyPair(SessionContext session, NamedResource resourceKey, InputStream inputStream)
             throws IOException, GeneralSecurityException {
         return SecurityUtils.loadKeyPairIdentity(session, resourceKey, inputStream, null);
     }
@@ -255,8 +258,9 @@ public abstract class AbstractGeneratorHostKeyProvider extends AbstractKeyPairPr
     protected void writeKeyPair(KeyPair kp, Path keyPath, OpenOption... options)
             throws IOException, GeneralSecurityException {
         if ((!Files.exists(keyPath)) || isOverwriteAllowed()) {
+            PathResource location = new PathResource(keyPath); // The options are for write (!!)
             try (OutputStream os = Files.newOutputStream(keyPath, options)) {
-                doWriteKeyPair(keyPath.toString(), kp, os);
+                doWriteKeyPair(location, kp, os);
             } catch (Throwable e) {
                 log.warn("writeKeyPair({}) failed ({}) to write key {}: {}",
                          keyPath, e.getClass().getSimpleName(), e.getMessage());
@@ -266,11 +270,13 @@ public abstract class AbstractGeneratorHostKeyProvider extends AbstractKeyPairPr
             }
         } else {
             log.error("Overwriting key ({}) is disabled: using throwaway {}: {}",
-                      keyPath, KeyUtils.getKeyType(kp), KeyUtils.getFingerPrint((kp == null) ? null : kp.getPublic()));
+                  keyPath, KeyUtils.getKeyType(kp), KeyUtils.getFingerPrint((kp == null) ? null : kp.getPublic()));
         }
     }
 
-    protected abstract void doWriteKeyPair(String resourceKey, KeyPair kp, OutputStream outputStream) throws IOException, GeneralSecurityException;
+    protected abstract void doWriteKeyPair(
+            NamedResource resourceKey, KeyPair kp, OutputStream outputStream)
+                throws IOException, GeneralSecurityException;
 
     protected KeyPair generateKeyPair(String algorithm) throws GeneralSecurityException {
         KeyPairGenerator generator = SecurityUtils.getKeyPairGenerator(algorithm);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/sshd-common/src/main/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProvider.java b/sshd-common/src/main/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProvider.java
index b40edcb..c0ccd16 100644
--- a/sshd-common/src/main/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProvider.java
+++ b/sshd-common/src/main/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProvider.java
@@ -28,6 +28,7 @@ import java.security.GeneralSecurityException;
 import java.security.KeyPair;
 import java.security.spec.InvalidKeySpecException;
 
+import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.session.SessionContext;
 
 /**
@@ -45,7 +46,7 @@ public class SimpleGeneratorHostKeyProvider extends AbstractGeneratorHostKeyProv
     }
 
     @Override
-    protected KeyPair doReadKeyPair(SessionContext session, String resourceKey, InputStream inputStream)
+    protected KeyPair doReadKeyPair(SessionContext session, NamedResource resourceKey, InputStream inputStream)
             throws IOException, GeneralSecurityException {
         try (ObjectInputStream r = new ObjectInputStream(inputStream)) {
             try {
@@ -57,7 +58,7 @@ public class SimpleGeneratorHostKeyProvider extends AbstractGeneratorHostKeyProv
     }
 
     @Override
-    protected void doWriteKeyPair(String resourceKey, KeyPair kp, OutputStream outputStream)
+    protected void doWriteKeyPair(NamedResource resourceKey, KeyPair kp, OutputStream outputStream)
             throws IOException, GeneralSecurityException {
         try (ObjectOutputStream w = new ObjectOutputStream(outputStream)) {
             w.writeObject(kp);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/sshd-common/src/test/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcherTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcherTest.java b/sshd-common/src/test/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcherTest.java
index 81c458f..64573c4 100644
--- a/sshd-common/src/test/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcherTest.java
+++ b/sshd-common/src/test/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcherTest.java
@@ -35,6 +35,7 @@ import java.util.EnumMap;
 import java.util.Map;
 import java.util.Objects;
 
+import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.config.keys.BuiltinIdentities;
 import org.apache.sshd.common.config.keys.FilePasswordProvider;
 import org.apache.sshd.common.config.keys.KeyUtils;
@@ -80,7 +81,7 @@ public class BuiltinClientIdentitiesWatcherTest extends JUnitTestSupport {
         ClientIdentityLoader loader = new ClientIdentityLoader() {
             @Override
             public KeyPair loadClientIdentity(
-                    SessionContext session, String location, FilePasswordProvider provider)
+                    SessionContext session, NamedResource location, FilePasswordProvider provider)
                         throws IOException, GeneralSecurityException {
                 BuiltinIdentities id = findIdentity(location);
                 assertNotNull("Invalid location: " + location, id);
@@ -88,18 +89,19 @@ public class BuiltinClientIdentitiesWatcherTest extends JUnitTestSupport {
             }
 
             @Override
-            public boolean isValidLocation(String location) throws IOException {
+            public boolean isValidLocation(NamedResource location) throws IOException {
                 return findIdentity(location) != null;
             }
 
-            private BuiltinIdentities findIdentity(String location) {
-                if (GenericUtils.isEmpty(location)) {
+            private BuiltinIdentities findIdentity(NamedResource location) {
+                String idPath = (location == null) ? null : location.getName();
+                if (GenericUtils.isEmpty(idPath)) {
                     return null;
                 }
 
                 for (Map.Entry<BuiltinIdentities, Path> le : locationsMap.entrySet()) {
                     Path path = le.getValue();
-                    if (String.CASE_INSENSITIVE_ORDER.compare(location, path.toString()) == 0) {
+                    if (String.CASE_INSENSITIVE_ORDER.compare(idPath, path.toString()) == 0) {
                         return le.getKey();
                     }
                 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/895f30f1/sshd-common/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcherTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcherTest.java b/sshd-common/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcherTest.java
index bd43f44..2cf3828 100644
--- a/sshd-common/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcherTest.java
+++ b/sshd-common/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcherTest.java
@@ -32,6 +32,7 @@ import java.util.Date;
 import java.util.Objects;
 import java.util.concurrent.atomic.AtomicInteger;
 
+import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.config.keys.FilePasswordProvider;
 import org.apache.sshd.common.session.SessionContext;
 import org.apache.sshd.common.util.io.IoUtils;
@@ -61,15 +62,15 @@ public class ClientIdentityFileWatcherTest extends JUnitTestSupport {
         ClientIdentityLoader loader = new ClientIdentityLoader() {
             @Override
             public KeyPair loadClientIdentity(
-                    SessionContext session, String location, FilePasswordProvider provider)
+                    SessionContext session, NamedResource location, FilePasswordProvider provider)
                         throws IOException, GeneralSecurityException {
                 assertTrue("Invalid location: " + location, isValidLocation(location));
                 return identity;
             }
 
             @Override
-            public boolean isValidLocation(String location) throws IOException {
-                return Objects.equals(location, toString());
+            public boolean isValidLocation(NamedResource location) throws IOException {
+                return Objects.equals(location.getName(), toString());
             }
 
             @Override